Skip to content

Develop - #68

Merged
matteoettam09 merged 7 commits into
mainfrom
develop
Sep 11, 2025
Merged

Develop#68
matteoettam09 merged 7 commits into
mainfrom
develop

Conversation

@matteoettam09

@matteoettam09 matteoettam09 commented Sep 11, 2025

Copy link
Copy Markdown
Member

Summary by Sourcery

Integrate modular Chainlink Automation flows and reentrancy protection in LiquidityOrchestrator, streamline InternalStatesOrchestrator complexity, and simplify vault intent validation.

New Features:

  • Add ReentrancyGuard to LiquidityOrchestrator and protect performUpkeep
  • Introduce encoded action constants and modular start/processUpkeep handlers in LiquidityOrchestrator
  • Store epoch-specific sell/buy token and amount arrays with minibatch processing indices

Enhancements:

  • Refactor LiquidityOrchestrator checkUpkeep/performUpkeep to use bytes4-encoded actions
  • Cache array lengths and enable solhint code-complexity regions in InternalStatesOrchestrator
  • Simplify FHE intent validation in OrionEncryptedVault and remove redundant checks in OrionTransparentVault

Chores:

  • Update eslint config to ignore protocol-costs directory
  • Remove outdated gas-report.txt from the repository

Summary by CodeRabbit

  • New Features

    • Batched trade processing with minibatch controls and new public visibility into current minibatch and per-epoch buy/sell orders.
    • Added reentrancy protection to upkeep execution.
  • Refactor

    • Reworked upkeep into discrete, action-driven steps.
    • Simplified encrypted intent validation and allowed zero-weight entries in transparent intents.
  • Chores

    • Ignored protocol-costs and reports in tooling/config; removed static gas report and bumped package/dev tooling versions.
  • Tests

    • Added an end-to-end upkeep cycle test exercising decryption and full epoch progression.

@coderabbitai

coderabbitai Bot commented Sep 11, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Caution

Review failed

The pull request is closed.

Walkthrough

Adds ignore entries; updates LiquidityOrchestrator to use ReentrancyGuard, minibatch-driven upkeep via encoded performData, and expose epoch order arrays; refactors InternalStatesOrchestrator loop handling and callback signature; modifies encrypted/transparent vault intent validation and decryption callback signatures; updates ABIs/artifacts and test to exercise the new upkeep flow; removes gas report.

Changes

Cohort / File(s) Summary
Ignore & lint configs
\.gitignore, \.prettierignore, eslint.config.mjs
Add protocol-costs (and reports/ in .gitignore) to ignore/exclude lists.
Liquidity orchestrator (contract + artifact)
contracts/orchestrators/LiquidityOrchestrator.sol, artifacts/.../LiquidityOrchestrator.sol/LiquidityOrchestrator.json
Add ReentrancyGuard + nonReentrant on performUpkeep; convert upkeep to action-encoded performData (start/processSell/processBuy) with minibatch indexing; add public arrays sellingTokens/Amounts, buyingTokens/Amounts and scalars executionMinibatchSize, currentMinibatchIndex; artifact ABI and bytecode updated (error addition/removal, getter additions, performUpkeep param rename).
Internal states orchestrator
contracts/orchestrators/InternalStatesOrchestrator.sol, artifacts/.../IInternalStateOrchestrator.sol/IInternalStateOrchestrator.json
Add lint/slither pragmas; use local vault-length helpers in loops; rename cipher arrays; change callbackPreProcessDecrypt ABI/types from (uint256[], bytes[]) to (bytes, bytes) and update internal decoding logic.
Vaults: encrypted & transparent
contracts/vaults/OrionEncryptedVault.sol, contracts/vaults/OrionTransparentVault.sol, artifacts/.../IOrionEncryptedVault.sol/IOrionEncryptedVault.json
Encrypted: remove per-weight validity checks, rely on totalWeight equality; rename cipher vars; change _validateIntent and callbackDecryptSingleEbool signatures to accept (bytes cleartexts, bytes decryptionProof) and decode cleartexts; Transparent: allow zero-weight intent entries (removed per-weight >0 revert). ABI artifacts updated accordingly.
Artifacts / bytecode updates
artifacts/contracts/OrionConfig.sol/OrionConfig.json, artifacts/.../UtilitiesLib.sol/UtilitiesLib.json, artifacts/.../PriceAdapterRegistry.sol/PriceAdapterRegistry.json
Bytecode / deployedBytecode strings updated; ABIs otherwise unchanged.
Package & tooling
package.json
Bump package version to 0.4.3; upgrade/add FHE-related tooling (@fhevm/solidity ^0.8.0, @fhevm/hardhat-plugin ^0.1.0, add @fhevm/mock-utils, @zama-fhe/relayer-sdk).
Tests
test/Orchestrators.test.ts
Replace skeleton with end-to-end upkeep cycle test exercising decryption/oracle and multiple upkeep/phase transitions; expected phase flow updated (Buffering step).
Reports removal
reports/gas-report.txt
Remove generated static gas report file.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Keeper as Chainlink Automation
  participant LO as LiquidityOrchestrator
  participant ISO as InternalStatesOrchestrator
  participant Adapter as DEX Adapter(s)

  Note over LO: New public state: selling/buying tokens & amounts,\nexecutionMinibatchSize, currentMinibatchIndex

  Keeper->>LO: checkUpkeep(checkData)
  activate LO
  LO-->>Keeper: (upkeepNeeded, performData: action + minibatchIndex)
  deactivate LO

  alt upkeepNeeded
    Keeper->>+LO: performUpkeep(performData) [nonReentrant]
    alt action == start
      LO->>ISO: getSellingOrders() / getBuyingOrders()
      ISO-->>LO: orders
      LO->>LO: _handleStart() -> load epoch orders, advance phase
    else action == processSell
      LO->>LO: _processMinibatchSell(minibatchIndex)
      LO->>Adapter: _executeSell(...)
      Adapter-->>LO: result
    else action == processBuy
      LO->>LO: _processMinibatchBuy(minibatchIndex)
      LO->>Adapter: _executeBuy(...)
      Adapter-->>LO: result
    end
    LO-->>-Keeper: done
  else no upkeep
    Note over Keeper,LO: no operation
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Develop #68 — Appears to contain the same set of interface/signature updates, LiquidityOrchestrator minibatch/reentrancy changes, vault callback/type changes, and ignore updates.

