Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · February 2025

Cross-Chain Yield Vault

for Orderly

Orderly engaged Guardian to review the security of their cross-chain, share-based yield aggregator smart contracts. From the 2nd of January to the 20th of January, a team of 6 auditors reviewed the source code in scope.

Published
Review window
January 2 to 20, 2025
Language
Solidity
Chains
Ethereum, Arbitrum, Optimism, Base, Solana
Sector
Perpetuals
  • 7 Critical
  • 6 High
  • 16 Medium
  • 35 Low
  • 0 Informational

32 resolved · 1 partially resolved · 31 acknowledged

Scope

Overview

Orderly engaged Guardian to review the security of their cross-chain, share-based yield aggregator smart contracts. From the 2nd of January to the 20th of January, a team of 6 auditors reviewed the source code in scope.

Issues Detected Throughout the engagement 13 High/Critical issues were uncovered and promptly remediated by the Orderly team.

Security Recommendation Given the number of High and Critical issues detected as well as additional code changes made after the main review, Guardian recommends that an independent security review of the protocol at a finalized frozen commit is conducted before deployment.

Findings 64

  1. C-01 Critical Missing Access Control In sendMessage Function Configuration Resolved
    Location
    VaultCrossChainManager.sol: 47

    Description

    The VaultCrossChainManager contract implements the sendMessage which is used to send cross-chain messages.

    This function does not implement any type of access control and, therefore, any malicious user can craft arbitrary cross-chain payloads and transmit them causing the receiving chain’s contract logic to execute unverified operations.

    This could be easily exploited to perform unauthorized withdrawals or deposits.

    Recommendation

    Introduce strict access control to sendMessage so that only trusted contracts, such as whitelisted vaults, can invoke cross-chain operations.

    Resolution

    Orderly Team: The issue was resolved in commit 8540bc3.

  2. C-02 Critical Some ProtocolVault Functions Do Not Validate Properly The PayloadType Validation Resolved
    Location
    ProtocolVault.sol: 130, 157

    Description

    In the ProtocolVault contract, both the deposit and withdraw functions accept a PayloadType that is never validated against the function’s intended usage. As a result, a user could call the withdraw function but submit a payload indicating LP_DEPOSIT or SP_DEPOSIT.

    On the ledger side, this is interpreted as a deposit even though no tokens were transferred to the ProtocolVault, artificially increasing the user’s balance. This leads to unbacked shares on the ledger and allows users to perform “free” deposits.

    Recommendation

    Add strict checks in each function to require that deposit only accepts deposit payloads (LP_DEPOSIT or SP_DEPOSIT) and withdraw only accepts withdrawal payloads (LP_WITHDRAW or SP_WITHDRAW). Any other PayloadType should always revert.

    Resolution

    Orderly Team: The issue was resolved in commit 3275584.

  3. C-03 Critical handleOpFromVault Function Does Not Scale Decimals Validation Resolved
    Location
    ProtocolVaultLedger.sol: 132

    Description

    In the handleOpFromVault function, the ProtocolVaultLedger contract treats operationData.amount as a direct integer without adjusting for the underlying token’s decimals.

    Assets and shares are supposed to be stored with 6 decimals precision. As per the code comments, this accountToken will be USDC and USDC has 6 decimals in most of the chains. However USDC has 18 decimals instead of 6 on the following chains:

    • Oasys
    • BNB
    • OKX Chain
    • Sora
    • Kucoin Chain
    • Telos
    • Conflux
    • Bitgert

    If accountToken has a different number of decimals than 6 the ProtocolVaultLedger contract calculations end up over-counting or under-counting actual token amounts which would totally break the accounting in the contract.

    Recommendation

    Normalize operationData.amount according to the token’s decimals before updating ProtocolVaultLedger ’s state. A robust approach is to store each token’s decimal information on-chain and adjust incoming amounts consistently.

    Resolution

    Orderly Team: The issue was resolved in commit 872f355.

  4. C-04 Critical State Variables Are Updated After A Failed Check Logical Error Resolved
    Location
    ProtocolVaultLedger.sol: 704

    Description

    In the ProtocolVaultLedger contract, the _checkWithdraw function merely emits an event and returns early if a user attempts to withdraw more shares than they hold, instead of reverting the entire transaction. Because control flow proceeds after this early return, the contract continues to update its state variables (e.g., incrementing frozenShares or proceeding with other post‐withdraw steps) even when the user’s withdrawal request is invalid.

    Recommendation

    In case that the _checkWithdraw function require check was not passed, ensure that the frozenShares state variable is not updated.

    Resolution

    Orderly Team: The issue was resolved in commit 3d698ab.

  5. C-05 Critical Initial DoS State Contract Multiple Divisions By Zero Logical Error Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    In the ProtocolVaultLedger contract, when a strategy fund has not yet minted any shares, strategyFundTokenInfo[strategyProviderId][USDC_HASH].totalShares remains zero, leading to a guaranteed division by zero during future certain end-of-period operations.

    In settleMainAndStrategyFunds, the code invokes _calculateHWM to update the High Water Mark, which executes the line hwm = strategyFundToken.fundAssetsAfterFee * 10 * priceDecimal / totalShares reverting because totalShares is zero.

    A similar issue appears in updateStrategyFundAssets if fundShares (i.e., strategyFundToken.totalShares) is zero, again causing a division-by-zero revert.

    As a result, the very first call to these functions in the ProtocolVaultLedger contract will always fail, blocking any attempt to settle or update strategy fund assets when no shares are yet in circulation.

    This breaks the normal lifecycle flow for the initial period, preventing operators from correctly finalizing and advancing the ledger state.

    Recommendation

    Introduce specialized handling for the zero‐shares scenario in settleMainAndStrategyFunds and updateStrategyFundAssets. Whenever totalShares = 0, skip or defer the HWM calculation and other division‐based logic until at least one share exists.

    Resolution

    Orderly Team: The issue was resolved in commit 4d1464d.

  6. C-06 Critical rebalanceMint May Corrupt Ledger State Logical Error Resolved
    Location
    Global

    Description

    The ledger can perform rebalanceBurn and rebalanceMint of tokens. This effectively burns the tokens on one chain and mints them on another one by using Circle's tokenManager.

    The flow is as follows:

    1. Ledger.executeRebalanceBurn(). This will deduct the burnt amount from the chain's balance in

    VaultManager and will add it to the frozenBalances in case the burn fails.

    1. A cross chain message is sent to Vault.rebalanceBurn().
    2. Vault.rebalanceBurn() calls tokenMessengerContract.depositForBurn()
    3. If the call fails, we send a failed rebalanceBurnFinish message to the ledger, to increase the chain's

    balance back from the frozen tokens.

    1. If the call succeeds, the tokens are burnt from the vault and, event is emitted and a successful

    rebalanceBurnFinish message is sent to reduce the frozen tokens.

    1. Once enough attestations confirm the message, it can be executed on the destination chain by calling

    messageTransmitterContract.receiveMessage().

    1. If the receive is successful, the tokens are minted and a successful rebalanceMintFinish is sent to the

    Ledger to increase the balance of the destination chain.

    1. Otherwise, if the receive fails, a failed rebalanceMintFinish will be sent to the Ledger.

    The problem with this flow is that messageTransmitterContract.receiveMessage() is permissionless. If anyone calls it before the Vault, the message's nonce will be consumed and even though the mint is successful, the Vault will treat it as failed.

    In result, the tokens will be deducted from the source chain, but won't be credited to the destination chain, leading to loss of funds.

    Recommendation

    If the nonce is already used, messageTransmitterContract.receiveMessage() will revert with Nonce already used. You can catch that and send a successful rebalanceMintFinish to update the state correctly.

    Resolution

    Orderly Team: The issue was resolved in the merge request 332.

  7. C-07 Critical Compilation Error Due To Naming Mismatch Code Best Practices Resolved
    Location
    VaultCrossChainManagerUpgradeable.sol: 153

    Description

    EventTypes.Withdraw2Contract struct in the orderly-contract-evm repo has a uint256 clientId parameter, which is used instead of periodId.

    However, the orderly-evm-cross-chain repo attempts to read periodId from this struct in the VaultCrossChainManagerUpgradeable.receiveMessage function, causing a TypeError compilation error.

    Recommendation

    Update the orderly-evm-cross-chain repo to reflect the changes in the orderly-contract-evm repo.

    Resolution

    Orderly Team: The issue was resolved in commit 7d71522.

  8. H-01 High withdraw2Contract Crosschain Flow Pays Withdrawal Fee Twice Configuration Resolved
    Location
    Global

    Description

    In the withdraw2Contract crosschain flow, the Ledger credits the fee collector’s account with the withdrawal fee in executeWithdraw2Contract, then credits the same fee again in accountWithDrawFinish.

    As a result, a single user withdrawal leads to a double fee charge in the Ledger’s accounting, once when the funds are initially frozen and again when the ledger finalizes the withdrawal. This inflates the fee collector’s balance with inexistent funds.

    Recommendation

    Remove one of the two fee credits so that the fee is only applied once. For example, either credit the fee collector immediately on executeWithdraw2Contract and avoid doing so in accountWithDrawFinish, or defer the fee credit until final settlement.

    Resolution

    Orderly Team: The issue was resolved in the merge request 327.

  9. H-02 High depositToStrategy Function Will Always Revert Logical Error Resolved
    Location
    ProtocolVault.sol: 237

    Description

    The depositToStrategy function sends a deposit request to the dexVault via IDexVault(dexVault).depositTo(...), transferring USDC (or another token) in the process.

    However, there is no approval step for the vault to pull tokens from this contract. Without an ERC-20 approve call, the dexVault has no permission to transfer tokens on behalf of the ProtocolVault. As a result, the deposit call will always revert.

    Recommendation

    Approve the dexVault before calling the depositTo function.

    Resolution

    Orderly Team: The issue was resolved in commit f6381c3.

  10. H-03 High Newly Added Strategy Provider Will Not Receive LP Deposits Logical Error Acknowledged
    Location
    ProtocolVaultLedger.sol: 335

    Description

    Under the current proportional deposit logic in allocatToFunds, the contract allocates newly deposited LP assets among strategies based solely on the main vault’s existing shares (i.e., mainShares).

    When a new strategy provider is introduced at a later period (for example, in the period 5), it starts with zero main shares. Consequently, the formula:

    portionForThisStrategy = pendingLpDepositAssets * (mainAssetsInFund / totalMainAssetsInFund)
    

    will yield zero for that strategy. The new strategy never accumulates any main vault capital automatically, as it isn’t part of the existing distribution ratio (which depends on mainShares).

    Even if the new strategy invests its own capital (SP deposit), that action mints strategy provider shares, not main shares, thus it does not affect the main vault ratio or future LP deposit splits. As a result, this new strategy remains perpetually excluded from LP inflows.

    Recommendation

    Incorporate a method for the operator to allocate a desired seed amount of main vault capital to a newly added strategy (e.g., “rebalance from existing strategies” to ensure the newcomer starts with a non-zero main share).

    Resolution

    Orderly Team: Acknowledged.

  11. H-04 High Wrong Cross-chain Manager Usage Configuration Resolved
    Location
    LedgerImplC.sol

    Description

    LedgerImplC holds the logic for Solana withdrawals and withdraw2Contract withdrawals. It correctly uses crossChainManagerV2Address inside executeWithdrawSolAction() to execute a Solana withdrawal.

    However, it uses the same crossChainManagerV2Address inside executeWithdraw2Contract() while the withdraw2Contract() function is implemented in crossChainManagerAddress. In result, withdraw2Contract() will always fail.

    Recommendation

    Replace crossChainManagerV2Address with crossChainManagerAddress inside executeWithdraw2Contract().

    Resolution

    Orderly Team: The issue was resolved in the merge request 328.

  12. H-05 High DOS Of updateLPAndStrategyFund Logical Error Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    LP_WITHDRAW and SP_WITHDRAW inside ProtocolVaultLedger.handleOpFromVault() should only be allowed if the user has enough withdrawable shares.

    This is handled by _checkWithdraw() - it ensures the amount to be withdrawn added to the current frozen amount doesn't surpass the share balance of the account.

    After that, the amount to be withdrawn is added towards frozenShares so checkWithdrawal will continue to work properly for further withdrawal requests.

    The withdrawal request will be handled by _handleLpWithdraw() and the amount to be withdrawn will be subtracted by the user's pendingShares and frozenShares. After some time, settleAccounts() will update the user's actual shares by setting them to pendingShares.

    This creates a window between _handleLpWithdraw() and settleAccounts() where frozenShares is decreased, but account.shares is not updated.

    Any withdrawal request in that window will successfully performed if it doesn't exceed the user's shares because _checkWithdraw() won't stop it. This will result in an increase in frozenShares, potentially doubling the current value.

    The withdrawal request inside this window will be processed inside _handleLpWithdraw() for the next period.

    The amount to be withdrawn will be subtracted from pendingShares again, however this time pendingShares will not cover it causing a revert and DOS of the updateLPAndStrategyFund function.

    Recommendation

    One possible solution may be to disallow withdrawal requests in that window.

    Resolution

    Orderly Team: The issue was resolved in commit 2ff9be9.

  13. H-06 High Withdraw Message Sent Even If _validReceiver Check Fails Validation Acknowledged
    Location
    Vault.sol

    Description

    In the Vault.withdraw function IVaultCrossChainManager(crossChainManagerAddress).withdraw(vaultWithdrawData) is called before verifying that the receiver is valid.

    Consequently, if _validReceiver(data.receiver, address(tokenAddress)) returns false, the vault still sends a cross‐chain message to the ledger acknowledging a “successful” withdrawal.

    Meanwhile, the local code emits only a WithdrawFailed event and never transfers the tokens to the ProtocolVault. This leaves the ledger believing the user’s withdrawal went through, while in reality no tokens were actually delivered.

    As a result, the user’s ledger state and on-chain vault state become out of sync. The ledger sees a final “withdraw finish,” but the vault never transferred tokens if the receiver check fails. This scenario could strand the user’s funds or require an off-chain correction.

    Recommendation

    If the vault does not transfer tokens because _validReceiver(data.receiver, address(tokenAddress)) returned false, consider sending a “withdraw failure” message back to the ledger or omit sending a “successful” cross‐chain call.

    This ensures the ledger and vault remain consistent, reflecting that the withdrawal did not finalize.

    Resolution

    Orderly Team: Acknowledged.

  14. M-01 Medium SP Deposits Are Not Refunded Configuration Resolved
    Location
    ProtocolVaultLedger.sol: 141

    Description

    In the handleOpFromVault function of ProtocolVaultLedger, whenever a deposit operation targets a strategy provider (spId) that is not marked as allowed (isAllowedStrategyProvider[spId] = false), the contract simply emits an event and returns.

    This silent return means that the deposited tokens, already locked on the ProtocolVault side are not refunded to the strategy depositor. As a result, funds end up stuck, creating a loss scenario for the depositor.

    Recommendation

    Consider incorporating a refund logic for the deposited assets to cover this edge case.

    Resolution

    Orderly Team: The issue was resolved in commit 63d9a53.

  15. M-02 Medium Missing Payable Modifier Function Configuration Resolved
    Location
    ProtocolVault.sol: 237, VaultCrossChainManager.sol: 86

    Description

    In the depositToStrategy function, the contract attempts to call IDexVault(dexVault).depositTo{value: fee}(...) but the function itself is not declared as payable. As a result, it cannot receive native assets in the current transaction, causing a revert if no native assets are already in the contract’s balance.

    This breaks the expected flow of paying a fee at runtime, preventing the contract from funding the DexVault deposit call.

    Furthermore, in the VaultCrossChainManager contract’s _lzReceive function, the ASSETS_DISTRIBUTION case calls IProtocolVault(vault).depositToStrategy(...) but does not supply msg.value for the fee.

    Recommendation

    Add the payable modifier to depositToStrategy so that it can accept native assets in the same transaction.

    At the same time, ensure that the cross-chain message in VaultCrossChainManager includes the fee in msg.value when relaying ASSETS_DISTRIBUTION, so the contract handles and transfers the deposit fee to the DexVault.

    Resolution

    Orderly Team: The issue was resolved in commit e311386.

  16. M-03 Medium Operators Could Replay Signatures In The Contract Configuration Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    The ProtocolVaultLedger contract relies on its backend to provide signatures for state‐changing functions, such as updateLPAndStrategyFund, allocatToFunds and similar functions, but it does not seem to enforce strict replay protection.

    Once a valid signature has been used, an operator can potentially reuse that same signature multiple times (even in different contexts) to reapply changes or to trigger previously authorized operations again within the same period.

    This opens the door for double handling of deposit/withdraw operations or other malicious state transitions. On the other hand, the vaultId is derived from the vault address and broker hash.

    All protocol vaults are deployed to the same address using CREATE3 and the brokerHash is a hardcoded value. As a result, the vaultIds of all protocol vaults across every EVM chain are identical and their accounting is tracked as a single account on the ledger chain.

    None of the signature verifications include the chain ID. Since vaultIds are identical across all chains, a valid signature on one chain will also be valid on another chain for the same periodId.

    Recommendation

    Implement a nonce or sequential counter mechanism for each signature payload, incrementing a contract‐stored counter after each valid call. Moreover, consider adding chainIds to engine signatures.

    Resolution

    Orderly Team: The issue was resolved in commit 3275584.

  17. M-04 Medium No Enforcement Of Required Bridging Fee In sendMessage Validation Resolved
    Location
    VaultCrossChainManager.sol: 47

    Description

    The sendMessage function in VaultCrossChainManager calculates a MessagingFee via _quote but never checks whether the user-supplied msg.value actually matches the required bridging fee

    Because there is no comparison between messageFee.nativeFee and msg.value, the function can be underfunded without reverting.

    Recommendation

    Enforce that msg.value is at least equal to the calculated bridging fee. For example, add a check like require(msg.value = messageFee.nativeFee, "Insufficient bridging fee"); to ensure that the user has paid for the fee.

    Resolution

    Orderly Team: The issue was resolved in commit b13ebd6.

  18. M-05 Medium Strategy Deposit Might Fail Due To Limit Logical Error Acknowledged
    Location
    Vault.sol: 205-210

    Description

    Asset distributions to strategies are initiated by the operators on the ledger chain. The message is then transferred to the vault chain, where the depositToStrategy function triggers asset movements from the protocolVault to the dexVault.

    There is a time lag between an operator initiating the process on the ledger chain and the actual execution of the transfer on the vault chain.

    Even if the operator initiates the process with valid distribution amounts, the Vault.depositTo function might revert with a DepositExceedLimit error due to ongoing deposits during this time lag.

    For example:

    • dexVault deposit limit: 1000
    • Current balance of the dexVault: 850
    • Operator calls asset distribution with 100 on the ledger chain (valid amount at the time of

    initiation).

    • Regular users deposit 60 more until this message reaches to vault chain.
    • New balance of the dexVault: 910
    • The distribution transaction fails with DepositExceedLimit.
    • There is still 90 left in the deposit limit that is not filled.

    Since asset distributions can only be called once per period, the operator cannot attempt to distribute assets with a lower value. As a result, assets in the protocolVault remain unused.

    Recommendation

    Consider implementing a separate deposit function in the dexVault for strategy deposits. This function should not revert if the full amount cannot be deposited; instead, it should deposit the available amount up to the limit.

    Resolution

    Orderly Team: Acknowledged.

  19. M-06 Medium FeeRate Changes Lead To Loss Of Yield Logical Error Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    The setFeeRate function allows changing performance fee rates during an active period, which can result in users being charged different rates than what they initially agreed to. When users deposit funds, they implicitly agree to the current fee structure.

    However, if the fee rate is modified mid-period via setFeeRate, users will be charged the new rate when performance fees are calculated in updateStrategyFundAssets, even though this wasn't the rate in effect when they deposited.

    For example: 1. User deposits when fee rate is 20% 2. Mid-period, owner calls setFeeRate to change rate to 30% 3. At period end, performance fees are calculated using 30% rate 4. User pays higher fees than they agreed to when depositing

    Recommendation

    Only allow fee rate changes to take effect in future periods. Or restrict fee rate changes to only occur after the current period's performance fees have been calculated.

    Resolution

    Orderly Team: The issue was resolved in commit 26e43f9.

  20. M-07 Medium Native Funds Locked In Contract Logical Error Resolved
    Location
    OAppSenderUpgradeable.sol

    Description

    In the _lzSend function, the _refundAddress parameter is set to the contract's address, but the contract lacks functionality to withdraw or rescue these refunded ETH funds. When LayerZero returns excess fees to the contract address, they will be not be retrievable

    Recommendation

    Consider implementing a pull method for users to receive their refund. Or add a rescue function so that admin can recover the locked ETH.

    Resolution

    Orderly Team: The issue was resolved in commit b13ebd6.

  21. M-08 Medium Event Emitted With Incorrect Values Validation Resolved
    Location
    ProtocolVaultLedger.sol: 450

    Description

    The MainAndStrategyFundsSettled event’s first parameter should represent the mainAssets amount. However, it is currently emitted with the periodId. Since the protocol’s backend heavily relies on event emissions, this issue may cause incorrect operations on the backend.

    Recommendation

    Update the event.

    Resolution

    Orderly Team: The issue was resolved in commit d4b54b2.

  22. M-09 Medium A Portion Of Frozen Fees Will Remain Frozen Logical Error Acknowledged
    Location
    LedgerImplC.sol

    Description

    During the withdraw2contract flow the amount of funds and the fee will be frozen. Then at the end of the flow these amounts are intended to be unfrozen. However, there will be small difference between the amount frozen and unfrozen.

    This will happen when convertDecimal is called and the destination chain decimals are less than the sending chain. In this case there will be some precision loss and while X amount of funds are frozen at the beginning only X - Y will be unfrozen. Where Y equals the precision loss.

    Recommendation

    Adjust fee amount and withdraw amount so that there is no precision loss prior to freezing the funds. This can be done by truncating the amount to the destination decimals and then expanding it back to the senders decimals. This will ensure that the amount frozen and unfrozen are the same.

    Resolution

    Orderly Team: Acknowledged.

  23. M-10 Medium Period Update Breaks When Batching Logical Error Acknowledged
    Location
    ProtocolVaultLedger.sol 163

    Description

    When updateStrategyFundAssets is called it will iterate through all funds. However given that there is no hard cap on the number of funds the protocol can have and that fund creation will become permissionless it will eventually require multiple iterations to update all the funds.

    The issue with this is that mainAssetsAfterFees will become much less than its actual value on the second call since it will not take into account mainAssetsAfterFees from the first call. The reduced mainAssetsAfterFees will drastically reduce users share value.

    Recommendation

    Modify the updateStrategyFundAssets function so that it can be called multiple times without losing data from previous updateStrategyFundAssets calls.

    Resolution

    Orderly Team: Acknowledged.

  24. M-11 Medium Rounding Up Will DoS When Funds Are Withdrawn Logical Error Acknowledged
    Location
    ProtocolVaultLedger.sol 415

    Description

    When calculating distributeWithdrawAssets the value is rounded up. Because of this the amount of assets being distributed can be larger than the actual amount of funds.

    In some cases distributeWithdrawShares rounding down will offset this and there won't be excess funds distributed.

    But in situations where distributeWithdrawAssets does have a remainder causing the value to round up and distributeWithdrawShares does not have remainders resulting in no amount being rounded down.

    More assets will be distributed then intended. During times where all funds are withdrawn transferring an amount that is greater than what is available will lead to a failed transaction.

    Recommendation

    Consider rounding down when calculating distributeWithdrawAssets.

    Resolution

    Orderly Team: Acknowledged.

  25. M-12 Medium Enabling Tokens Breaks Protocol Configuration Acknowledged
    Location
    ProtocolVault.sol

    Description

    ProtocolVault.setAllowedToken() lets the owner of the contract enable or disable a new token for deposits. When users perform deposits with this token, they will pay an amount of that token.

    However, the whole system currently is setup to work with USDC. For example, _getOperationData hardcodes the tokenHash to USDC_HASH. If another token were to be enabled, users will be charged that token, but their balance of the USDC token will be increased on the Ledger side instead.

    Recommendation

    If you should support multiple tokens, consider not hardcoding the token hashes. Be careful with this approach because some tokens may have different decimals across chains.

    Resolution

    Orderly Team: Acknowledged.

  26. M-13 Medium Token Losses On Deposit Validation Acknowledged
    Location
    Vault.sol

    Description

    Users can use Vault._deposit() to deposit tokens from the vault side to the ledger side. They will be charged an amount of these tokens on the vault side.

    Since this token may have different decimal precision on each chain, the amount added towards the user balance on the ledger side is adjusted by convertDecimal

    In case srcDecimals > dstDecimals, the amount will be divided to convert it to dstDecimals and the rest will be lost. This adjusted amount will also be recorded in the vaultManager for the given srcChainId. In result, users will lose part of their tokens.

    Recommendation

    Consider adding a convertDecimal function to the Vault as well and charging the user the newly adjusted amount.

    Resolution

    Orderly Team: Acknowledged.

  27. M-14 Medium Vault LZ Fee Can Be Lost Validation Resolved
    Location
    ProtocolVault.sol

    Description

    When ProtocolVault.depositToStrategy() is called, the dexVault.getDepositFee() will be forwarded to dexVault. depositTo(). The vault will then use that value to pay for LZ fees.

    However, the vault has a depositFeeEnabled boolean. It will use the msg.value send to depositTo() to pay for the fees only if this flag is set to true. Otherwise, the sent native token will not be used and remain stuck in the contract.

    Recommendation

    Forward the fee from ProtocolVault to Vault only if depositFeeEnabled = true.

    IMPORTANT: If you implement this fix, any value provided by the executor to pay the fees will now be stuck in ProtocolVault. You should come up with a solution for these funds. You can:

    • transfer the fee to the vault if depositFeeEnabled = true
    • otherwise transfer it to the VaultCrossChainManagerUpgradeable

    Resolution

    Orderly Team: The issue was resolved in commit f7648f0.

  28. M-15 Medium Insufficient msgOptions Configuration Acknowledged
    Location
    VaultCrossChainManager.sol

    Description

    VaultCrossChainManager.sendMessage() will use the msgOptions mapping to determine what gas and value the executor should use for executing lzReceive on the destination chain. The values used is chosen based on the message's payloadType.

    However, they are the same for each chain. Some chains may require different parameters. While it may be fine for most EVM chains, sending messages to Solana is different.

    Instead of gas_limit and msg.value, the values used for Solana will be compute_units and lamports which is quite different.

    Reference: https://docs.layerzero.network/v2/developers/solana/gas-settings/options

    Recommendation

    Consider having different msgOption values for different chains (or at least Solana).

    Resolution

    Orderly Team: Acknowledged.

  29. M-16 Medium Insufficient Validation In withdraw2Contract Validation Partially resolved
    Location
    LedgerImplC.sol

    Description

    LedgerImplC.withdraw2Contract() doesn't implement the withdrawal validation implemented in LedgerImplA.executeWithdrawAction(). This poses a significant risk because of the withdrawNonce. Since its not validated, a lower nonce than the current last value may be used.

    This will then lead to overriding the last withdrawal nonce with the new value (which is way lower) and will enable past withdrawals to be executed again. The fee is also not validated which means it can exceed the maximum configured fee.

    Recommendation

    Consider implementing validation for the two things mentioned in the report.

    Resolution

    Orderly Team: The issue was resolved in the merge request 329.

  30. L-01 Low Wrong Check In _convertToShares Function Configuration Resolved
    Location
    ProtocolVaultLedger.sol: 832

    Description

    In the _convertToShares function, the condition currently checks whether (_toatlShares = 0) to decide if the vault is in a “first deposit” scenario.

    Recommendation

    Update the _convertToShares function to compare _totalAssets = 0 rather than _toatlShares = 0.

    Resolution

    Orderly Team: The issue was resolved in commit 5d09f50.

  31. L-02 Low quoteOperation Function Always Assumes a LP_DEPOSIT Payload Configuration Resolved
    Location
    ProtocolVault.sol: 326

    Description

    Within ProtocolVault contract, the quoteOperation function hardcodes LP_DEPOSIT as the PayloadType to calculate bridging fees.

    Recommendation

    Update the quoteOperation function to accept a payloadType parameter or determine it dynamically if needed, thereby ensuring the calculated bridging fee matches the actual operation type.

    Resolution

    Orderly Team: The issue was resolved in commit b61d5e4.

  32. L-03 Low ProtocolVault Claim Function Can Transfer Any Locked Token Validation Resolved
    Location
    ProtocolVault.sol: 206

    Description

    In the claim function, the user provides a token address in claimParams.token without any validation that it matches the asset they previously deposited.

    Because the contract simply performs SafeTransferLib.safeTransfer(ERC20(claimParams.token), msg.sender, amount), a malicious user can specify any token owned by the ProtocolVault, claiming funds that do not necessarily belong to them.

    Recommendation

    Restrict the token being claimed to the actual asset recorded for the user in the internal ledger (e.g., by storing the token in userClaimedById[id] and only transferring that one).

    Resolution

    Orderly Team: The issue was resolved in commit 8ef737e.

  33. L-04 Low Possible Inconsistent Decimal Precision Configuration Validation Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    The setDecimal function allows updating three separate decimal values—priceDecimal, shareDecimal, and assetsDecimal—independently. If these values are set to different scales, the accounting logic will be broken.

    Moreover, accountToken decimals should be always the same as priceDecimal. Finally, assetsDecimal state variable is not really used across the contract’s logic so it can simply be removed.

    Recommendation

    Enforce that _priceDecimal, _shareDecimal, and _assetsDecimal remain the same, or remove the function altogether if dynamic decimal reconfiguration is not a valid operational case.

    Ensure that accountToken decimals is equal to priceDecimal. Consider removing the assetsDecimal state variable.

    Resolution

    Orderly Team: The issue was resolved in commit 263831c.

  34. L-05 Low Redundant Period ID Parameter Code Best Practices Acknowledged
    Location
    ProtocolVaultLedger.sol

    Description

    The _check(uint256 periodId) function in the ProtocolVaultLedger contract compares periodId against latestPeriodId, but this extra parameter is superfluous.

    Since the contract already tracks the currently active period in latestPeriodId, requiring an extra parameter in many functions that is later on validated through the _check function introduces unnecessary complexity.

    Recommendation

    Remove the _check function and the redundant periodId parameter for all the functions. Instead, directly reference latestPeriodId wherever period alignment is needed.

    Resolution

    Orderly Team: Acknowledged.

  35. L-06 Low Proportional Allocation Overfunds Past Performers Configuration Acknowledged
    Location
    ProtocolVaultLedger.sol: 357

    Description

    In the ProtocolVaultLedger's current design, new LP deposits are allocated proportionally to each strategy’s existing “main vault” share, rewarding historically successful strategies with a continually larger share of new deposits.

    This works well if a strategy’s outperformance persists, but it can backfire when a once-top performer’s yield dwindles or fails.

    For example, if Strategy1 significantly outperforms Strategy2 over the first 20 periods, it ends up with a much larger share of the main vault’s capital.

    Consequently, even if Strategy1’s yield drops to near zero afterward, it continues to receive a high fraction of new LP deposits for many subsequent periods—because the contract only looks at the legacy ratio of main shares in each strategy.

    This can cause a suboptimal capital deployment where fresh user funds flow into a no-longer-productive strategy.

    Recommendation

    Consider implementing an operator function to realign capital if a strategy’s yield clearly stagnates, preventing capital from staying locked in a once-top performer.

    Resolution

    Orderly Team: Acknowledged.

  36. L-07 Low Minor Unallocated Remainders In Proportional LP Allocation Precision Loss Acknowledged
    Location
    ProtocolVaultLedger.sol: 357

    Description

    When splitting pendingLpDepositAssets among multiple strategies using mulDiv(..., Math.Rounding.Floor), each proportional slice may be truncated downward.

    Summing these truncated allocations for all strategies often leaves a small leftover in pendingLpDepositAssets that never gets allocated.

    Over many periods or multiple strategies, these tiny unallocated remainders can accumulate, causing a minimal mismatch between the total deposit intended and the amounts actually distributed.

    Therefore a very small amount of user-deposited capital remains undistributed. This issue also applies to withdrawals(pendingLpWithdrawShares).

    Recommendation

    After allocating to all but one strategy, assign the final strategy whatever remains of pendingLpDepositAssets to ensure there is no remaining dust.

    Resolution

    Orderly Team: Acknowledged.

  37. L-08 Low Debugging Checks And Test Code Left In Production Code Code Best Practices Acknowledged
    Location
    Global

    Description

    In multiple contracts, there are code snippets which are presumably for debugging or testing. Such debug checks should not remain in the live, production version of the contract.

    Recommendation

    Remove all these code snippets. If they are valuable for testing, maintain them in a separate test‐only version of the contracts.

    Resolution

    Orderly Team: Acknowledged.

  38. L-09 Low Lack Of A Double Step TransferOwnership Pattern Code Best Practices Resolved
    Location
    Global

    Description

    The current ownership transfer process for all the contracts inheriting from the Ownable or OwnableUpgradeable contracts involves the current owner calling the transferOwnership function.

    If the nominated EOA account is not a valid account, it is entirely possible that the owner may accidentally transfer ownership to an uncontrolled account, losing the access to all functions with the onlyOwner modifier.

    Recommendation

    It is recommended to implement a two-step process transfer ownership process where the owner nominates an account and the nominated account needs to call an acceptOwnership function for the transfer of the ownership to fully succeed.

    This ensures the nominated EOA account is a valid and active account. This can be easily achieved by using OpenZeppelin’s Ownable2Step contract.

    Resolution

    Orderly Team: Resolved.

  39. L-10 Low CCTP depositForBurn Has Maximum Burn Per Transaction Validation Resolved
    Location
    Vault.sol: 387

    Description

    In the rebalanceBurn flow, the contract relies on Circle’s CCTP method depositForBurn for transferring tokens from one chain to another.

    However, CCTP enforces a per‐transaction burn limit (a maximum USDC amount that can be burned at once) to mitigate risk and manage capacity on the Circle side.

    If the protocol attempts to deposit and burn an amount exceeding that limit, the call to ITokenMessenger(tokenMessengerContract).depositForBurn() will revert, preventing the rebalancing from succeeding.

    Recommendation

    Make clear in the protocol’s user interface that a single rebalancing transaction is constrained. Operators should plan rebalancing flows accordingly.

    Resolution

    Orderly Team: Resolved.

  40. L-11 Low Floating Pragma Code Best Practices Acknowledged
    Location
    Global

    Description

    Contracts should be deployed with the same compiler version and flags used during development and testing. Locking the pragma helps to ensure that contracts do not accidentally get deployed using another pragma.

    For example, an outdated pragma version might introduce bugs that affect the protocol negatively. All the contracts in scope are using the following floating pragma: pragma solidity ^0.8.18;

    Recommendation

    Consider locking the pragma version in all the smart contracts. It is not recommended to use a floating pragma in production. For example: pragma solidity 0.8.28.

    Resolution

    Orderly Team: Acknowledged.

  41. L-12 Low Incompatibility With CREATE2 On ZkSync Configuration Acknowledged
    Location
    VaultFactory.sol

    Description

    The VaultFactory contract relies on CREATE2 deterministic deployments (or CREATE3 via solady library) to produce predictable addresses.

    However, zkSync has its own nuances for CREATE2 instruction usage, documented at zkSync’s “Differences in EVM instructions”. Therefore, this version of VaultFactory should not be used in ZkSync.

    Recommendation

    If planning to deploy in ZkSync consult the official zkSync docs to adapt or replace the current VaultFactory logic with a mechanism that is officially supported and yields consistent results.

    Resolution

    Orderly Team: Acknowledged.

  42. L-13 Low Griefing Of Vault Deposits Validation Acknowledged
    Location
    Vault.sol

    Description

    The _deposit() function in Vault.sol will revert if the balance of the contract after the deposit would exceed the tokenAddress2DepositLimit set by the owner. This can be manipulated by external party by sending tokens directly to the vault and DOS-ing deposits.

    Recommendation

    Introduce internal token deposits tracking instead of using balanceOf.

    Resolution

    Orderly Team: The issue was resolved in the merge request 330.

  43. L-14 Low Unfair Performance Fee Distribution Logical Error Acknowledged
    Location
    ProtocolVaultLedger.sol

    Description

    In situations where a SP underperforms while there is an increase in shares it will take a weighted average of the previous HWM and the HWM of the incoming increase. Although this does lower the HWM it will still be greater than the share price that the incoming depositors are entering at.

    Because of this incoming depositors can experience an increase in share price (profit) without paying any performance fee if the higher HWM is not exceeded. This essentially gives any user the opportunity to participate in the protocol profit off the SP's strategy without paying any fees.

    Recommendation

    Document that the performance fee burden is not always fair amongst LP’s when the fund moves from underperforming to not underperforming. Additionally monitor activity if the attack becomes an issue consider implementing incentives for LP’s that start and stay with underperforming funds.

    Resolution

    Orderly Team: Acknowledged.

  44. L-15 Low No Paused Check For Withdrawals Configuration Acknowledged
    Location
    Vault.sol

    Description

    Withdrawals in the Vault contract check if the receiver of the token is blacklisted and if they are, the WithdrawFailed event will be emitted to credit the sender back their tokens on the Ledger side.

    However, there is no check if the token contract is currently paused. If it is, the sender will have to wait until the contract gets unpaused even though the token may not be paused on other Orderly supported chains.

    Recommendation

    Check if the contract is paused, just like you are checking if the receiver is blacklisted.

    Resolution

    Orderly Team: Acknowledged.

  45. L-16 Low Centralization Risks Configuration Acknowledged
    Location
    Global

    Description

    The share price is calculated based on the total assets value provided by the backend. Operators can set the share price to arbitrary values by providing incorrect total asset amounts.

    Additionally, the setFeeRate function does not have an upper limit for fee rates, allowing them to be set even above 100%. Users also cannot access their funds that were deposited into the protocolVault until the deposits are handled in the period logic.

    Because of this the protocol can delay or not perform period updates and cause the users funds to be stuck in the protocolVault.

    Recommendation

    Users of the protocol should be aware of these centralization risks.

    Resolution

    Orderly Team: Acknowledged.

  46. L-17 Low Multiple Typos Code Best Practices Resolved
    Location
    Global

    Description

    ProtocolVault contract L235: “cal dex” should be “call dex”. ProtocolVaultLedger contract lines 685, 826 and 834: “_toatlShares” should be “_totalShares”. VaultFactory contract line 38: "with keythe deployer" should be "with the deployer".

    Recommendation

    Fix typos.

    Resolution

    Orderly Team: The issue was resolved in commit a528c91.

  47. L-18 Low updateUnclaimed Does Not Have Duplicate Check Validation Acknowledged
    Location
    ProtocolVaultLedger.sol: 541

    Description

    The updateUnclaimed function checks unhandled requestIds and creates userClaimInfos array based on their length. However, it does not perform a duplicate check for the requestIds.

    In the case of a duplicate entry, an unhandled requestId will be counted twice when calculating the array length but will only be added once to the array, as the first entry will mark that requestId as handled. This will result in the userClaimInfos array containing empty elements.

    Recommendation

    Consider implementing a check to prevent duplicate requestId entries.

    Resolution

    Orderly Team: Acknowledged.

  48. L-19 Low Salt Is Not Hashed With Deployer Address Configuration Acknowledged
    Location
    VaultFactory.sol

    Description

    According to the comments in the code, each deployer should have its own namespace, which is obtained by hashing the salt with the deployer's address. However, this is not the case in the actual code, where the salt is hashed by itself.

    Recommendation

    Consider hashing the salt with the deployer's address, or update the comments to reflect the current implementation.

    Resolution

    Orderly Team: The issue was resolved in commit a528c91.

  49. L-20 Low Unused Errors And Events Code Best Practices Resolved
    Location
    Global

    Description

    • NotAllowedToken and NotEnoughFee errors in IProtocolVault,
    • InsufficientBalance, AlreadyAllocatedShare, InvalidTotalAssets errors and StrategyExecuted event

    in IProtocolVaultLedger are not used in the codebase and can be removed.

    Recommendation

    Consider removing unused errors.

    Resolution

    Orderly Team: The issue was resolved in commit 8b6b665.

  50. L-21 Low Warning About Paused DexVault Configuration Acknowledged
    Location
    ProtocolVault.sol: 236-237

    Description

    The distributeAssets flow invokes ProtocolVault.depositToStrategy, which subsequently calls the DexVault.getDepositFee and DexVault.depositTo functions. The ProtocolVaultLedger contract on the Ledger chain does not implement Pausable, whereas the DexVault on the EVM chain is pausable.

    If the broker initiates the distributeAssets flow while the DexVault is paused, the Ledger chain transaction will succeed, but the EVM part of the transaction will revert.

    Consequently, isAssetDistributed will be set to true on the Ledger chain, even though the assets remain undistributed.

    Recommendation

    Be aware of this situation and avoid initiating a transaction on the Ledger chain when the receiver on the EVM chain is paused.

    Resolution

    Orderly Team: Acknowledged.

  51. L-22 Low Unvalidated Params In setAllowedStrategyProvider Validation Resolved
    Location
    ProtocolVaultLedger.sol: 606-616

    Description

    The setAllowedStrategyProvider function accepts multiple parameters, including spId, vaultId, the vault address, and brokerHash. It sets the spId and emits the AllowedStrategyProviderSet event with these parameters.

    Normally, spId is derived from these parameters. However, there is no check to ensure that the provided spId matches the ID derived from these parameters.

    If the provided values and the spId do not match, the function will still execute and emit an event containing misleading values for the backend.

    Recommendation

    Consider adding a check to ensure that the provided values match the spId.

    Resolution

    Orderly Team: The issue was resolved in commit 27dc0db.

  52. L-23 Low Some Functions Can't Cover Fee Costs Configuration Acknowledged
    Location
    ProtocolVaultLedger.sol

    Description

    distributeAssets and updateUnclaimed both will send a message which requires a fee amount. But The functions depend on there being a existing amount in the cross chain contract. This means that both distributeAssets and updateUnclaimed cant send its own msg.value to over the fee.

    Recommendation

    Consider making these functions payable and give the operator the option to supply some msg.value.

    Resolution

    Orderly Team: Acknowledged.

  53. L-24 Low A Hard-fork Can Disrupt Messaging Configuration Acknowledged
    Location
    Global

    Description

    If a hard fork occurs while a message is being sent it is possible that the chainId will change. If this were to happen there would be a mismatch in the chainID's impacting the messaging.

    Recommendation

    Monitor chain upgrades and in the rare cases that the chainId is going to change notify users or pause the protocol for a few blocks prior to the hard fork.

    Resolution

    Orderly Team: Acknowledged.

  54. L-25 Low accountToken.assets Is Never Decreased Logical Error Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    accountToken.assets is increased in the handleOpFromVault function when users deposit. But there is no way for this value to decrease. So regardless if there are withdrawals or not accountToken.assets will continue to grow with each deposit.

    Recommendation

    As accountToken is withdrawn consider decreasing the assets amount.

    Resolution

    Orderly Team: The issue was resolved in commit 474e339.

  55. L-26 Low No Withdrawal Confirmation For Solana Configuration Acknowledged
    Location
    LedgerImplC.sol

    Description

    When withdrawal requests are processed from the Ledger side to the Vault side, the balance of the user is decreased and the amount is frozen. Upon successful confirmation, the frozen value is being zeroed out.

    By tracking users' frozen balances, Orderly can increase their real balance back if the withdrawal action failed. This 2-step process is not happening for Solana withdrawals - everything is processed at once in LedgerImplC.executeWithdrawSolAction().

    If the withdrawal fails, the frozen balance will be 0 and the user can't get their funds back. In addition, the fee collector is rewarded fee amount with the decimal precision of the Ledger side. If there is a difference between these decimals on Solana, further problems may arise.

    Recommendation

    Be aware of the potential risks

    Resolution

    Orderly Team: Acknowledged.

  56. L-27 Low Market Manager Flag Not Cleared Validation Resolved
    Location
    LedgerImplB.sol

    Description

    At the end, LedgerB.executeProcessValidatedFuturesBatch() loops over each trade and calls _writeBackLastFundingUpdatedTimestamp().

    This function updates the last funding timestamp of the manager and sets the TSMarketManagerFlag() to true, which means no more updates for that tradeHash.

    This value is not cleared after the for loop ends. If multiple calls to executeProcessValidatedFuturesBatch() are made in the same transaction, only the first call will update the timestamp, since the transient storage flag will be left as true.

    Recommendation

    Be sure to use the function correctly.

    Resolution

    Orderly Team: The issue was resolved in the merge request 333.

  57. L-28 Low Slight withdrawAssets Discrepancy Precision Loss Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    ProtocolVaultLedger._handleLpWithdraw() converts the current withdrawal shares to assets and adds them to the appropriate userClaimInfo.

    Later in the flow, inside the allocatToFunds() function, the sum of all withdrawal shares (pendingLpWithdrawShares) is converted the same way to assets and the result is subtracted from pendingState.pendingTotalAssets.

    Because Solidity truncates on division, convertToAssets(pendingLpWithdrawShares) may not be equal to convertToAssets(withdrawShares1) + convertToAssets(withdrawShares2) + ....

    This can lead to a slight discrepancy between the recorded assets to be withdrawn and the actual amount, potentially corrupting the flow because of a wrong result returned by checkMainAndStrategyFund().

    Recommendation

    Be aware of this behavior.

    Resolution

    Orderly Team: The issue was resolved in commit ff1ab3.

  58. L-29 Low safeApprove() Deprecated Code Best Practices Acknowledged
    Location
    Vault.sol: 305

    Description

    SafeERC20::safeApprove() has been Deprecated. The developer note in the function discourages using this function, and instead recommends using safeDecreaseAllowance() and safeIncreaseAllowance().

    Recommendation

    Use safeIncreaseAllowance() instead of safeApprove().

    Resolution

    Orderly Team: Acknowledged.

  59. L-30 Low Token Deposits Cannot Be Disabled Configuration Acknowledged
    Location
    Vault.sol: 205

    Description

    Vault.deposit() has a validation that checks if tokenAddress2DepositLimit for a token is not zero, before seeing if the deposit limit has been exceeded. This prevents disabling deposits for a specific token, since a deposit limit of zero will bypass the second condition.

    Recommendation

    Consider adding a flag that will revert if the token is currently disabled for deposits.

    Resolution

    Orderly Team: Acknowledged.

  60. L-31 Low enableDepositFee Can Be Paused Configuration Acknowledged
    Location
    Vault.sol

    Description

    Vault.enableDepositFee() is a function which changes configuration, but it has the whenNotPaused modifier. This will stop the owner of updating the flag when the contract is paused.

    Recommendation

    Consider removing the modifier.

    Resolution

    Orderly Team: Acknowledged.

  61. L-32 Low Mixed Decimals Configuration Resolved
    Location
    ProtocolVaultLedger.sol

    Description

    It's expected that assetPerShare and hwm in ProtocolVaultLedger.sol will be with priceDecimals, but currently they will be with assetsDecimals + priceDecimals - shareDecimals.

    Recommendation

    Be aware of that.

    Resolution

    Orderly Team: The issue was resolved in commit 263831c.

  62. L-33 Low Unable To Reinitialize Contracts Configuration Acknowledged
    Location
    Global

    Description

    The initialize functions of the already deployed contracts won't be executed successfully because they are already initialized. For example, CrossChainRelayUpgradeable.sol has added logic in its initialize() function.

    Recommendation

    Remove the initializer modifier from the initialize function and add the reinitialize and onlyOwner modifiers.

    Resolution

    Orderly Team: Acknowledged.

  63. L-34 Low Lack Of onlyProxy Modifier Code Best Practices Acknowledged
    Location
    LedgerCrossChainManagerUpgradeable.sol, VaultCrossChainManagerUpgradeable.sol

    Description

    LedgerCrossChainManagerUpgradeable and VaultCrossChainManagerUpgradeable are UUPSUpgradeable contracts with upgradeTo() functions.

    In these contract the upgradeTo() function is overridden, but there is no onlyProxy modifier to it. This allows direct upgrades to the implementation.

    Recommendation

    Consider adding the onlyProxy modifier.

    Resolution

    Orderly Team: Acknowledged.

  64. L-35 Low Cross-chain Communication May Be Blocked Configuration Acknowledged
    Location
    CrossChainRelayUpgradeable.sol

    Description

    The CrossChainRelayUpgradeable is a blocking OApp which means that once initiated, a message has to be successfully executed on the destination chain in order for any subsequent message to be received.

    If the receiving transaction reverts, the communication channel between the two chains will be blocked until the owner call forceResumeReceive().

    A transaction can revert if one of the require checks which confirms the correct data is sent reverts, the contract the vault interacts with become paused and etc...

    Recommendation

    Consider switching to a non-blocking Oapp.

    Resolution

    Orderly Team: Acknowledged.

