Skip to content

Dev - #162

Merged
matteoettam09 merged 39 commits into
mainfrom
dev
Mar 21, 2026
Merged

Dev#162
matteoettam09 merged 39 commits into
mainfrom
dev

Conversation

@matteoettam09

@matteoettam09 matteoettam09 commented Mar 9, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Release Notes (v2.2.0)

  • New Features

    • Added APY-based strategist with equal-weight and APY-weighted modes.
    • Implemented automatic strategist-to-vault linking with ERC-165 validation.
    • Added cross-rate pricing support via quote feed configuration.
    • Added pendingRedeemBatch helper for redemption visibility.
  • Improvements

    • Enhanced vault buffer tracking with epoch history.
    • Improved redeem fulfillment efficiency.
  • Bug Fixes

    • Refined strategist interface to enforce vault linkage before intent submission.

ojasarora77 and others added 12 commits March 6, 2026 17:54
…logic

- Updated IOrionStrategist to inherit from IERC165 and added setVault function for linking to a vault.
- Modified submitIntent to operate on the linked vault.
- Introduced error handling for vault linking in ErrorsLib.
- Updated KBestTvlWeightedAverage and KBestTvlWeightedAverageInvalid contracts to reflect new interface changes.
- Implemented vault linking logic in OrionVault to ensure strategists are correctly associated with their vaults.
- Added comprehensive tests for strategist assignment, including scenarios for EOA, non-ERC165, and ERC165 non-IOrionStrategist contracts.
- Enhanced the test suite for vault creation and strategist linking logic.
@coderabbitai

coderabbitai Bot commented Mar 9, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR refactors strategist-vault interactions to use ERC-165 interface detection and two-step binding (setVault followed by submitIntent), introduces a new APY-weighted strategist with share-price checkpointing, adds cross-rate pricing support via optional quote feeds in ChainlinkPriceAdapter, updates vault redeem batching and buffer tracking in LiquidityOrchestrator, and removes fixture-based orchestrator tests while adding comprehensive strategist linking and strategy-specific test coverage.

Changes