Poem

I thump my paws in careful beats,
Minibatches march in tidy fleets.
Start, sell, buy — encoded cue,
NonReentrant keeps the chaos through.
Zero weights slip soft and light,
Epochs settle, carrots bright. 🥕

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e96f6 and 181a57a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • artifacts/contracts/OrionConfig.sol/OrionConfig.json (1 hunks)
  • artifacts/contracts/interfaces/IInternalStateOrchestrator.sol/IInternalStateOrchestrator.json (1 hunks)
  • artifacts/contracts/interfaces/IOrionEncryptedVault.sol/IOrionEncryptedVault.json (1 hunks)
  • artifacts/contracts/libraries/UtilitiesLib.sol/UtilitiesLib.json (1 hunks)
  • artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (6 hunks)
  • artifacts/contracts/price/PriceAdapterRegistry.sol/PriceAdapterRegistry.json (1 hunks)
  • contracts/interfaces/IInternalStateOrchestrator.sol (1 hunks)
  • contracts/interfaces/IOrionEncryptedVault.sol (1 hunks)
  • contracts/orchestrators/InternalStatesOrchestrator.sol (9 hunks)
  • contracts/orchestrators/LiquidityOrchestrator.sol (8 hunks)
  • contracts/vaults/OrionEncryptedVault.sol (4 hunks)
  • package.json (4 hunks)
  • test/Orchestrators.test.ts (1 hunks)
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

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

@sourcery-ai

sourcery-ai Bot commented Sep 11, 2025

Copy link
Copy Markdown

Reviewer's Guide

This PR refactors LiquidityOrchestrator into an action-driven, reentrancy-protected Chainlink Automation executor with phased minibatch processing and new epoch state tracking; tightens loop performance and lint directives in InternalStatesOrchestrator; simplifies FHE intent validation in vault contracts; and updates ESLint ignore patterns.

Sequence diagram for phased action-driven upkeep in LiquidityOrchestrator

sequenceDiagram
    participant AutomationRegistry
    participant LiquidityOrchestrator
    participant InternalStatesOrchestrator
    AutomationRegistry->>LiquidityOrchestrator: checkUpkeep()
    LiquidityOrchestrator->>InternalStatesOrchestrator: epochCounter()
    InternalStatesOrchestrator-->>LiquidityOrchestrator: epoch value
    AutomationRegistry->>LiquidityOrchestrator: performUpkeep(performData)
    LiquidityOrchestrator->>LiquidityOrchestrator: decode performData
    alt ACTION_START
        LiquidityOrchestrator->>LiquidityOrchestrator: _handleStart()
        LiquidityOrchestrator->>InternalStatesOrchestrator: getSellingOrders()
        LiquidityOrchestrator->>InternalStatesOrchestrator: getBuyingOrders()
    else ACTION_PROCESS_SELL
        LiquidityOrchestrator->>LiquidityOrchestrator: _processMinibatchSell(minibatchIndex)
    else ACTION_PROCESS_BUY
        LiquidityOrchestrator->>LiquidityOrchestrator: _processMinibatchBuy(minibatchIndex)
    end
Loading

ER diagram for new epoch state tracking in LiquidityOrchestrator

erDiagram
    LIQUIDITY_ORCHESTRATOR {
        uint16 lastProcessedEpoch
        uint8 executionMinibatchSize
        uint8 currentMinibatchIndex
        address[] sellingTokens
        uint256[] sellingAmounts
        address[] buyingTokens
        uint256[] buyingAmounts
    }
    INTERNAL_STATES_ORCHESTRATOR {
        getSellingOrders()
        getBuyingOrders()
    }
    LIQUIDITY_ORCHESTRATOR ||--o| INTERNAL_STATES_ORCHESTRATOR : fetches orders
Loading

Class diagram for updated LiquidityOrchestrator structure

classDiagram
    class LiquidityOrchestrator {
        +Ownable
        +ReentrancyGuard
        +ILiquidityOrchestrator
        uint16 lastProcessedEpoch
        uint8 executionMinibatchSize
        uint8 currentMinibatchIndex
        LiquidityUpkeepPhase currentPhase
        uint256 slippageBound
        uint256 targetBufferRatio
        address[] sellingTokens
        uint256[] sellingAmounts
        address[] buyingTokens
        uint256[] buyingAmounts
        transferRedemptionFunds(user, amount)
        checkUpkeep(bytes)
        performUpkeep(bytes)
        _handleStart()
        _processMinibatchSell(minibatchIndex)
        _processMinibatchBuy(minibatchIndex)
    }
    LiquidityOrchestrator --|> Ownable
    LiquidityOrchestrator --|> ReentrancyGuard
    LiquidityOrchestrator --|> ILiquidityOrchestrator
Loading

Class diagram for updated OrionEncryptedVault intent validation

classDiagram
    class OrionEncryptedVault {
        +_validateIntent(assets, totalWeight)
    }
Loading

Class diagram for updated OrionTransparentVault intent validation

classDiagram
    class OrionTransparentVault {
        +set(token, weight)
        // Removed: revert if weight == 0
    }
Loading

File-Level Changes