Invariants 32

The review's fuzzing suite asserted 32 invariants. 26 held and 6 did not.

Every invariant tested
IDInvariantResult
PV-01Deposit to ProtocolVault should deduct user tokensHeld
PV-02Receiver account token info unallocatedAssets should increase by amount when deposit isHeld
PV-03called on the ProtocolVault Receiver account token info assets should increase by amount when deposit is called onHeld
PV-04the ProtocolVault Deposit to ProtocolVault should deduct strategy tokensHeld
PV-05Strategy token info unallocatedAssets should increase by amount when deposit is called onHeld
PV-06the ProtocolVault Receiver account token info frozenShares should increase by amount when withdraw isHeld
PV-07called on the ProtocolVault NotEnoughWithdrawShares check should not be bypassedBroken
PV-08Strategy token info frozenShares should increase by amount when withdraw is called onHeld
PV-09the ProtocolVault User asset balance should increase by unclaimed assets when claiming assetsHeld
PV-10Strategy asset balance should increase by unclaimed assets when claiming assetsHeld
PV-11If asset per share > strategy hwm fundAssetsAfterFee must be less than total pending fund assets when callingHeld
PV-12updateStrategyFundAssets If asset per share > strategy hwm pendingStrategyProviderShares must increase when callingHeld
PV-13updateStrategyFundAssets If asset per share > strategy hwm pendingTotalShares must be greater than total pending fund assets when callingHeld
PV-14updateStrategyFundAssets AccountId PendingShares must be converted to accountId Shares after settleAccountsHeld
PV-15latestPeriodId should increment by 1 after updatePeriodIdHeld
PV-16pendingLpDepositAssets should increment be set to 0 after updatePeriodIdHeld
PV-17pendingLpWithdrawShares should increment be set to 0 after updatePeriodIdHeld
PV-17-REMpendingLpWithdrawAssets should be set to 0 after updatePeriodIdBroken
PV-18Protocol Vault token balance should decrease by asset distributionBroken
PV-19Dex Vault token balance should increase by asset distributionBroken
PV-20Dex Vault Ledger token balance should increase by asset distributionBroken
PV-21User unclaimable assets in ProtocolVault should increase by user claim info assets inHeld
PV-22ProtocolVaultLedger On executeWithdrawAction accountId ledger balance should decrease by amountHeld
PV-23On executeWithdrawAction receiver token balance should increaseHeld
PV-24On executeWithdrawAction protocolVault accountId ledger balance should decrease byHeld
PV-25amount On executeWithdrawAction protocolVault token balance should increaseHeld
PV-26feeCollector account balance should increment by fee amount afterBroken
PV-27withdraw2Contract Protocol Vault balance should increase by amount minus fee after withdraw2ContractHeld
PV-28When depositing to the DexVault user token balance should decrease by amountHeld
PV-29User account balance for the Ledger should increase by tokenAmount when depositing toHeld
PV-30the DexVault globalEventId should increment by 1 after a deposit from the DexVaultHeld
PV-31globalDeposittId should increment by 1 after a deposit from the DexVaultHeld

More from Orderly

All 8 reports
  1. Solana Vault, Sol-CC and EVM Updates

    53 findings1 critical · 6 high 53 findings: 1 critical, 6 high, 5 medium, 22 low, 19 informational
  2. Solana Vault

    41 findings1 high 41 findings: 1 high, 3 medium, 18 low, 19 informational
  3. Strategy Vault Updates

    30 findings1 high 30 findings: 1 high, 5 medium, 16 low, 8 informational
  4. Solana Staking

    35 findings1 critical · 1 high 35 findings: 1 critical, 1 high, 4 medium, 29 low

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote