Orderly Network engaged Guardian to review the security of its perpetuals protocol utilizing an off-chain order book. From the 1st of October to the 13th of October, a team of 3 auditors reviewed the source code in scope.
- Published
- Review window
- October 1 to 13, 2023
- Language
- Solidity
- Chains
- Ethereum, Arbitrum, Optimism, Base, Solana
- Sector
- Perpetuals
- 3 Critical
- 2 High
- 17 Medium
- 19 Low
- 0 Informational
Scope
Overview
Orderly Network engaged Guardian to review the security of its perpetuals protocol utilizing an off-chain order book. From the 1st of October to the 13th of October, a team of 3 auditors reviewed the source code in scope.
Findings 41
-
LGR-1 Critical Duplicated insuranceTransferAmount Logical Error Resolved
Description
In the
executeSettlementfunction, if thesettlement.insuranceTransferAmountamount is nonzero it is added to both theinsuranceFund.balancesand theaccount.balancestherefore duplicating thesettlement.insuranceTransferAmountacross these two accounts.The validation on the
insuranceTransferAmountindicates that thesettlement.insuranceTransferAmountshould be deducted from theinsuranceFund.balancesand added or “transferred” to theaccount.balances.Recommendation
Modify the following lines:
insuranceFund.balances[settlement.settledAssetHash] += settlement.insuranceTransferAmount; account.balances[settlement.settledAssetHash] += settlement.insuranceTransferAmount;Such that the balance is transferred rather than duplicated:
insuranceFund.balances[settlement.settledAssetHash] -= settlement.insuranceTransferAmount; account.balances[settlement.settledAssetHash] += settlement.insuranceTransferAmount;Resolution
Orderly Team: Fixed.
-
LCMU-1 Critical Any User Can Set A Token’s Decimals Access Control Resolved
Description
There is a lack of access control on the
setTokenDecimalfunction, therefore any user can set an arbitrary decimal amount for anytokenHashon anytokenChainId. This can easily be used to inflate how many tokens a user has on-chain and drain the vault.For example, assume WETH has 18 decimals on chain A and 18 decimals on chain B. However, Alice sets the decimals for chain A as 18 and the decimals for chain B as 19. Upon converting 1 ETH from Chain A, the amount of WETH on Chain B after conversion is
tokenAmount * uint128(10 **(dstDecimal - srcDecimal))=1e18 * 10**1 = 1e19.Alice was just able to turn 1 WETH on chain A into 10 WETH on chain B, and can withdraw those extra funds from the vault. The theft can be even more drastic by increasing the decimal spread between chains.
Recommendation
Ensure only the owner can set the token decimals.
Resolution
Orderly Team: Fixed.
-
GLOBAL-1 Critical Malicious User Drains CrossChainRelay DoS Acknowledged
Description
There is no minimum deposit amount in the Vault contract, therefore a malicious actor may make many deposits with trivial amounts to drain the
CrossChainRelayof its Ether and halt execution in the system.Recommendation
Implement a minimum deposit amount in the
Vaultcontract such that DoS attacks like this one become unfeasible.Resolution
Orderly Team: We have proposed a solution to this issue. As mentioned, we will require users to cover the cross-chain fee, this feature will be released in the next sprint.
-
LGR-2 High Rounded Frozen Balance Bricks Withdrawals Precision Acknowledged
Description
Proof of concept: PoC
When making a withdrawal, the action goes from the ledger chain to the vault chain back to the ledger chain. When sending the message to the vault,
withdrawis called which converts the fee to the proper amount of decimals on the vault chain usingconvertDecimal. However, truncation may occur due to the collateral on the vault chain having fewer decimals than on the ledger chain.For example, let's assume the fee is initially 1 wei and the decimal spread between chains is 12. When
executeWithdrawActionis triggered on the ledger, 1 wei of fees have been accounted for such that theVaultManagerfreezes 1 less wei than thewithdraw.tokenAmounttransmitted: vaultManager.frozenBalance(tokenHash, withdraw.chainId, withdraw.tokenAmount - withdraw.fee).Once
ILedgerCrossChainManager(crossChainManagerAddress).withdraw(withdraw)is called, decimal conversion occurs and the new fee to be sent to the vault chain is1 / 10**12 = 0. When the message finally arrives back to the ledger to finish withdrawal vaultManager.finishFrozenBalance(withdraw.tokenHash, withdraw.chainId, withdraw.tokenAmount -withdraw.fee)is called.Because the
withdraw.feeis smaller than accounted for initially (1 wei vs 0 wei), more is unfrozen than was originally frozen uponexecuteWithdrawAction, which will cause an arithmetic underflow in theVaultManagerand brick all withdrawals until a user deposits to cover the deficit. This can continuously be done maliciously to continuously block withdrawals from occurring.Recommendation
Round beforehand such that the rounded value when finishing the withdrawal is the same as when starting the withdrawal. Alternatively, use higher precision for token amounts although this may be a considerable refactor.
Resolution
Orderly Team: This is an known issue and wont fix in codes. The key point is that the decimal of Ledger should be less or equal to the decimal of vaults, thus no round occurs.
-
ATPH-1 High Incorrect Decimals Precision Resolved
Description
When the
calAverageEntryPricefunction is called during a liquidation or ADL execution, theliquidationQuoteDiffhas 6 decimals of precision. Therefore the resultingquoteDiffon line 72 has 14 decimals of precision.This differs from the
quoteDiffdecimals of 16 when uploading a trade. Therefore theaverageEntryPriceandopeningCostvalues are perturbed when thequoteDiffis used to calculate the average entry during a liquidation or ADL.Recommendation
Adjust the
quoteDiffduring liquidation or ADL such that it has the expected 16 decimals of precision.Resolution
Orderly Team: Fixed.
-
GLOBAL-2 Medium LayerZero Message Blocking DoS Acknowledged
Description
LayerZero implements blocking functionality such that when a transaction on the destination chain fails, before any new transactions can be executed, the failed transaction has to be retried until success.
A malicious user can submit a deposit or withdrawal they know will fail, and will prevent all other deposits and withdrawals from occurring, leading to loss of protocol functionality and loss of funds for those who already deposited.
Some methods a malicious user can use to force such a scenario include but are not limited to:
- Broker is allowed for Vault on
srcChainbut disallowed on theVaultManagerondstChain. The
configuration of these two contracts aren’t atomic. 2)
depositTofor an address that is blacklisted for the collateral token. When the blacklisted address triggers a withdraw action, the Vault withdrawal will revert.Recommendation
Consider utilizing a non-blocking approach as described in LayerZero documentation.
Resolution
Orderly Team: We can handle blocking scenarios, and we also have a force resume function to force drop a message.
- Broker is allowed for Vault on
-
GLOBAL-3 Medium Centralization Risk Centralization Risk Acknowledged
Description
Throughout the smart contract system there is a lack of validation to prevent privileged addresses from taking malicious actions or even committing errors that have drastic consequences.
- Executing malicious withdrawals, settlements, ADLs, and liquidations.
- No validation that the
liquidationFee = liquidatorFee + insuranceFee. - No validation that the ratio of
positionQtyTransfertocostPositionTransferis accurate to theadlPriceprovided. - No validation that tokens being actively used as collateral cannot be removed from support, causing liquidations and insolvency.
- No validation that
trade.notional = trade.tradeQty * trade.executedPricein theexecuteProcessValidatedFuturesfunction. - No cap on configured values such as the
maxWithdrawalFeeandliquidationFeeMax. - No cap on the
liquidationFeeas theliquidationFeeMaxis unused at the contract level.
Recommendation
Consider implementing validations to prevent any potential errors privileged addresses may make. And be sure to document the risks of these privileged abilities.
Resolution
Orderly Team: Acknowledged.
-
VAULT-1 Medium Allowed Token Contract Address Added Logical Error Resolved
Description
The documentation for
setAllowedTokenstates that the function is supposed to “Add contract address for an allowed token given the tokenHash.”However,
setAllowedTokenonly adds or removes thetokenHashfrom theallowedTokenSet. As a result, the address for the token is never added to theallowedTokenmapping.Deposits and withdrawals will revert as the zero address does not have function
safeTransferFrom, and continue to revert untilchangeTokenAddressAndAllowis called, which according to the documentation is an "unusual case on Mainnet".Recommendation
Add a parameter for the token address and perform
allowedToken[_tokenHash]=_tokenAddress.Resolution
Orderly Team: Fixed.
-
ATPH-2 Medium Half Rounding Improperly Handles 0 Quotient Logical Error Resolved
Description
Proof of concept: PoC
Orderly utilizes the
halfUpandhalfDownmethods to round the quotient up or down on the magnitude of the fractional part. However, the edge case of a 0 quotient will cause the division to return a mathematically incorrect result. For example, consider the function callhalfUp16_8(10, 11)int256 quotient = dividend / divisor = 10 / 11 = 0 int256 remainder = dividend % divisor = 10 % 11 = 10 if (10 * 2 >= 11) { if (quotient > 0) { quotient += 1; } else { quotient -= 1; <- quotient = 0 - 1 = -1 } }The returned quotient is
-1although10 / 11 = 0.909is not supposed to be a negative number and should round to1.Recommendation
Add 1 to the quotient when
quotient >= 0Resolution
Orderly Team: Fixed.
-
ATPH-3 Medium Average Entry Can Be Rounded In User’s Favor Logical Error Resolved
Description
When rounding operations are performed, it is safer for the protocol to round against the users so that there is a smaller likelihood of needing to use the insurance fund or ADL.
The average entry price is calculated using
position.averageEntryPrice =halfDown16_8(-openingCost, currentHolding).toUint128()which rounds up if the fractional part is greater than half, and down otherwise.When a trader is long, it is possible for the entry price to round down which would provide the user a superior entry as they want to buy as low as possible.
When a trader is short, it is possible for the entry price to round up which would provide the user a superior entry as they want to sell as high as possible.
Recommendation
Round against the user depending on their trade direction.
Resolution
Orderly Team: The recommendation was implemented in commit 5dc96c9b.
-
GLOBAL-4 Medium Vault Can Be Drained On Specific Chain Protocol Manipulation Acknowledged
Description
Because a withdrawal message can be sent to a Vault on any supported chain, a malicious user can deposit in a Vault on one chain and withdraw from a vault on another chain. This can be used to drain the Vault on a chain, forcing other users to withdraw their liquidity on other chains.
Recommendation
Consider restricting what Vault a user can withdraw from, such as only allowing withdrawals from the chain they performed a deposit.
Resolution
Orderly Team: Will fix in the next version. Rebalance (with CCIP) is considered to solve this issue.
-
LGR-3 Medium User Can Withdraw Entire Collateral When Open Position Logical Error Acknowledged
Description
Proof of concept: PoC
Before creating a position, a user is required to deposit a balance sufficient for their position size. Upon the call to
executeWithdrawAction, there is validation that the withdraw amount is not greater than the balance. However, there is no validation at the contract level in theexecuteWithdrawActionfunction that restricts a user from withdrawing their entire balance once they have an open position.If a user’s position is in loss, the user could simply withdraw their entire collateral and not risk losing any of their balance during settlement. This terribly disrupts the operations of the protocol as funds won’t be available to pay profitable traders.
Recommendation
Before allowing a withdrawal, consider validating that the position will not be in a liquidatable state on chain. This may require passing a price with the
WithdrawData.Resolution
Orderly Team: Won’t fix now. maybe in the future version.
The restrict of withdraw is controlled by engine team, so the contract will not lose balance.
In order to restrict withdrawal by contract, a lot of work need take into consideration with a big update:
- align price oracle for liquidation, for both contract and engine
- liquidation should be triggered before withdrawals, for prevent bad debts
- as the suggestion, add a price with the WithdrawData
-
VAULTM-1 Medium Funds Will Be Locked On Hard Fork Hard Fork Acknowledged
Description
In the
VaultManagercontract, token balances are stored in mappings such astokenBalanceOnchainortokenFrozenBalanceOnchain. These mappings take a token hash and achainIDand point to a token balance.If a chain were to experience a hard fork that
chainIDwill change. This will result in the token balance being inaccessible as there will be a difference between what is being stored in state and the actualchainIDof the chain.Recommendation
Add an
onlyOwnerrestricted function that will allow the protocol to migrate balances from the oldchainIDto the newchainID.Resolution
Orderly Team: Won’t fix. Will add migrate method only this really happens.
- When a hard fork happens, the legitimate chain should remain the same ChainID, and we should
follow this chain.
- Even the ChainID changes in HF, we can upgrade the contract to support migrate.
-
LGR-4 Medium Large Withdrawals Can Fail Logical Error Acknowledged
Description
In the
executeWithdrawActionfunction, there is a check to ensure that thewithdraw.feeis less than themaxWithdrawFee. However themaxWithdrawFeeis a fixed value that will be used for all withdrawals. While thewithdraw.feewill be a percentage based on the size of the individual withdraw.Because the two are inherently misaligned, there is a risk that large withdraws will fail as their
withdraw.feewill be greater than themaxWithdrawFee. Users in this scenario will need to break up their withdrawals into multiple smaller withdrawals leading to operational inefficiency.Recommendation
Make
maxFeepercentage based so that the fee is never more then X percentage of the amount being withdrawn. This will ensure that all valid withdrawals are still possible.Resolution
Orderly Team: Won’t fix.
- In design, maxWithdrawFee should be a net value, not a percentage value.
- maxWithdrawFee will be only used in emergency case.
-
SIG-1 Medium Signature Malleability Signatures Resolved
Description
The
verifyfunction usesecrecoverwithout any validation that thesvalue is from one half of the validsrange, therefore it is possible for signatures to be maliciously replayed with a differents. At present, theOperatorManageris the only address that can perform this signature malleability, however, the opportunity should be removed.Recommendation
Use the OpenZeppelin ECDSA library, which automatically restricts the valid s range, to verify signatures.
Resolution
Orderly Team: Fixed.
-
MKTM-1 Medium Incorrect updatedAt Assigned Logical Error Resolved
Description
In the
updateMarketUploadfunctions for both the perp prices andsumUnitaryFundingsthelastMarkPriceUpdatedandlastFundingUpdatedare set to theblock.timestamp. However, these attributes ought to be set to theperpPrice.timestampandsumUnitaryFunding.timestampas these are the timestamps from which the data was recorded.Using the
block.timestampfor thelastFundingUpdatedandlastMarkPriceUpdatedvalues is not in line with the logic in theLedger.executeProcessValidatedFuturesfunction where thelastFundingUpdatedis assigned to thetrade.timestamprather than theblock.timestamp.Recommendation
Replace the
cfg.setLastFundingUpdated(block.timestamp)lines withcfg.setLastFundingUpdated(data.timestamp).Resolution
Orderly Team: Fixed.
-
LGR-5 Medium Potential For Trapped Deposits Trapped Funds Acknowledged
Description
In the event that the
accountDepositfunction execution reverts, there is no recourse for the user to recover their deposit in the vault.The
accountDepositfunction may revert if theAccountDepositdata carries abrokerHashortokenHash&srcChainIdthat does not agree with the configuration in thevaultManagercontract.Recommendation
Consider implementing a method for the user to recover their funds in the event that the cross-chain deposit transaction cannot succeed even upon retry.
Resolution
Orderly Team: Won’t fix, but should pay attention:
- Ledger should update whitelist before vault update.
- Even the situation occurs, the cc tx will be payload-store, and can be retried after updating
Ledger’s whitelist.
-
LGR-6 Medium Insurance Account May Become Insolvent Logical Error Acknowledged
Description
When liquidatable accounts cannot cover the
liquidatorFeewith their remaining margin, all positions in the account and the remaining margin balance are transferred to the insurance fund.In times of volatility, several accounts may become insolvent and all have their positions transferred to the insurance account. The insurance account may then find itself to be insolvent, in which case ADL will not be sufficient to remedy the situation.
The insurance account is also intended to cover insolvent accounts where the settled PnL is more negative than the account margin. This behavior can also be a pathway for the insurance account to become insolvent, especially when combined with receiving positions from insolvent accounts.
Recommendation
Though this scenario may be rare, it is a distinct possibility. Have a contingency plan in the event that this scenario ever plays out, for example a deposit can automatically be made for the insurance fund when it nears insolvency.
Resolution
Orderly Team: Yes this is a risk; but this risk is also present in other CEXes Reply from our product.
-
GLOBAL-5 Medium Liquidations When Deposits Paused Logical Error Acknowledged
Description
When the Vault is paused, functions
depositanddepositToare prevented from being processed. As a result, the protocol can reach a state where liquidations can occur while deposits are paused, preventing users from keeping their positions solvent.Recommendation
Consider pausing liquidations when users are unable to increase their collateral and/or clearly document this scenario for users.
Resolution
Orderly Team: Won’t fix.
- Should only pause Vault in emergency case, that is, Vault is hacked, or under HF, or unstable state.
- Even one chain may be paused, other vault is still working. 3. So no need to pause liquidation in
unstable situation. The engine team will take more actions.
-
GLOBAL-6 Medium Lacking Storage Gaps Storage Gaps Acknowledged
Description
There are several Component abstract contracts that intend to be parents of upgradeable contracts, however they lack an appropriate
_gapstorage variable.For example, the
LedgerComponentcontract is an abstract contract that is meant to be inherited by upgradeable contracts, however there is aledgerAddressstorage variable defined in theLedgerComponentfollowed by no gap variable in the event that more variables would be added to theLedgerComponentcontract.Recommendation
Add a storage
_gapvariable so that storage variables may be added to theLedgerComponentcontract without causing storage collisions.For more information on the
_gapvariable refer to theOpenZeppelindocumentation.Resolution
Orderly Team: Wont fix, but will continously pay attention to this issue.
- LedgerComponent is an abstract contract, very simple, and will not add any new fields.
- Contract Ledger and OperatorManager is already use a DataLayout contract with _gap
- Namespaced Storage is better than _gap method, we are still investigating
https://blog.openzeppelin.com/introducing-openzeppelin-contracts-5.0#Namespaced
-
OPMAN-1 Medium Risk Of DoS DoS Acknowledged
Description
In the
_futuresTradeUploadDatafunction, a batch of trades are uploaded in a single transaction which allows a single malicious trade to DoS the entire batch if it reverts.For example, one trade in the batch could have an invalid
symbolHash, which would cause theexecuteProcessValidatedFuturesexecution to revert. Similarly, in the_eventUploadDatafunction, a batch of events are uploaded to be processed in a single tx which allows a single invalid event to DoS the entire batch.Recommendation
Be aware of this DoS risk and be sure to simulate batches to verify that they contain no invalid trades before sending a batch upload transaction. Additionally, consider implementing logic at the contract to handle invalid events or trades.
Resolution
Orderly Team: Won’t fix, acknowledged. These two function are triggered by engine team, with onlyOperator modifier. In design, there should be no invalid call. We also add many checks to avoid malicious call(signature check, whitelist symbolHash check, etc).
-
ATPH-4 Medium DoS On Average Entry Price DoS Acknowledged
Description
Proof of concept: PoC
When a position is updated after a trade, the average entry price is calculated using
calAverageEntryPrice.However, if the
openingCostandcurrentHoldingare of the same sign, the calculation ofposition.averageEntryPrice = halfDown16_8(-openingCost, currentHolding).toUint128()will revert with aSafeCastOverflow. This is becausehalfDown16_8(-openingCost, currentHolding)will return a negativeintwhich cannot be cast to auint.This can occur if the
currentHoldingexceeds theopeningCostfor a short position, becausehalfDown16_8will round the quotient to -1 onelse { quotient -= 1; };Note that ifhalfDown16_8is modified to round up on a 0 quotient, the issue can still occur ifcurrentHoldingexceeds theopeningCostfor a long position, becausehalfDown16_8will round the quotient to 1 and thecurrentHoldingis also positive.Recommendation
Carefully consider which markets are supported, as markets with small prices are more susceptible to this issue. Furthermore, consider implementing a minimum trade size since the attack is more susceptible to smaller quantities causing the
openingCostto round down to -1.Resolution
Orderly Team: Acknowledged. In design, this will not happen. halfDown16_8 is only a stateless function, and in this situation, the return value should never be negative.
-
GLOBAL-7 Low Unnecessary Timestamp Emitted Superfluous Code Resolved
Description
Throughout the codebase the
block.timestampis often emitted with events, however this is unnecessary as the timestamp of the event can be retrieved from the block in which it was emitted. Therefore gas does not need to be expended to emit theblock.timestampas a part of event data.Recommendation
Remove the
block.timestampfrom each event and retrieve the timestamp from the block in which the event was emitted.Resolution
Orderly Team: The recommendation was implemented in commit 31d480d2.
-
PRPT-1 Low Unused Side Attribute Superfluous Code Acknowledged
Description
The
sideboolean on theFuturesTradeUploadstruct is unused in the contracts.Recommendation
Implement a use case for the side attribute or consider removing it.
Resolution
Orderly Team: Won’t fix now, the usage of this is for signature verification.
-
ETYP-1 Low Unused liquidationTransferId attribute Superfluous Code Acknowledged
Description
The
liquidationTransferIdis never accessed from theliquidationTransferobject.Recommendation
Implement a use-case for the
liquidationTransferIdor remove it from theLiquidationTransferstruct.Resolution
Orderly Team: Won’t fix now, the usage of this is for signature verification.
-
SFCH-1 Low Inaccurate Overflow Error Errors Resolved
Description
In the
toUint128function if the providedint128value is negative, the function reverts with aSafeCastOverflowerror. However the function should revert with aSafeCastUnderflowerror as the attempted casting would underflow rather than overflow.Recommendation
Create a
SafeCastUnderflowerror and use this error to revert thetoUint128function in the event that a negative number is provided.Resolution
Orderly Team: Fixed.
-
GLOBAL-8 Low Multiple Sources Of Truth Warning Acknowledged
Description
The
Vaultcontract and theVaultManagercontract both track which tokens are valid for the vault, therefore there are two sources of truth for which tokens are supported by any given vault.However there is no guarantee that the
allowedTokenSetin the Vault contract and theallowedChainTokenmapping in theVaultManagercontract are in sync.If the
allowedTokenSetandallowedChainTokenmapping are ever in disagreement the system accounting is perturbed and the system is may become insolvent.Recommendation
Be aware of this design flaw and risk when updating configuration on a vault chain or the ledger chain.
Resolution
Orderly Team: Acknowledged
- Team should keep consistent of vualt and ledger chain’s allowedList.
- And Ledger should update whitelist before vault update as this issue commented:
04971a6bd0f4ba0f08&p=50b425edf5af4caba8ffce2e0771c6ca&pm=s
-
ATPH-5 Low MaintenanceMargin Does Not Match Documentation Documentation Acknowledged
Description
The unused
maintenanceMarginfunction in theAccountTypePositionHelpercalculates thepositionQty * markPrice * Base MMR.MMR i = Max(Base MMR i, Base MMR i / Base IMR i * IMR Factor i * Abs(Position Notional i)^(4/5))
Recommendation
Either update the documentation or update the implementation of the
maintenanceMarginfunction.Resolution
Orderly Team: Acknowledged.
-
ATPH-6 Low Unsafe Casting Casting Resolved
Description
The quotient returned in
halfUp16_8_i256is cast to anint128from anint256. This casting operation is unsafe as the result can silently overflow.For example, when the
quotient = type(int256).max, casting toint128will not revert and return -1 which is an unexpected result.Recommendation
Use the OpenZeppelin
SafeCastlibrary or implement your own checks to validate the range of a type is not exceeded prior to casting.Resolution
Orderly Team: Fixed.
-
VAULT-2 Low Missing Check-Effect-Interact Pattern Reentrancy Resolved
Description
In the
withdrawfunction the token amount is transferred to the user before callingwithdrawon thecrossChainManagerAddress.This could result in reentrancy opportunities, currently there are no immediate risks with the
withdrawfunction. However, best practice is to follow the Check-Effects-Interactions pattern when transferring out tokens to protect against reentrancy attacks.Recommendation
Use the Check-Effects-Interactions pattern by transferring tokens out of the vault after state changes occur.
Resolution
Orderly Team: Fixed.
-
LGR-7 Low Redundant for-loop Optimization Resolved
Description
In the
executeSettlementfunction, the first for-loop is redundant as thetotalSettleAmountcan be computed inside the second for-loop and validated at the end.Recommendation
Combine the validation logic into a single for-loop in the
executeSettlementfunction.Resolution
Orderly Team: The recommendation was implemented.
-
ATPH-7 Low Typo Typo Resolved
Description
The
accruedFeeUncovertedvariable misspells unconverted as uncoverted.Recommendation
Replace
accruedFeeUncovertedwithaccruedFeeUnconverted.Resolution
Orderly Team: The recommendation was implemented.
-
LGR-8 Low Unused Helper Functions Superfluous Code Resolved
Description
The balances adjustment in the
executeSettlementfunction can usesubBalanceand theaddBalancehelper functions rather than adjusting the balances mapping directly.Recommendation
Use the
subBalanceandaddBalancefunctions to adjust the balances mapping in theexecuteSettlementfunction.Resolution
Orderly Team: The recommendation was implemented.
-
CCRU-1 Low Hardcoded Zero Address Hardcoded Value Acknowledged
Description
In the
sendMessagefunction_lzSendis called. One of the parameters in the_lzSendfunction is_zroPaymentAddress.According to Layer Zero integration recommendations the
_zroPaymentAddressshould not be hardcoded. Instead it should be passed as a parameter instead.Recommendation
Pass the
_zroPaymentAddressas a parameter instead of hardcoding it.Resolution
Orderly Team: Acknowledged.
-
ATPH-8 Low Multiplication On The Result Of Division Precision Resolved
Description
The
maintenanceMarginfunction performs a multiplication on the result of a division, leading to precision loss in the final maintenance margin requirements.The
position.positionQty.abs().toInt128() * markPricecalculation will return a 16 decimal result because both quantity and price are 8 decimal precision values.Further multiplying by the
baseMaintenanceMarginin the numerator has virtually no overflow risk as the max value of thebaseMaintenanceMarginis 10,000.Recommendation
Perform the multiplication before the division:
position.positionQty.abs().toInt128() * markPrice * baseMaintenanceMargin /(int128(MARGIN_100PERCENT) * PRICE_QTY_MOVE_RIGHT_PRECISIONS)Resolution
Orderly Team: The recommendation was implemented.
-
VCCMU-1 Low Excess Fee Locked in Contract Trapped Funds Resolved
Description
In the
depositWithFeefunction a deposit can be made with a fee attached. The fee is determined by theamountparameter, anddepositWithFeechecks that themsg.valueis greater than or equal to theamountbeing passed in.When the fee is sent in
sendMessageWithFeethe value is the originalamountthat was passed into the function. The issue is that ifmsg.valueis greater thenamountthe excessmsg.valuewill be left in the contract with no way of retrieving it.Currently the function is not accessible so this possesses no immediate risk. But the issue should be fixed if there are intentions of using this function.
Recommendation
Either send the excess
msg.valueback tomsg.senderor to an address that can handle the funds so that they are not stuck.Resolution
Orderly Team: Fixed, amount is removed and msg.value is taken as a fee.
-
LGR-9 Low Excess Insurance Fund Transfer Protection Validation Acknowledged
Description
The following validation ensures that any transferred insurance amount is sufficient to cover any negative collateral for an account:
if ( balance.toInt128() + settlement.insuranceTransferAmount.toInt128() + settlement.settledAmount < 0 || settlement.insuranceTransferAmount > settlement.settledAmount.abs() ) { revert InsuranceTransferAmountInvalid( balance, settlement.insuranceTransferAmount, settlement.settledAmount ); }However the validation allows for a potentially significant amount of extra funds from the insurance account to be transferred to the user’s account.
Recommendation
Consider altering the validation such that if an
insuranceTransferAmountis specified, it must be exactly the amount necessary to make the account solvent, or within a smaller range of an amount that would make the account solvent.Resolution
Orderly Team: Acknowledged.
-
LGR-10 Low Position Never Cleared Optimization Resolved
Description
Upon liquidating a position,
liquidatedPosition.isFullSettled()is called to check whether the cost and quantity of the position are both 0, and clears the position if so.However,
isFullSettledis never checked uponexecuteSettlementnorexecuteAdl.Recommendation
When positions are cleared in the
executeSettlementorexecuteAdlfunctions, clear them whenisFullSettled()istrue.Resolution
Orderly Team: The recommendation was implemented for liquidations, however ADL should rarely need to clear positions.
-
CCRU-2 Low Missing Address 0 Check Validation Acknowledged
Description
In the
CrossChainRelayUpgradeablecontract, theinitializefunction accepts an_endpointaddress, yet fails to validate that it is notaddress(0).Recommendation
Validate that the
_endpointaddress is notaddress(0)in theinitializefunction to avoid improper deployments.Resolution
Orderly Team: Endpoint can be updated later using function updateEndpoint(address _endpoint) external onlyOwner. Even if endpoint address is not address(0) , it could be another wrong address. Both situations are handled by later calling updateEndpoint.
-
GLOBAL-9 Low Use LayerZero Package Maintainability Acknowledged
Description
The Layer Zero documentation recommends that projects use the latest version of the
solidity-examplespackage, rather than directly copying example contracts.The
solidity-examplespackage is not used in theevm-cross-chainrepository, and therefore if a patch is ever issued it would not be present inevm-cross-chain.Recommendation
Use the solidity-examples package as suggested in the LayerZero docs.
Resolution
Orderly Team: In the future we may have custom requirements, so we will choose to maintain this code ourselves.
-
UTIL-1 Low Unnecessary Util Functions Superfluous Code Resolved
Description
There is no need to have both the
getBrokerHashandgetTokenHashfunctions since their logic is exactly the same, only the naming of the parameters differ.The
getBrokerHashandgetTokenHashfunctions simply return the result ofcalculateStringHash.Recommendation
Use the
calculateStringHashfunction directly.Resolution
Orderly Team: The recommendation was implemented.
No findings match.
More from Orderly
All 8 reports-
Solana Vault, Sol-CC and EVM Updates
53 findings1 critical · 6 high 53 findings: 1 critical, 6 high, 5 medium, 22 low, 19 informational -
Solana Vault
41 findings1 high 41 findings: 1 high, 3 medium, 18 low, 19 informational -
Strategy Vault Updates
30 findings1 high 30 findings: 1 high, 5 medium, 16 low, 8 informational -
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.