Change Details Files
Overhaul of LiquidityOrchestrator to action-based automation
  • Imported ReentrancyGuard and applied nonReentrant modifier
  • Defined ACTION_START, ACTION_PROCESS_SELL, ACTION_PROCESS_BUY constants
  • Reworked checkUpkeep/performUpkeep to encode/decode actions and dispatch handlers
  • Added epoch state arrays (sellingTokens, sellingAmounts, buyingTokens, buyingAmounts) and currentMinibatchIndex
  • Implemented internal helpers _handleStart, _processMinibatchSell, _processMinibatchBuy
contracts/orchestrators/LiquidityOrchestrator.sol
Refactor of InternalStatesOrchestrator loops and lint directives
  • Wrapped complex functions with solhint disable/enable pragmas
  • Documented performData param and enforced nonReentrant on performUpkeep
  • Cached vault/token array lengths in local variables for loop efficiency
  • Removed outdated comments and aligned code-complexity directives
contracts/orchestrators/InternalStatesOrchestrator.sol
Simplified intent validation in vault contracts
  • Removed redundant areWeightsValid tracking and related FHE checks in OrionEncryptedVault
  • Updated _validateIntent signature to drop weight validity parameter
  • Eliminated zero-weight revert in OrionTransparentVault intent processing
contracts/vaults/OrionEncryptedVault.sol
contracts/vaults/OrionTransparentVault.sol
ESLint configuration updated to ignore protocol-costs
  • Added "**/protocol-costs" to ignore patterns in eslint.config.mjs