Cohort / File(s) Summary
ERC-165 Strategist Interface
contracts/interfaces/IOrionStrategist.sol, contracts/libraries/ErrorsLib.sol
Updated IOrionStrategist to extend IERC165 and split submitIntent(vault) into setVault(address) and parameterless submitIntent(). Added StrategistVaultAlreadyLinked error for preventing multi-vault linking.
Strategist Implementations
contracts/strategies/KBestTvlWeightedAverage.sol, contracts/strategies/KBestApyStrategist.sol, contracts/test/KBestTvlWeightedAverageInvalid.sol
Refactored KBestTvlWeightedAverage to use immutable CONFIG, removed investmentUniverse parameter, added ERC-165 support and vault binding. New KBestApyStrategist contract with APY-weighted position selection, share-price checkpointing, and fallback equal-weighting. Test contract updated to match interface changes.
Vault Strategist Linking
contracts/vaults/OrionVault.sol, contracts/vaults/OrionTransparentVault.sol
Added _linkStrategistVault() internal function to auto-detect and link IOrionStrategist implementations via ERC-165. Vault initialization now calls this function to establish strategist-vault relationship automatically. Added pendingRedeemBatch() external getter and optimized fulfillRedeem burn semantics.
Price Adapter Cross-Rates
contracts/price/ChainlinkPriceAdapter.sol, contracts/interfaces/ILiquidityOrchestrator.sol
Extended ChainlinkPriceAdapter.configureFeed() with optional quoteFeed parameter for cross-rate normalization. getPriceData now returns normalized 18-decimal prices when quote feed configured. Updated FeedConfigured event signature.
Orchestrator Buffer Tracking
contracts/LiquidityOrchestrator.sol
Replaced epoch-scoped epochFeesAccrued with two-step protocol-fee accrual. Added _epochBufferHistory checkpointing and _cachedAssetsHash/_cachedVaultsHash for failure recovery. Refactored _buildEpochStateCommitmentAndComponents() and epoch reset logic.
New Test Contracts & Mocks
contracts/test/MockERC165NonStrategist.sol, contracts/test/MockNonERC165Contract.sol, contracts/test/MockChainlinkFeed.sol, contracts/test/MockNoDecimalsAsset.sol
Added ERC-165 test mocks (compliant and non-compliant), MockChainlinkFeed implementing AggregatorV3Interface for price adapter testing, and MockNoDecimalsAsset to exercise revert handling on decimals() calls.
Strategist Linking & APY Tests
test/StrategistLinking.test.ts, test/NewStrategies.test.ts
Comprehensive test suites validating auto-linking of IOrionStrategist via ERC-165 during vault creation, vault-reuse prevention, idempotent re-linking, and full strategist intent submission workflows. APY strategist tests verify checkpoint recording, APY ranking, equal/proportional weighting modes, residual weight handling.
Updated Strategy Tests
test/PassiveStrategist.test.ts
Removed investmentUniverse constructor argument and adapted all submitIntent() calls to parameterless form. Updated config getter references and asset enumeration via orionConfig.getAllWhitelistedAssets().
Price Adapter Tests
test/crossAsset/ChainlinkPriceAdapter.test.ts, test/ChainlinkPriceAdapterUnit.test.ts, test/crossAsset/ERC4626ExecutionAdapter.test.ts, test/crossAsset/ERC4626PriceAdapter.test.ts
New unit test suite for cross-rate scenarios and quote feed staleness checks. Updated existing cross-asset tests to pass ethers.ZeroAddress (no quote feed) in configureFeed calls.
Removed Test Infrastructure
test/RedeemBeforeDepositOrder.test.ts, test/Removal.test.ts, test/VerifyPerformDataRejection.test.ts, test/helpers/orchestratorHelpers.ts, test/orchestrator/Orchestrators.test.ts, test/fixtures/*
Deleted fixture-based orchestrator tests and supporting zkVM proof utilities. Removed 14 JSON fixture files (Orchestrator*.json, RedeemBeforeDepositOrder*.json, Removal*.json). Removed orchestratorHelpers.ts (advanceEpochTime, processFullEpoch).
Configuration & Docs
package.json, .prettierignore, README.md
Version bump 2.1.0 → 2.2.0. Removed test/fixtures from .prettierignore. Updated README badges (removed Codecov/Sourcery, added CodeRabbit).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TransparentVault
    participant Strategist
    participant ERC165Checker
    participant Config

    User->>TransparentVault: initialize(strategist_)
    TransparentVault->>TransparentVault: Set portfolioIntent to 100% strategist
    TransparentVault->>TransparentVault: _linkStrategistVault(strategist_)
    TransparentVault->>ERC165Checker: Check if strategist<br/>is contract
    alt strategist is contract
        TransparentVault->>ERC165Checker: IERC165(strategist).supportsInterface<br/>(IOrionStrategist)
        alt supports IOrionStrategist
            ERC165Checker-->>TransparentVault: true
            TransparentVault->>Strategist: setVault(address(this))
            Strategist->>Strategist: Store vault reference
            Strategist-->>TransparentVault: ✓ Linked
        else does not support
            ERC165Checker-->>TransparentVault: false
            TransparentVault-->>TransparentVault: Skip linking (non-strategist)
        end
    else not a contract (EOA)
        TransparentVault-->>TransparentVault: Skip linking (EOA)
    end
    TransparentVault-->>User: Initialized
Loading
sequenceDiagram
    participant Caller
    participant KBestStrategist
    participant LinkedVault
    participant Config

    Caller->>KBestStrategist: setVault(address vault_)
    KBestStrategist->>KBestStrategist: Validate vault != zero
    KBestStrategist->>KBestStrategist: Check not already<br/>linked to different vault
    alt already linked to different vault
        KBestStrategist-->>Caller: ✗ StrategistVaultAlreadyLinked
    else new or same vault
        KBestStrategist->>KBestStrategist: _vault = vault_
        KBestStrategist-->>Caller: ✓ Stored
    end

    Caller->>KBestStrategist: submitIntent()
    KBestStrategist->>KBestStrategist: Validate _vault != zero
    KBestStrategist->>Config: getAllWhitelistedAssets()
    Config-->>KBestStrategist: assets[]
    KBestStrategist->>KBestStrategist: Calculate positions<br/>(TVL/APY ranking)
    KBestStrategist->>KBestStrategist: Compute normalized<br/>weights (sum to 1e9)
    KBestStrategist->>LinkedVault: submitIntent(IntentPosition[])
    LinkedVault->>LinkedVault: Validate & store intent
    LinkedVault-->>KBestStrategist: ✓ Accepted
    KBestStrategist-->>Caller: ✓ Submitted
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

  • Dev #130: Both PRs modify strategist–vault linking patterns and IOrionStrategist interface surface, including ERC-165 checks and vault auto-linking logic.
  • Passive curator #101: Both PRs implement the same push-based submitIntent()/setVault() refactor with ERC-165 interface detection and matching updates to KBestTvlWeightedAverage and vault wiring.
  • Zk orchestrator #140: Both PRs overlap on LiquidityOrchestrator and related interfaces (ILiquidityOrchestrator), strategist–vault linking (IOrionStrategist, KBestTvlWeightedAverage, vault contracts), and ErrorsLib modifications.

Poem

🐰 Strategists now dance with vaults in two steps true,
ERC-165 whispers "I'm real, not a phew!"
APY-weighted rabbits hop through checkpoints high,
Cross-rates normalize beneath a pricing sky.
Linked by design, no more vault confusion—
A hop toward harmony, a strategic illusion!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Dev' is vague and generic, using a non-descriptive term that does not convey meaningful information about the changeset. Replace with a specific, descriptive title that summarizes the main change—for example, 'Refactor strategist vault linking with ERC165 support' or 'Implement two-step vault binding for strategists'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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

Caution

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

⚠️ Outside diff range comments (3)
test/PassiveStrategist.test.ts (2)

300-303: ⚠️ Potential issue | 🔴 Critical

Inconsistent submitIntent call uses old signature.

This test still passes transparentVault as an argument to submitIntent, but the updated KBestTvlWeightedAverage contract now uses a parameterless submitIntent(). This will either fail to compile (if TypeChain types are updated) or call a non-existent overload.

🐛 Proposed fix
-      await expect(passiveStrategist.connect(strategist).submitIntent(transparentVault)).to.be.revertedWithCustomError(
+      await expect(passiveStrategist.connect(strategist).submitIntent()).to.be.revertedWithCustomError(
         passiveStrategist,
         "OrderIntentCannotBeEmpty",
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/PassiveStrategist.test.ts` around lines 300 - 303, The test is calling
submitIntent with an outdated signature; update the call to use the new
parameterless submitIntent() on the connected strategist instance (replace
passiveStrategist.connect(strategist).submitIntent(transparentVault) with
passiveStrategist.connect(strategist).submitIntent()), and search for any other
usages of submitIntent(transparentVault) in the test suite to change them to the
parameterless submitIntent() so the assertion using
revertedWithCustomError(passiveStrategist, "OrderIntentCannotBeEmpty") still
targets the correct call.

333-348: ⚠️ Potential issue | 🟠 Major

Test does not call submitIntent() after updateParameters.

The loop updates k via updateParameters but then checks vault.getIntent() without calling submitIntent(). The vault's intent won't reflect the new k value until submitIntent() is called, so this test is checking stale intent data from beforeEach.

🐛 Proposed fix
     it("should maintain valid intent weights after parameter changes", async function () {
       // Test various k values to ensure weights always sum to 100%
       for (let k = 1; k <= 4; k++) {
         await passiveStrategist.connect(strategist).updateParameters(k);
+        await passiveStrategist.connect(strategist).submitIntent();
         const [_tokens, weights] = await transparentVault.getIntent();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/PassiveStrategist.test.ts` around lines 333 - 348, The test updates
parameters via passiveStrategist.connect(strategist).updateParameters(k) but
then reads stale intent from transparentVault.getIntent() without applying the
new parameters; call passiveStrategist.connect(strategist).submitIntent() (or
the appropriate submitIntent method on passiveStrategist) after each
updateParameters(k) and before transparentVault.getIntent() so the vault
reflects the updated intent; ensure you await the submitIntent() call so the
subsequent transparentVault.getIntent() returns the new weights and the summed
totalWeight check is valid.
contracts/strategies/KBestTvlWeightedAverage.sol (1)

131-136: ⚠️ Potential issue | 🟡 Minor

Potential overflow in weight calculation.

The calculation uint32((topTvls[i] * intentScale) / totalTVL) can overflow if topTvls[i] * intentScale exceeds type(uint256).max. While unlikely with typical TVL values, KBestApyWeightedAverage uses Math.mulDiv for this same calculation, which handles overflow safely.

🛡️ Proposed fix using mulDiv
     for (uint16 i = 0; i < kActual; ++i) {
-        uint32 weight = uint32((topTvls[i] * intentScale) / totalTVL);
+        uint32 weight = uint32(Math.mulDiv(topTvls[i], intentScale, totalTVL));
         intent[i] = IOrionTransparentVault.IntentPosition({ token: tokens[i], weight: weight });
         sumWeights += weight;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/strategies/KBestTvlWeightedAverage.sol` around lines 131 - 136, The
weight multiplication can overflow when computing uint32((topTvls[i] *
intentScale) / totalTVL); replace the raw multiply/divide with a safe mulDiv
call (as used in KBestApyWeightedAverage) to compute weight =
uint32(Math.mulDiv(topTvls[i], intentScale, totalTVL)); update the assignment to
intent[i] = IOrionTransparentVault.IntentPosition({ token: tokens[i], weight:
weight }) and keep sumWeights accumulation unchanged; ensure Math.mulDiv is
imported/available in this contract.
🤖 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/interfaces/IOrionStrategist.sol`:
- Around line 12-17: The setVault(address vault_) function is currently
permissionless and allows third parties to pre-bind a strategist; change it so
only the vault itself can bind the strategist and add a cross-check to ensure
the vault acknowledges this strategist: require vault_ != address(0),
require(msg.sender == vault_) (or otherwise authenticate the caller as the
vault), and call the vault contract (e.g., IVault(vault_).strategist() or
equivalent) to assert it already points to this strategist (or that it expects
this strategist) before storing the vault and emitting the link; keep the
existing StrategistVaultAlreadyLinked behavior for idempotent calls from the
same vault.

In `@contracts/strategies/ApyStrategistBase.sol`:
- Around line 114-129: The NatSpec for _getAssetApy is misleading: the function
calculates a simple annualized return ((P1−P0)/P0 scaled to a year) not a
compounded APY; update the comment above function _getAssetApy to say it returns
a "simple annualized return (non‑compounded) in WAD" and mention it is used as
an APY proxy for ranking, so readers know compounding is not applied. Also keep
the existing behavior and names (_getAssetApy, cp.sharePrice, SECONDS_PER_YEAR,
WAD) unchanged—only adjust the documentation text to reflect “simple annualized
return” semantics.

In `@contracts/strategies/EqualWeight.sol`:
- Around line 43-45: The cast to uint16 for the asset count (uint16 n =
uint16(assets.length)) in EqualWeight.sol risks truncation when assets.length >
65,535; replace the uint16 cast with a uint256 counter (e.g., use uint256 n =
assets.length) or add a defensive require that assets.length <= type(uint16).max
before casting, and keep the existing empty-check revert
(ErrorsLib.OrderIntentCannotBeEmpty()) intact; update any loops or uses of n
(and related index variables) such as in functions referencing
assets/getAllWhitelistedAssets() to use the matching uint256 type.

In `@contracts/strategies/KBestApyEqualWeighted.sol`:
- Around line 27-30: The submitIntent function in KBestApyEqualWeighted is
implementing a declaration from IOrionStrategist (via ApyStrategistBase) but
lacks the required Solidity override specifier; update the function signature
for submitIntent() in KBestApyEqualWeighted to include the override keyword
(e.g., function submitIntent() external override) so it properly overrides the
interface method.

In `@test/NewStrategies.test.ts`:
- Around line 812-837: The test only sets APY state once (inside the hasApy && k
=== 1 branch) but then relies on that state for subsequent iterations, making it
fragile; change the loop so that for every iteration where hasApy is true you
run the APY setup (call strategy.updateCheckpoints(...), advancePastMinWindow(),
and simulateGains(...) each time) or explicitly reset state between iterations
so each k with hasApy=true performs its own checkpointing and gain simulation;
locate and modify the block around strategy.updateCheckpoints,
advancePastMinWindow, and simulateGains in the loop (and/or add a reset/teardown
before each iteration) to ensure APY data is established per-iteration rather
than only when k === 1.
- Around line 40-49: Add explicit assertions before using non-null assertions on
`event` and `parsed` in the helper inside NewStrategies.test.ts: after locating
`event` from `receipt?.logs` (via `factory.interface.parseLog`) assert `event`
is defined (e.g., `expect(event).toBeDefined()` or throw a descriptive error)
and then parse it into `parsed` and assert `parsed` and `parsed.args[0]` are
defined before calling `ethers.getContractAt("OrionTransparentVault",
parsed!.args[0])`; this replaces blind `event!`/`parsed!` usage with clear,
early failures and makes the test error messages informative.

In `@test/StrategistLinking.test.ts`:
- Around line 172-194: Add a test that covers the initialize-time linking path
by creating a fresh KBestTvlWeightedAverage strategy and passing it into
createVault() so the vault is initialized with the strategist (exercise
OrionTransparentVault.initialize()), then deposit TVL as in the existing test
and assert that strategy.connect(user).submitIntent() does not revert and
returns the expected intent (e.g., tokens length equals 2); mirror the setup
used in the current test (mintAndDeposit, underlyingAsset/assets, vault
getIntent) but omit the explicit vault.updateStrategist() call to verify
initialize-time linking works.

---

Outside diff comments:
In `@contracts/strategies/KBestTvlWeightedAverage.sol`:
- Around line 131-136: The weight multiplication can overflow when computing
uint32((topTvls[i] * intentScale) / totalTVL); replace the raw multiply/divide
with a safe mulDiv call (as used in KBestApyWeightedAverage) to compute weight =
uint32(Math.mulDiv(topTvls[i], intentScale, totalTVL)); update the assignment to
intent[i] = IOrionTransparentVault.IntentPosition({ token: tokens[i], weight:
weight }) and keep sumWeights accumulation unchanged; ensure Math.mulDiv is
imported/available in this contract.

In `@test/PassiveStrategist.test.ts`:
- Around line 300-303: The test is calling submitIntent with an outdated
signature; update the call to use the new parameterless submitIntent() on the
connected strategist instance (replace
passiveStrategist.connect(strategist).submitIntent(transparentVault) with
passiveStrategist.connect(strategist).submitIntent()), and search for any other
usages of submitIntent(transparentVault) in the test suite to change them to the
parameterless submitIntent() so the assertion using
revertedWithCustomError(passiveStrategist, "OrderIntentCannotBeEmpty") still
targets the correct call.
- Around line 333-348: The test updates parameters via
passiveStrategist.connect(strategist).updateParameters(k) but then reads stale
intent from transparentVault.getIntent() without applying the new parameters;
call passiveStrategist.connect(strategist).submitIntent() (or the appropriate
submitIntent method on passiveStrategist) after each updateParameters(k) and
before transparentVault.getIntent() so the vault reflects the updated intent;
ensure you await the submitIntent() call so the subsequent
transparentVault.getIntent() returns the new weights and the summed totalWeight
check is valid.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 370fa347-8591-4573-b141-cd9d708283aa

📥 Commits

Reviewing files that changed from the base of the PR and between da40c8b and cefc6be.

📒 Files selected for processing (15)
  • contracts/interfaces/IOrionStrategist.sol
  • contracts/libraries/ErrorsLib.sol
  • contracts/strategies/ApyStrategistBase.sol
  • contracts/strategies/EqualWeight.sol
  • contracts/strategies/KBestApyEqualWeighted.sol
  • contracts/strategies/KBestApyWeightedAverage.sol
  • contracts/strategies/KBestTvlWeightedAverage.sol
  • contracts/test/KBestTvlWeightedAverageInvalid.sol
  • contracts/test/MockERC165NonStrategist.sol
  • contracts/test/MockNonERC165Contract.sol
  • contracts/vaults/OrionTransparentVault.sol
  • contracts/vaults/OrionVault.sol
  • test/NewStrategies.test.ts
  • test/PassiveStrategist.test.ts
  • test/StrategistLinking.test.ts

Comment thread contracts/interfaces/IOrionStrategist.sol
Comment thread contracts/strategies/ApyStrategistBase.sol Outdated
Comment thread contracts/strategies/EqualWeight.sol Outdated
Comment thread contracts/strategies/KBestApyEqualWeighted.sol Outdated
Comment thread test/NewStrategies.test.ts
Comment thread test/NewStrategies.test.ts
Comment thread test/StrategistLinking.test.ts

@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

🤖 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/strategies/ApyStrategistBase.sol`:
- Around line 52-57: setVault currently allows any caller to set _vault once and
thus can be hijacked; replace this with a two-step, contract-only linking flow:
add proposeVault(address proposed) callable only by the strategist/admin that
sets a new storage variable proposedVault, and change setVault to be callable
only by the proposedVault contract itself (require(msg.sender == proposedVault
&& Address.isContract(msg.sender))) which then sets _vault and clears
proposedVault (and keep the existing zero-address checks and
StrategistVaultAlreadyLinked guard). Update ErrorsLib usage to validate and
revert appropriately when propose/accept flow is misused (e.g., no proposed
vault or already linked).
- Line 52: The function setVault in ApyStrategistBase.sol implements
IOrionStrategist.setVault but is missing the Solidity override specifier; update
the function declaration for setVault to include the override keyword (e.g.,
function setVault(address vault_) external override { ... }) so the compiler
recognizes it as implementing IOrionStrategist.setVault and matches the
interface signature.
- Around line 69-70: The loops in ApyStrategistBase (e.g., where `uint16 n =
uint16(assets.length); for (uint16 i = 0; i < n; ++i)`) silently truncate when
`assets.length` exceeds 65,535; update these to use `uint256` for the length and
loop index (replace `uint16 n` and `uint16 i` with `uint256`) throughout the
file (including the loop ranges at the locations noted and in
`_submitIntentInternal`), or alternatively add an explicit require that reverts
if `assets.length > type(uint16).max` and keep `uint16` if you must; also ensure
upstream `OrionConfig.getAllWhitelistedAssets()` does not cast to `uint16` (or
adds the same guard) so the length is consistent end-to-end.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02f6fd32-ae2d-4dcc-ad09-834880e620ef

📥 Commits

Reviewing files that changed from the base of the PR and between cefc6be and 13fb046.

📒 Files selected for processing (1)
  • contracts/strategies/ApyStrategistBase.sol

Comment thread contracts/strategies/KBestApyStrategist.sol
Comment thread contracts/strategies/KBestApyStrategist.sol
Comment thread contracts/strategies/ApyStrategistBase.sol Outdated
Comment thread contracts/strategies/ApyStrategistBase.sol
@codecov

codecov Bot commented Mar 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.21827% with 37 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
contracts/LiquidityOrchestrator.sol 17.85% 23 Missing ⚠️
contracts/vaults/OrionVault.sol 41.17% 10 Missing ⚠️
contracts/price/ChainlinkPriceAdapter.sol 89.65% 3 Missing ⚠️
contracts/strategies/KBestApyStrategist.sol 99.02% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@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/strategies/EqualWeight.sol`:
- Around line 31-36: The function setVault in EqualWeight implements
IOrionStrategist but lacks the required Solidity override specifier; update the
function signature to include override (e.g., change "function setVault(address
vault_) external {" to "function setVault(address vault_) external override {"
or "external override(IOrionStrategist)" if there are multiple inherited
interfaces) so the compiler recognizes this as the interface implementation
while leaving the body logic unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4d3a9437-0155-4e15-bd7a-1b7c3f2ea7f1

📥 Commits

Reviewing files that changed from the base of the PR and between 13fb046 and 2b38ac6.

📒 Files selected for processing (7)
  • contracts/strategies/ApyStrategistBase.sol
  • contracts/strategies/EqualWeight.sol
  • contracts/strategies/KBestApyEqualWeighted.sol
  • contracts/strategies/KBestApyWeightedAverage.sol
  • contracts/test/MockNoDecimalsAsset.sol
  • test/NewStrategies.test.ts
  • test/StrategistLinking.test.ts

Comment thread contracts/strategies/EqualWeight.sol Outdated

@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/strategies/KBestApyWeightedAverage.sol`:
- Around line 25-27: Reject zero selections and guard before dividing/indexing:
validate and reject k == 0 in the constructor (KBestApyWeightedAverage) and in
updateParameters so the stored k cannot be set to 0; additionally, in the method
that computes kActual/intentScale/intent (the selection/weighting flow where
variables kActual, intentScale and intent are used) add a require/revert if
kActual == 0 before performing intentScale / kActual or indexing intent[0] to
avoid panics from division-by-zero or empty-array access.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 92254ced-e1c9-4ae9-9f34-8904f4c271e5

📥 Commits

Reviewing files that changed from the base of the PR and between 2b38ac6 and 683b1c8.

📒 Files selected for processing (1)
  • contracts/strategies/KBestApyWeightedAverage.sol

Comment thread contracts/strategies/KBestApyWeightedAverage.sol Outdated
ojasarora77 and others added 4 commits March 10, 2026 20:08

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

Caution

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

⚠️ Outside diff range comments (2)
test/PassiveStrategist.test.ts (1)

300-303: ⚠️ Potential issue | 🔴 Critical

Inconsistent API usage: submitIntent is called with an argument but the interface is now parameterless.

Line 300 passes transparentVault as an argument to submitIntent(), but the IOrionStrategist interface now defines submitIntent() as parameterless. This will cause a compilation or runtime error.

🐛 Proposed fix
-      await expect(passiveStrategist.connect(strategist).submitIntent(transparentVault)).to.be.revertedWithCustomError(
+      await expect(passiveStrategist.connect(strategist).submitIntent()).to.be.revertedWithCustomError(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/PassiveStrategist.test.ts` around lines 300 - 303, The test calls
submitIntent(transparentVault) but the IOrionStrategist API changed to a
parameterless submitIntent(); update the test to call submitIntent() with no
arguments (remove transparentVault), and ensure any related expectations still
reference passiveStrategist.connect(strategist).submitIntent(); confirm the test
uses the correct signer/context (connect(strategist)) and that no other tests
pass parameters to submitIntent anywhere else.
contracts/strategies/KBestTvlWeightedAverage.sol (1)

88-110: 🧹 Nitpick | 🔵 Trivial

Consider using sentinel pattern for consistency with KBestApyStrategist.

_selectTopKAssets initializes maxIndex = 0, while KBestApyStrategist._selectTopKByApy uses maxIndex = type(uint16).max as a sentinel. The current approach works (defaulting to index 0 when all TVLs are equal), but using the sentinel pattern consistently across strategists would improve maintainability.

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

In `@contracts/strategies/KBestTvlWeightedAverage.sol` around lines 88 - 110, In
_selectTopKAssets, replace the current defaulting behavior by using the sentinel
pattern like KBestApyStrategist: initialize maxIndex as type(uint16).max inside
the outer loop, update maxIndex when a new maxTVL is found, and after the inner
loop ensure maxIndex != type(uint16).max before marking used and writing
tokens/topTvls (or break if sentinel remains); this makes selection consistent
with KBestApyStrategist and avoids implicit defaulting to index 0.
♻️ Duplicate comments (3)
test/NewStrategies.test.ts (1)

34-53: 🧹 Nitpick | 🔵 Trivial

Add explicit guard before non-null assertions in createVault helper.

The helper uses event! and parsed! which will throw unclear errors if the event isn't found. The past review suggested adding explicit checks.

♻️ Suggested improvement
   const event = receipt?.logs.find((log) => {
     try {
       const parsed = factory.interface.parseLog(log);
       return parsed?.name === "OrionVaultCreated";
     } catch {
       return false;
     }
   });
+  if (!event) throw new Error("OrionVaultCreated event not found in transaction receipt");
   const parsed = factory.interface.parseLog(event!);
+  if (!parsed) throw new Error("Failed to parse OrionVaultCreated event");
   return ethers.getContractAt("OrionTransparentVault", parsed!.args[0]) as unknown as Promise<OrionTransparentVault>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/NewStrategies.test.ts` around lines 34 - 53, The createVault helper
currently uses non-null assertions on event and parsed which can produce unclear
runtime errors; modify createVault to explicitly check that the receipt logs
contain the "OrionVaultCreated" event (after calling factory.interface.parseLog)
and throw a clear, descriptive Error if event is undefined or parsing fails
(include strategistAddr or tx hash for context), and also validate
parsed.args[0] exists before calling
ethers.getContractAt("OrionTransparentVault", ...); update the code paths that
reference event and parsed to use these guards instead of event! and parsed!.
test/StrategistLinking.test.ts (1)

24-43: 🧹 Nitpick | 🔵 Trivial

Same createVault helper pattern needs explicit guards.

This helper duplicates the pattern from NewStrategies.test.ts with the same non-null assertion concern.

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

In `@test/StrategistLinking.test.ts` around lines 24 - 43, The createVault helper
uses non-null assertions for receipt, event and parsed which can crash tests;
add explicit guards and clear errors: after awaiting tx.wait() verify receipt is
defined, ensure receipt.logs contains a log that parses to an
"OrionVaultCreated" event (handle parse failures with try/catch), assert the
found event and parsed object are not undefined and that parsed.args[0] exists
before calling factory.interface.parseLog(event) and ethers.getContractAt; if
any check fails, throw a descriptive error so failures are deterministic and
easy to debug (update symbols: createVault, receipt, event, parsed,
factory.interface.parseLog, parsed.args[0]).
contracts/strategies/KBestApyStrategist.sol (1)

82-87: ⚠️ Potential issue | 🔴 Critical

setVault remains vulnerable to front-running hijack.

The past review flagged that any caller can permanently bind _vault to an arbitrary address before the legitimate vault calls setVault. This concern persists: if an attacker front-runs the vault creation/linking transaction, they can set _vault to their own contract, causing all future submitIntent() calls to target the attacker's address.

The recommended mitigation is to restrict setVault to an authorized flow (e.g., require the caller to be a registered vault in OrionConfig, or implement a propose/accept pattern).

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

In `@contracts/strategies/KBestApyStrategist.sol` around lines 82 - 87, The
setVault function currently allows any caller to permanently set _vault,
enabling front-running hijacks; modify setVault to restrict who can set/override
_vault by either (A) requiring the caller be an authorized vault from
OrionConfig (e.g., check OrionConfig.isRegisteredVault(msg.sender) or similar)
before assigning _vault, or (B) implement a two-step propose/accept flow for
linking a vault: add proposeVault(address candidate) that stores a pendingVault
and an acceptVault() callable only by that candidate (or the current legitimate
vault) to finalize _vault, and ensure submitIntent() continues to reference
_vault; update associated error/revert conditions
(ErrorsLib.StrategistVaultAlreadyLinked(), ZeroAddress) accordingly.
🤖 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/strategies/KBestTvlWeightedAverage.sol`:
- Around line 40-45: The setVault function in KBestTvlWeightedAverage.sol allows
any caller to bind _vault and can be front-run; restrict who can call or
implement a propose/accept flow. Either add an access control check (e.g.,
require(msg.sender == owner() or a vault registry check) so only an authorized
actor can call setVault, or replace setVault with a two-step pattern
(proposeVault(address) callable by the legitimate vault/owner and acceptVault()
callable by the proposed address) to avoid third-party hijack; update related
logic and events accordingly and mirror the same protection used in
KBestApyStrategist.setVault to keep strategist implementations consistent.
- Around line 48-62: Add ReentrancyGuard to KBestTvlWeightedAverage and mark
submitIntent as nonReentrant: import OpenZeppelin's ReentrancyGuard, have the
contract inherit from ReentrancyGuard, and add the nonReentrant modifier to the
submitIntent function (which performs the external call
IOrionTransparentVault(vault_).submitIntent). Ensure any existing inheritance
order is updated accordingly and recompile to confirm no constructor changes are
required.

In `@contracts/test/MockChainlinkFeed.sol`:
- Around line 28-38: The mock contract MockChainlinkFeed currently returns
block.timestamp for startedAt in latestRoundData and getRoundData, preventing
tests of the startedAt > block.timestamp path; add a uint256 storage variable
(e.g., _startedAt), add a public setter function setStartedAt(uint256 startedAt)
to set it, and change latestRoundData and getRoundData to return _startedAt
instead of block.timestamp so tests can control startedAt values.

In `@test/ChainlinkPriceAdapterUnit.test.ts`:
- Around line 77-93: The test passes minPrice=0 to adapter.configureFeed when
setting up the feed for asset; update the call to adapter.configureFeed in the
test "scaleFactor correct when base=18dec, quote=8dec → 10^8" to use a non-zero
minPrice (e.g., 1) instead of 0 so the test remains realistic and consistent
with other tests that enforce a minimum price; keep the same parameters
otherwise (asset, await base18.getAddress(), false, STALENESS, <minPrice>,
MAX_PRICE, await quoteFeed.getAddress()) and verify cfg.scaleFactor as before.

In `@test/orchestrator/Orchestrators.test.ts`:
- Around line 21-22: Re-add a lightweight local full-cycle smoke test in
Orchestrators.test.ts that exercises performUpkeep end-to-end and asserts the
core orchestrator invariants: call the same performUpkeep flow used previously,
then verify LiquidityOrchestrator returns to Idle, buffer/accounting balances
are consistent, and epoch-based liquidity/deposit effects occurred; locate the
test harness that calls performUpkeep and the LiquidityOrchestrator instance in
the file and reintroduce a short test (e.g., "full cycle smoke test") that
performs the upkeep, advances the epoch, and asserts those invariants so the
repo retains direct regression coverage for orchestrator/vault flows.

---

Outside diff comments:
In `@contracts/strategies/KBestTvlWeightedAverage.sol`:
- Around line 88-110: In _selectTopKAssets, replace the current defaulting
behavior by using the sentinel pattern like KBestApyStrategist: initialize
maxIndex as type(uint16).max inside the outer loop, update maxIndex when a new
maxTVL is found, and after the inner loop ensure maxIndex != type(uint16).max
before marking used and writing tokens/topTvls (or break if sentinel remains);
this makes selection consistent with KBestApyStrategist and avoids implicit
defaulting to index 0.

In `@test/PassiveStrategist.test.ts`:
- Around line 300-303: The test calls submitIntent(transparentVault) but the
IOrionStrategist API changed to a parameterless submitIntent(); update the test
to call submitIntent() with no arguments (remove transparentVault), and ensure
any related expectations still reference
passiveStrategist.connect(strategist).submitIntent(); confirm the test uses the
correct signer/context (connect(strategist)) and that no other tests pass
parameters to submitIntent anywhere else.

---

Duplicate comments:
In `@contracts/strategies/KBestApyStrategist.sol`:
- Around line 82-87: The setVault function currently allows any caller to
permanently set _vault, enabling front-running hijacks; modify setVault to
restrict who can set/override _vault by either (A) requiring the caller be an
authorized vault from OrionConfig (e.g., check
OrionConfig.isRegisteredVault(msg.sender) or similar) before assigning _vault,
or (B) implement a two-step propose/accept flow for linking a vault: add
proposeVault(address candidate) that stores a pendingVault and an acceptVault()
callable only by that candidate (or the current legitimate vault) to finalize
_vault, and ensure submitIntent() continues to reference _vault; update
associated error/revert conditions (ErrorsLib.StrategistVaultAlreadyLinked(),
ZeroAddress) accordingly.

In `@test/NewStrategies.test.ts`:
- Around line 34-53: The createVault helper currently uses non-null assertions
on event and parsed which can produce unclear runtime errors; modify createVault
to explicitly check that the receipt logs contain the "OrionVaultCreated" event
(after calling factory.interface.parseLog) and throw a clear, descriptive Error
if event is undefined or parsing fails (include strategistAddr or tx hash for
context), and also validate parsed.args[0] exists before calling
ethers.getContractAt("OrionTransparentVault", ...); update the code paths that
reference event and parsed to use these guards instead of event! and parsed!.

In `@test/StrategistLinking.test.ts`:
- Around line 24-43: The createVault helper uses non-null assertions for
receipt, event and parsed which can crash tests; add explicit guards and clear
errors: after awaiting tx.wait() verify receipt is defined, ensure receipt.logs
contains a log that parses to an "OrionVaultCreated" event (handle parse
failures with try/catch), assert the found event and parsed object are not
undefined and that parsed.args[0] exists before calling
factory.interface.parseLog(event) and ethers.getContractAt; if any check fails,
throw a descriptive error so failures are deterministic and easy to debug
(update symbols: createVault, receipt, event, parsed,
factory.interface.parseLog, parsed.args[0]).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f339606-e4ca-4deb-a46c-548f8a127fec

📥 Commits

Reviewing files that changed from the base of the PR and between 683b1c8 and f856197.

📒 Files selected for processing (45)
  • .prettierignore
  • README.md
  • contracts/LiquidityOrchestrator.sol
  • contracts/interfaces/ILiquidityOrchestrator.sol
  • contracts/interfaces/IOrionStrategist.sol
  • contracts/interfaces/IOrionVault.sol
  • contracts/libraries/ErrorsLib.sol
  • contracts/price/ChainlinkPriceAdapter.sol
  • contracts/strategies/KBestApyStrategist.sol
  • contracts/strategies/KBestTvlWeightedAverage.sol
  • contracts/test/MockChainlinkFeed.sol
  • contracts/vaults/OrionTransparentVault.sol
  • contracts/vaults/OrionVault.sol
  • package.json
  • test/ChainlinkPriceAdapterUnit.test.ts
  • test/NewStrategies.test.ts
  • test/PassiveStrategist.test.ts
  • test/RedeemBeforeDepositOrder.test.ts
  • test/Removal.test.ts
  • test/StrategistLinking.test.ts
  • test/VerifyPerformDataRejection.test.ts
  • test/crossAsset/ChainlinkPriceAdapter.test.ts
  • test/crossAsset/ERC4626ExecutionAdapter.test.ts
  • test/crossAsset/ERC4626PriceAdapter.test.ts
  • test/fixtures/Orchestrator1.json
  • test/fixtures/Orchestrator2.json
  • test/fixtures/Orchestrator3.json
  • test/fixtures/Orchestrator4.json
  • test/fixtures/Orchestrator5.json
  • test/fixtures/Orchestrator6.json
  • test/fixtures/RedeemBeforeDepositOrder1.json
  • test/fixtures/RedeemBeforeDepositOrder2.json
  • test/fixtures/RedeemBeforeDepositOrder3.json
  • test/fixtures/RedeemBeforeDepositOrder4.json
  • test/fixtures/RedeemBeforeDepositOrder5.json
  • test/fixtures/Removal1.json
  • test/fixtures/Removal2.json
  • test/fixtures/Removal3.json
  • test/fixtures/Removal4.json
  • test/fixtures/Removal5.json
  • test/fixtures/Removal6.json
  • test/fixtures/Removal7.json
  • test/fixtures/Removal8.json
  • test/helpers/orchestratorHelpers.ts
  • test/orchestrator/Orchestrators.test.ts
💤 Files with no reviewable changes (24)
  • .prettierignore
  • test/fixtures/Removal2.json
  • test/fixtures/RedeemBeforeDepositOrder5.json
  • test/fixtures/Orchestrator2.json
  • test/fixtures/Removal7.json
  • test/fixtures/Removal1.json
  • test/fixtures/Removal8.json
  • test/fixtures/Orchestrator1.json
  • test/fixtures/Removal6.json
  • test/fixtures/Removal4.json
  • test/fixtures/RedeemBeforeDepositOrder3.json
  • test/fixtures/Removal3.json
  • test/fixtures/Orchestrator3.json
  • test/fixtures/Removal5.json
  • test/fixtures/Orchestrator5.json
  • test/fixtures/RedeemBeforeDepositOrder1.json
  • test/fixtures/RedeemBeforeDepositOrder2.json
  • test/fixtures/Orchestrator6.json
  • test/fixtures/Orchestrator4.json
  • test/Removal.test.ts
  • test/RedeemBeforeDepositOrder.test.ts
  • test/helpers/orchestratorHelpers.ts
  • test/VerifyPerformDataRejection.test.ts
  • test/fixtures/RedeemBeforeDepositOrder4.json

Comment thread contracts/strategies/KBestTvlWeightedAverage.sol
Comment thread contracts/strategies/KBestTvlWeightedAverage.sol
Comment thread contracts/test/MockChainlinkFeed.sol
Comment thread contracts/vaults/OrionVault.sol
Comment thread test/ChainlinkPriceAdapterUnit.test.ts
Comment thread test/orchestrator/Orchestrators.test.ts
@matteoettam09
matteoettam09 merged commit 4a40d0a into main Mar 21, 2026
4 checks passed
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