eslint.config.mjs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `contracts/orchestrators/LiquidityOrchestrator.sol:255` </location>
<code_context>
-    // solhint-disable-next-line code-complexity
+    /// @param performData Encoded data containing the action type and minibatch index
     function performUpkeep(bytes calldata performData) external override onlyAutomationRegistry nonReentrant {
         if (performData.length < 4) revert ErrorsLib.InvalidArguments();

</code_context>

<issue_to_address>
Input validation for performData length is added; consider stricter checks.

Since the expected encoding is (bytes4, uint8), update the check to performData.length < 5 to ensure proper validation and prevent decoding errors.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
        if (performData.length < 4) revert ErrorsLib.InvalidArguments();
=======
        if (performData.length < 5) revert ErrorsLib.InvalidArguments();
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread contracts/orchestrators/LiquidityOrchestrator.sol

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
contracts/vaults/OrionEncryptedVault.sol (1)

172-189: Bug: new portfolio loop uses old keys length.

Second loop indexes portfolio[] using portfolioLength taken from _portfolioKeys.length, leading to out-of-bounds reads or missed entries when lengths differ.

-        // Update portfolio
-        for (uint16 i = 0; i < portfolioLength; ++i) {
+        // Update portfolio
+        uint16 newPortfolioLength = uint16(portfolio.length);
+        for (uint16 i = 0; i < newPortfolioLength; ++i) {
             _portfolio[portfolio[i].token] = portfolio[i].value;
             _portfolioKeys.push(portfolio[i].token);
         }
contracts/orchestrators/LiquidityOrchestrator.sol (2)

364-378: Use SafeERC20 and reset allowance to zero first.

Avoid approve race; some ERC20s require zeroing before increasing.

-import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
 ...
 contract LiquidityOrchestrator is Ownable, ReentrancyGuard, ILiquidityOrchestrator {
+    using SafeERC20 for IERC20;
 ...
-        bool success = IERC20(asset).approve(address(adapter), amount);
-        if (!success) revert ErrorsLib.TransferFailed();
+        IERC20 token = IERC20(asset);
+        token.safeApprove(address(adapter), 0);
+        token.safeApprove(address(adapter), amount);

384-398: Mirror SafeERC20 pattern for buys.

-        bool success = IERC20(underlyingAsset).approve(address(adapter), amount);
-        if (!success) revert ErrorsLib.TransferFailed();
+        IERC20 ua = IERC20(underlyingAsset);
+        ua.safeApprove(address(adapter), 0);
+        ua.safeApprove(address(adapter), amount);
🧹 Nitpick comments (6)
eslint.config.mjs (1)

34-34: Consider ignoring reports as well.

Optional: add "**/reports" to globalIgnores to avoid linting generated artifacts.

     "**/fhevmTemp",
     "**/protocol-costs",
+    "**/reports",
contracts/vaults/OrionEncryptedVault.sol (1)

125-138: Nit: typo in variable naming.

Prefer “cipherTexts” over “cypherTexts” for consistency with crypto nomenclature.

-        bytes32[] memory cypherTexts = new bytes32[](1);
-        cypherTexts[0] = FHE.toBytes32(isIntentEValid);
+        bytes32[] memory cipherTexts = new bytes32[](1);
+        cipherTexts[0] = FHE.toBytes32(isIntentEValid);
 ...
-        FHE.requestDecryption(cypherTexts, this.callbackDecryptSingleEbool.selector);
+        FHE.requestDecryption(cipherTexts, this.callbackDecryptSingleEbool.selector);
contracts/orchestrators/LiquidityOrchestrator.sol (4)

58-66: Use uint16 for minibatch index if >255 items are possible.

currentMinibatchIndex as uint8 risks wrap in large token sets. If not guaranteed <256, widen.

-    uint8 public executionMinibatchSize;
+    uint8 public executionMinibatchSize;
 ...
-    uint8 public currentMinibatchIndex;
+    uint16 public currentMinibatchIndex;

79-90: Storing epoch arrays on-chain: consider sparse compaction.

Optional: filter out zero-amount entries before storing to reduce gas in subsequent loops.


252-266: Decode guard is weak; tighten length check.

abi.decode(bytes4,uint8) needs at least 68 bytes for dynamic encoding. Enforce a stricter bound or assert action matches known constants.

-        if (performData.length < 4) revert ErrorsLib.InvalidArguments();
+        if (performData.length < 68) revert ErrorsLib.InvalidArguments();

310-330: Start handler: filter zeros to shrink work.

Drop entries with amount == 0 when populating arrays to avoid no-op execution.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bcdd735 and 50e96f6.

📒 Files selected for processing (9)
  • .gitignore (1 hunks)
  • .prettierignore (1 hunks)
  • artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (6 hunks)
  • contracts/orchestrators/InternalStatesOrchestrator.sol (9 hunks)
  • contracts/orchestrators/LiquidityOrchestrator.sol (6 hunks)
  • contracts/vaults/OrionEncryptedVault.sol (2 hunks)
  • contracts/vaults/OrionTransparentVault.sol (0 hunks)
  • eslint.config.mjs (1 hunks)
  • reports/gas-report.txt (0 hunks)
💤 Files with no reviewable changes (2)
  • contracts/vaults/OrionTransparentVault.sol
  • reports/gas-report.txt
🧰 Additional context used
🪛 GitHub Check: Build, Lint and Test
contracts/orchestrators/LiquidityOrchestrator.sol

[warning] 350-350:
Code contains empty blocks


[warning] 334-334:
Code contains empty blocks

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: Build, Lint and Test
🔇 Additional comments (17)
.gitignore (1)

22-23: Additive ignore entries look good.

protocol-costs/ and reports/ are safely ignored. No conflicts with existing negation rules.

.prettierignore (1)

13-13: Prettier ignore is fine.

Adding protocol-costs reduces noise in formatting runs.

contracts/vaults/OrionEncryptedVault.sol (2)

104-104: Intent validation change: confirm risk acceptance.

You’ve dropped per-weight checks and now validate only totalWeight == target. Ensure downstream logic tolerates zero weights and does not rely on individual-weight constraints.


194-198: Callback access control relies on signature check — OK.

FHE.checkSignatures gate is sufficient; no extra modifier needed.

artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (1)

75-79: Reentrancy error added — consistent with nonReentrant.

ABI includes ReentrancyGuardReentrantCall; matches new ReentrancyGuard usage.

contracts/orchestrators/InternalStatesOrchestrator.sol (6)

241-241: OK to suppress code-complexity for orchestrator.

Given the action dispatcher, the pragma is reasonable.


278-279: Doc param addition improves clarity.

performData now documented; keep.


432-435: Static-analysis guards are appropriate.

Solhint/slither suppressions scoped and justified.

Also applies to: 535-535


569-569: Comment tweak LGTM.

“Skip if intent is invalid” clarifies flow.


579-579: Fee labeling clarified.

“CURATOR + PROTOCOL REVENUE SHARE FEES” reads better.


631-633: Loop-bound locals reduce repeated SLOADs.

Using nTransparentVaults/nEncryptedVaults is a small gas win; nice.

Also applies to: 636-642, 653-664

contracts/orchestrators/LiquidityOrchestrator.sol (6)

5-5: Reentrancy guard import is correct.


30-30: Inheritance update is appropriate.

nonReentrant on performUpkeep matches ABI change.


73-78: Action selectors approach LGTM.


235-246: Upkeep gating logic looks sound.

Starts only when internal epoch advanced; phases return actionable performData.


333-347: Empty body triggers automation churn; gate or revert for now.

Until implemented, either advance the phase/index or revert to avoid repeated no-op upkeeps and static-analysis “empty block” warnings.

-    function _processMinibatchSell(uint8 minibatchIndex) internal {
-        // TODO: implement.
+    function _processMinibatchSell(uint8 minibatchIndex) internal {
+        // TODO: implement.
+        revert ErrorsLib.InvalidState();
     }

349-358: Same as sell path — avoid silent no-ops.

-    function _processMinibatchBuy(uint8 minibatchIndex) internal {
-        // TODO: implement.
+    function _processMinibatchBuy(uint8 minibatchIndex) internal {
+        // TODO: implement.
+        revert ErrorsLib.InvalidState();
     }

@coderabbitai

coderabbitai Bot commented Sep 11, 2025

Copy link
Copy Markdown

Caution

No docstrings were generated.

@codecov

codecov Bot commented Sep 11, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 47.69231% with 34 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
contracts/orchestrators/LiquidityOrchestrator.sol 19.44% 29 Missing ⚠️
...racts/orchestrators/InternalStatesOrchestrator.sol 83.33% 3 Missing ⚠️
contracts/vaults/OrionEncryptedVault.sol 81.81% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@matteoettam09
matteoettam09 merged commit 6413ede into main Sep 11, 2025
3 of 5 checks passed
@matteoettam09
matteoettam09 deleted the develop branch September 11, 2025 18:24

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
contracts/orchestrators/InternalStatesOrchestrator.sol (3)

5-5: Fix import path for ReentrancyGuard (build will fail).

OpenZeppelin’s ReentrancyGuard is under security/, not utils/.

-import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
+import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

329-349: Vault mappings cleared with token keys — leaves stale/incorrect state.

In _handleStart(), vaultsTotalAssets and encryptedVaultsTotalAssets are erased using token addresses, not vault addresses. This can leave stale per‑vault values around and corrupt state.

 for (uint16 i = 0; i < _currentEpoch.tokens.length; ++i) {
   address token = _currentEpoch.tokens[i];
   delete _currentEpoch.priceArray[token];
   delete _currentEpoch.initialBatchPortfolio[token];
-  delete _currentEpoch.vaultsTotalAssets[token];
   delete _currentEpoch.finalBatchPortfolio[token];
   delete _currentEpoch.sellingOrders[token];
   delete _currentEpoch.buyingOrders[token];
   delete _currentEpoch.tokenExists[token];
   _currentEpoch.encryptedInitialBatchPortfolio[token] = _ezero;
-  _currentEpoch.encryptedVaultsTotalAssets[token] = _ezero;
   _currentEpoch.encryptedFinalBatchPortfolio[token] = _ezero;
 }
 delete _currentEpoch.tokens;
 delete _decryptedValues;
+
+// Clear previous epoch per-vault mappings before overwriting epoch vault arrays
+for (uint16 i = 0; i < transparentVaultsEpoch.length; ++i) {
+    delete _currentEpoch.vaultsTotalAssets[transparentVaultsEpoch[i]];
+}
+for (uint16 i = 0; i < encryptedVaultsEpoch.length; ++i) {
+    _currentEpoch.encryptedVaultsTotalAssets[encryptedVaultsEpoch[i]] = _ezero;
+}
 
 transparentVaultsEpoch = config.getAllOrionVaults(EventsLib.VaultType.Transparent);
 encryptedVaultsEpoch = config.getAllOrionVaults(EventsLib.VaultType.Encrypted);
 validEncryptedVaultsCount = 0;

650-671: mulDiv can revert on zero denominator in _buffer().

If protocolTotalAssets == 0 (e.g., no vaults or all zero), vaultBufferCost = delta.mulDiv(..., protocolTotalAssets) will revert even when delta is 0. Guard before the per‑vault loops.

 // Only increase buffer if current buffer is below target (conservative approach)
 if (bufferAmount > targetBufferAmount) return;
 
-uint256 deltaBufferAmount = targetBufferAmount - bufferAmount;
+if (protocolTotalAssets == 0) return;
+uint256 deltaBufferAmount = targetBufferAmount - bufferAmount;
+if (deltaBufferAmount == 0) return;
♻️ Duplicate comments (1)
artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (1)

75-79: ABI–source mismatch (AdapterNotSet) — re-verify artifacts.

Earlier we flagged that LiquidityOrchestrator.sol reverts with ErrorsLib.AdapterNotSet but the ABI lacked that error. Reconfirm the source/artifact sync for this PR build and update artifacts if needed.

#!/bin/bash
# Check whether source still references AdapterNotSet and whether ABI exposes it
rg -n "AdapterNotSet" contracts/orchestrators/LiquidityOrchestrator.sol contracts/libraries/ErrorsLib.sol || true
rg -n '"name":\s*"AdapterNotSet"' artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json || true
🧹 Nitpick comments (10)
package.json (1)

4-4: Version bump to 0.4.3 — add CHANGELOG/tag.

Patch bump looks fine; remember to update release notes and tag on merge.

contracts/interfaces/IOrionEncryptedVault.sol (1)

53-59: Clarify encoding and enforce caller constraints in implementations.

Switching to bytes for cleartexts/proof is fine, but the interface docs should spell out the expected encoding and security requirements (authorized caller, request binding).

Apply doc tweak:

-    /// @param cleartexts The cleartexts
-    /// @param decryptionProof The decryption proof
+    /// @param cleartexts The oracle-delivered cleartexts payload (encoding MUST be documented: e.g., FHE plaintext serialization or ABI-encoded values).
+    /// @param decryptionProof The proof/attestation binding requestID to cleartexts.
+    /// @dev Implementations MUST restrict msg.sender to the authorized oracle/relayer and validate requestID binding before state changes.

Please confirm the concrete implementation gates this with an onlyOracle/onlyRelayer check and validates requestID via FHE.checkSignatures or equivalent.

contracts/interfaces/IInternalStateOrchestrator.sol (1)

63-69: Bytes-based decrypt callback — document payload layout + auth.

Good to move to bytes; please extend docs to define cleartexts/proof format and require authorized-caller validation in implementations.

-    /// @param cleartexts The cleartexts
-    /// @param decryptionProof The decryption proof
+    /// @param cleartexts Oracle-delivered cleartexts payload (document encoding/layout).
+    /// @param decryptionProof Proof binding requestID↔cleartexts.
+    /// @dev Implementations MUST restrict caller to the oracle/relayer and validate requestID and payload lengths before use.

Confirm InternalStatesOrchestrator enforces caller auth and checks requestID inside callbackPreProcessDecrypt.

artifacts/contracts/interfaces/IInternalStateOrchestrator.sol/IInternalStateOrchestrator.json (1)

14-22: ABI-breaking change acknowledged — update clients and regenerate types.

callbackPreProcessDecrypt now accepts raw bytes. Ensure off-chain producer/relayer encodes payloads accordingly and that tests cover malformed length/encoding.

I can generate a minimal test that feeds bad-length cleartexts/proof to assert reverts if you want.

artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (1)

165-183: Array element getters only — consider batched reads to cut RPC round-trips.

buyingTokens/buyingAmounts and sellingTokens/sellingAmounts expose per-index getters. Off-chain reads will require N calls. Add view helpers that return the full arrays for a given epoch/minibatch to reduce I/O.

Also applies to: 185-202, 388-425

test/Orchestrators.test.ts (1)

304-354: End-to-end upkeep flow LGTM; add one assertion and drop superfluous void casts.

  • After returning to Idle, also assert liquidityOrchestrator.checkUpkeep returns true to link both orchestrators in this “with decryption” path (you already do it in the “without decryption” test).
  • Replace void expect(...) with plain expect(...) — no awaits here.
contracts/orchestrators/InternalStatesOrchestrator.sol (4)

280-283: Tighten performData length validation to avoid ambiguity.

performData is always (bytes4,uint8) → 5 bytes. Enforce exact length.

-if (performData.length < 5) revert ErrorsLib.InvalidArguments();
+if (performData.length != 5) revert ErrorsLib.InvalidArguments();

361-367: Use >= instead of (a > b || a == b).

Equivalent and clearer; apply in all three places.

-if (i1 > transparentVaultsEpoch.length || i1 == transparentVaultsEpoch.length) {
+if (i1 >= transparentVaultsEpoch.length) {
-if (i1 > nVaults || i1 == nVaults) {
+if (i1 >= nVaults) {

Also applies to: 686-691, 738-747


445-451: Prefer uint256 loop/index types to avoid silent truncation.

Indices and counters are uint16/uint8. If vault/token counts ever exceed 65,535, arithmetic overflows. Using uint256 avoids this class of bugs with negligible gas impact on modern Solidity.

Also applies to: 637-639, 641-648, 659-670, 771-780


505-531: Build cipherTexts only for valid intents to cut costs.

You allocate for all nVaults but skip invalid ones in processing. Consider packing only validEncryptedVaultsCount to reduce calldata to the FHE backend and decryption costs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e96f6 and 181a57a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • artifacts/contracts/OrionConfig.sol/OrionConfig.json (1 hunks)
  • artifacts/contracts/interfaces/IInternalStateOrchestrator.sol/IInternalStateOrchestrator.json (1 hunks)
  • artifacts/contracts/interfaces/IOrionEncryptedVault.sol/IOrionEncryptedVault.json (1 hunks)
  • artifacts/contracts/libraries/UtilitiesLib.sol/UtilitiesLib.json (1 hunks)
  • artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (6 hunks)
  • artifacts/contracts/price/PriceAdapterRegistry.sol/PriceAdapterRegistry.json (1 hunks)
  • contracts/interfaces/IInternalStateOrchestrator.sol (1 hunks)
  • contracts/interfaces/IOrionEncryptedVault.sol (1 hunks)
  • contracts/orchestrators/InternalStatesOrchestrator.sol (9 hunks)
  • contracts/orchestrators/LiquidityOrchestrator.sol (8 hunks)
  • contracts/vaults/OrionEncryptedVault.sol (4 hunks)
  • package.json (4 hunks)
  • test/Orchestrators.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/orchestrators/LiquidityOrchestrator.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build, Lint and Test
🔇 Additional comments (10)
artifacts/contracts/libraries/UtilitiesLib.sol/UtilitiesLib.json (1)

6-7: Bytecode-only delta — artifacts missing compiler/metadata; confirm deterministic builds & pin solc

Artifact artifacts/contracts/libraries/UtilitiesLib.sol/UtilitiesLib.json shows "compiler" and "metadata" === null, so the bytecode/deployedBytecode delta cannot be verified as a metadata/hash-only change. hardhat.config.ts pins solidity v0.8.28 with optimizer enabled (runs:10) and evmVersion: "cancun".

  • Regenerate artifacts (npx hardhat compile) and provide jq '.compiler, .metadata' output for the artifact to confirm the delta is only metadata/hash.
  • If it is metadata-only, accept; otherwise align compiler/settings or lock the toolchain in CI (pin solc 0.8.28) to prevent artifact churn.
package.json (2)

35-37: FHE toolchain upgrades — verify plugin/config breaking changes

  • hardhat.config.ts imports "@fhevm/hardhat-plugin" (line 1); hardhat.slither.config.ts has the import commented out.
  • No occurrences of @fhevm/mock-utils or @zama-fhe/relayer-sdk found in tests; "scripts" directory was missing so that path wasn't searched.
  • Public release notes show no documented breaking changes between the 0.0.x series and 0.1.0.
    Action: run the repo's test/CI locally on a branch with the upgraded deps and compare the plugin changelog/commits; if tests fail inspect Hardhat config and any hre.fhevm usages for API changes.

75-75: @fhevm/solidity → ^0.8.0 — confirm API compatibility.

package.json (line 75) pins @fhevm/solidity ^0.8.0. Repo consistently imports/uses euint128/externalEuint128 and FHE helpers (fromExternal, allowThis, asEuint128, add/div/mul); FHE.checkSignatures(requestID, cleartexts, decryptionProof) is invoked at contracts/vaults/OrionEncryptedVault.sol:200 and contracts/orchestrators/InternalStatesOrchestrator.sol:542. Compile the project and run tests against @fhevm/solidity ^0.8.0 to confirm there are no breaking API changes or renamed helpers.

artifacts/contracts/OrionConfig.sol/OrionConfig.json (1)

605-606: Bytecode-only change — ABI NOT VERIFIED

Verification script failed with: "/bin/bash: -c: line 4: conditional binary operator expected" — unable to confirm ABI equality. Run:
git show HEAD~1:artifacts/contracts/OrionConfig.sol/OrionConfig.json | jq -c '.abi' > /tmp/prev_abi.json && jq -c '.abi' artifacts/contracts/OrionConfig.sol/OrionConfig.json > /tmp/curr_abi.json && diff -u /tmp/prev_abi.json /tmp/curr_abi.json
If ABI is unchanged, treat this as a non-breaking deployment; if proxies are used, validate storage/layout compatibility.

artifacts/contracts/price/PriceAdapterRegistry.sol/PriceAdapterRegistry.json (1)

226-227: Runtime bytecode updated — ABI/interface intact.
Function ABI unchanged: adapterOf, configAddress, getPrice, owner, priceAdapterDecimals, renounceOwnership, setPriceAdapter, transferOwnership, unsetPriceAdapter.
If contracts are already deployed, plan a migration path — no storage-layout change expected but verify storage layout and upgrade steps separately.

artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json (2)

75-79: ReentrancyGuard error surfaced in ABI — good.

Presence of ReentrancyGuardReentrantCall confirms artifacts include the guard added to the contract.


351-355: Regenerate TypeChain/ethers typings for performUpkeep param rename (performData).

ABI shows performUpkeep param renamed to "performData" (artifacts/contracts/orchestrators/LiquidityOrchestrator.sol/LiquidityOrchestrator.json:351-355). Rebuild typings and update call sites/tests to use performData: BytesLike.

Current local attempt failed with: "Error: Cannot find module '@fhevm/hardhat-plugin'". Fix/install deps (e.g., pnpm install or npm ci / add the missing plugin) and then run:

rm -rf typechain-types && npx hardhat compile && rg -nP 'performUpkeep\s*(\sperformData:\sBytesLike' typechain-types

contracts/vaults/OrionEncryptedVault.sol (2)

104-104: Good: validate before mutating on-chain state.

Calling _validateIntent before overwriting intent storage avoids partial writes if validation fails.


180-182: LGTM: cache length before loop.

Minor gas/readability win by using newPortfolioLength.

contracts/orchestrators/InternalStatesOrchestrator.sol (1)

354-367: Validate minibatchIndex against state to prevent reordering.

Optionally require minibatchIndex == currentMinibatchIndex to avoid accidental reprocessing/reordering by the automation.

Do you want a patch enforcing this check in both _preprocessTransparentMinibatch and _preprocessEncryptedMinibatch?

Also applies to: 438-451

Comment on lines +424 to 432
"internalType": "bytes",
"name": "cleartexts",
"type": "bytes"
},
{
"internalType": "bytes[]",
"name": "signatures",
"type": "bytes[]"
"internalType": "bytes",
"name": "decryptionProof",
"type": "bytes"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

ABI-breaking change: callbackDecryptSingleEbool now (uint256, bytes cleartexts, bytes decryptionProof).

Update all callers (TS, relayer/oracle) and regenerate TypeChain types. Ensure the wire format of cleartexts/proof is specified and validated on-chain.

Consider emitting an event on successful callback with requestID to aid observability.


🏁 Script executed:

#!/bin/bash
# Locate old parameter names/usages to update
rg -nP 'callbackDecryptSingleEbool\s*\(' --type=sol --type=ts -C2
rg -n 'decryptedInput|signatures' -g '!artifacts/**'

Length of output: 176


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'PWD: %s\n' "$(pwd)"
echo '--- searching for occurrences of "callbackDecryptSingleEbool" ---'
if command -v rg >/dev/null 2>&1; then
  rg -n -uu 'callbackDecryptSingleEbool' -S -C3 || true
else
  grep -nR --exclude-dir=.git 'callbackDecryptSingleEbool' . || true
fi

echo '--- searching for "cleartexts" or "decryptionProof" occurrences ---'
if command -v rg >/dev/null 2>&1; then
  rg -n -uu 'cleartexts|decryptionProof' -S -C3 || true
else
  grep -nR --exclude-dir=.git -E 'cleartexts|decryptionProof' . || true
fi

ART="artifacts/contracts/interfaces/IOrionEncryptedVault.sol/IOrionEncryptedVault.json"
echo "--- checking artifact file: $ART ---"
if [ -f "$ART" ]; then
  echo "FOUND $ART"
  if command -v jq >/dev/null 2>&1; then
    jq '.abi[] | select(.name=="callbackDecryptSingleEbool")' "$ART" || true
  else
    python3 - <<'PY'
import json,sys
p="artifacts/contracts/interfaces/IOrionEncryptedVault.sol/IOrionEncryptedVault.json"
try:
    with open(p) as f:
        j=json.load(f)
    for a in j.get("abi",[]):
        if a.get("name")=="callbackDecryptSingleEbool":
            import pprint
            pprint.pprint(a)
            break
    else:
        print("ABI entry not found")
except Exception as e:
    print("ERROR", e, file=sys.stderr)
PY
  fi
else
  echo "$ART not found"
fi

echo '--- searching generated types / TypeChain for interface or function ---'
if command -v rg >/dev/null 2>&1; then
  rg -n -uu 'callbackDecryptSingleEbool|IOrionEncryptedVault' -S -C3 || true
else
  grep -nR --exclude-dir=.git -E 'callbackDecryptSingleEbool|IOrionEncryptedVault' . || true
fi

Length of output: 29410


ABI-breaking change: callbackDecryptSingleEbool signature changed — update callers & regenerate TypeChain

  • Confirmed: signature is now (uint256 requestID, bytes cleartexts, bytes decryptionProof) in contracts/interfaces/IOrionEncryptedVault.sol and contracts/vaults/OrionEncryptedVault.sol (implementation uses abi.decode(cleartexts,(bool))); artifacts updated (artifacts/contracts/interfaces/IOrionEncryptedVault.sol/IOrionEncryptedVault.json).
  • Action: Update all off-chain callers (TypeScript client, relayer/oracle) to the new ABI, regenerate TypeChain types, and ensure the relayer wire-format matches the on-chain decoding (add explicit on-chain validation of cleartexts/decryptionProof and test coverage).
  • Optional: emit an event on successful callback with requestID for observability.
🤖 Prompt for AI Agents
In
artifacts/contracts/interfaces/IOrionEncryptedVault.sol/IOrionEncryptedVault.json
around lines 424 to 432, the callbackDecryptSingleEbool ABI changed to (uint256
requestID, bytes cleartexts, bytes decryptionProof); update all off-chain
callers (TypeScript client, relayer/oracle) to pass requestID, and encode
cleartexts as the on-chain-expected ABI (bool via abi.encode/abi.decode) and
decryptionProof as bytes, then regenerate TypeChain types so callers’ signatures
match the new ABI; also update relayer wire-format to send cleartexts as an
ABI-encoded bool, add on-chain validation in OrionEncryptedVault to assert
cleartexts length/decoded type and validate decryptionProof, add tests covering
successful and malformed payloads, and consider emitting an event with requestID
on successful callback for observability.

Comment on lines 542 to 553
@@ -563,12 +570,9 @@ contract InternalStatesOrchestrator is SepoliaConfig, Ownable, ReentrancyGuard,
address vault = encryptedVaultsEpoch[i];
IOrionEncryptedVault vaultContract = IOrionEncryptedVault(vault);

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

Callback sentinel check is incorrect; decode as uint256[] and handle empty.

Comparing cleartexts to abi.encode(uint256(0)) is incompatible with decoding as uint256[] and risks a revert. Decode first, then branch on length.

 FHE.checkSignatures(requestID, cleartexts, decryptionProof);
-
-if (keccak256(cleartexts) == keccak256(abi.encode(uint256(0)))) {
-    currentPhase = InternalUpkeepPhase.Buffering;
-    currentMinibatchIndex = 0;
-    return;
-}
-
-// Store decrypted values for processing in the next phase.
-// TODO(fhevm): avoid breaking down this into two phases, consider letting Zama callback do all the work.
-_decryptedValues = abi.decode(cleartexts, (uint256[]));
+uint256[] memory values = abi.decode(cleartexts, (uint256[]));
+if (values.length == 0) {
+    currentPhase = InternalUpkeepPhase.Buffering;
+    currentMinibatchIndex = 0;
+    return;
+}
+// Store decrypted values for processing in the next phase.
+_decryptedValues = values;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
if (keccak256(cleartexts) == keccak256(abi.encode(uint256(0)))) {
currentPhase = InternalUpkeepPhase.Buffering;
currentMinibatchIndex = 0;
return;
}
// Store decrypted values for processing in the next phase.
// TODO(fhevm): avoid breaking down this into two phases, consider letting Zama callback do all the work.
_decryptedValues = decryptedValues;
_decryptedValues = abi.decode(cleartexts, (uint256[]));
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
uint256[] memory values = abi.decode(cleartexts, (uint256[]));
if (values.length == 0) {
currentPhase = InternalUpkeepPhase.Buffering;
currentMinibatchIndex = 0;
return;
}
// Store decrypted values for processing in the next phase.
// TODO(fhevm): avoid breaking down this into two phases, consider letting Zama callback do all the work.
_decryptedValues = values;
🤖 Prompt for AI Agents
In contracts/orchestrators/InternalStatesOrchestrator.sol around lines 542 to
553, the code currently compares raw cleartexts to abi.encode(uint256(0)) before
decoding, which is incompatible with the subsequent abi.decode to uint256[] and
can revert; instead, first abi.decode(cleartexts, (uint256[])) into a local
uint256[] variable, then check if the decoded array is empty (length == 0) and
if so set currentPhase = InternalUpkeepPhase.Buffering, currentMinibatchIndex =
0 and return; remove the keccak256 comparison and use the decoded array for
subsequent storage by assigning _decryptedValues = decodedArray.

Comment on lines +125 to 138
function _validateIntent(address[] memory assets, euint128 totalWeight) internal {
_validateIntentAssets(assets);

ebool isIntentEValid = FHE.and(areWeightsValid, FHE.eq(totalWeight, _encryptedTotalWeight));
ebool isIntentEValid = FHE.eq(totalWeight, _encryptedTotalWeight);

// slither-disable-next-line unused-return
FHE.allowThis(isIntentEValid);

bytes32[] memory cypherTexts = new bytes32[](1);
cypherTexts[0] = FHE.toBytes32(isIntentEValid);
bytes32[] memory cipherTexts = new bytes32[](1);
cipherTexts[0] = FHE.toBytes32(isIntentEValid);

// slither-disable-next-line unused-return
FHE.requestDecryption(cypherTexts, this.callbackDecryptSingleEbool.selector);
FHE.requestDecryption(cipherTexts, this.callbackDecryptSingleEbool.selector);
}

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

Race on decryption callbacks — stale requests can flip isIntentValid.

Multiple submitIntent calls can overlap; callbackDecryptSingleEbool accepts any valid signed callback and will update isIntentValid for the latest or a stale request indistinguishably. Store the latest request ID and ignore stale callbacks.

Apply:

 function _validateIntent(address[] memory assets, euint128 totalWeight) internal {
   _validateIntentAssets(assets);

   ebool isIntentEValid = FHE.eq(totalWeight, _encryptedTotalWeight);
   // slither-disable-next-line unused-return
   FHE.allowThis(isIntentEValid);

   bytes32[] memory cipherTexts = new bytes32[](1);
   cipherTexts[0] = FHE.toBytes32(isIntentEValid);
-  // slither-disable-next-line unused-return
-  FHE.requestDecryption(cipherTexts, this.callbackDecryptSingleEbool.selector);
+  // Track the latest request to guard against stale callbacks
+  uint256 reqId = FHE.requestDecryption(cipherTexts, this.callbackDecryptSingleEbool.selector);
+  _latestDecryptRequestId = reqId;
 }

Add state (outside this hunk):

// Track the most recent decryption request for intent validation
uint256 private _latestDecryptRequestId;

Comment on lines +195 to 203
function callbackDecryptSingleEbool(
uint256 requestID,
bytes calldata cleartexts,
bytes calldata decryptionProof
) external {
FHE.checkSignatures(requestID, cleartexts, decryptionProof);

isIntentValid = abi.decode(cleartexts, (bool));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Gate callback to the latest request; optionally clear the latch.

Guard against out-of-order deliveries and stray callbacks.

Apply:

 function callbackDecryptSingleEbool(
   uint256 requestID,
   bytes calldata cleartexts,
   bytes calldata decryptionProof
 ) external {
   FHE.checkSignatures(requestID, cleartexts, decryptionProof);
-  isIntentValid = abi.decode(cleartexts, (bool));
+  if (requestID != _latestDecryptRequestId) {
+    return; // drop stale callback
+  }
+  isIntentValid = abi.decode(cleartexts, (bool));
+  _latestDecryptRequestId = 0; // optional: reset
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function callbackDecryptSingleEbool(
uint256 requestID,
bytes calldata cleartexts,
bytes calldata decryptionProof
) external {
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
isIntentValid = abi.decode(cleartexts, (bool));
}
function callbackDecryptSingleEbool(
uint256 requestID,
bytes calldata cleartexts,
bytes calldata decryptionProof
) external {
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
if (requestID != _latestDecryptRequestId) {
return; // drop stale callback
}
isIntentValid = abi.decode(cleartexts, (bool));
_latestDecryptRequestId = 0; // optional: reset
}

@coderabbitai coderabbitai Bot mentioned this pull request Sep 15, 2025
Merged
This was referenced Nov 17, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Feb 4, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Mar 9, 2026
Merged
@coderabbitai coderabbitai Bot mentioned this pull request Jul 18, 2026
Merged
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.

1 participant