Organization
- @OpenTrade
Engagement Type
Cantina Reviews
Period
-
Researchers
Findings
Medium Risk
2 findings
2 fixed
0 acknowledged
Low Risk
8 findings
6 fixed
2 acknowledged
Informational
7 findings
6 fixed
1 acknowledged
Gas Optimizations
2 findings
2 fixed
0 acknowledged
Medium Risk2 findings
Missing necessary status checks
State
- Fixed
PR #2
Severity
- Severity: Medium
Submitted by
Akshay Srivastav
Description
Various functions of protocol contracts are missing necessary protocol/account status checks.
LYTPool.processRedemption
: MissingwithdrawStateActive
modifier.LYTPool.depositOffChain
: MissingdepositStateActive
modifier.LYTPool.depositFromTransfer
: MissingdepositStateActive
modifier.LYTPool.changeRedemptionDestination
: MissingwithdrawStateActive
modifier.LYTPool.processRedemption
: Missing blacklist check onfundsDestination
address.LYTPool.changeRedemptionDestination
: Missing blacklist check onfundsDestination
address.LYTPool.setIndicativeAndCollateralRates
: MissingatState(ILYTPoolLifeCycleState.Active)
modifier.LYTPool.setExchangeRate
: MissingatState(ILYTPoolLifeCycleState.Active)
modifier.LYTPool.maxRedeemRequest
: MissingonlyNotPaused
modifier.LYTPool.maxRedeemRequest
: MissingatState(ILYTPoolLifeCycleState.Active)
modifier.
Recommendation
Consider adding all the mentioned status checks.
LYTPool: Pool tokens which are currently in active withdrawal queue can be burned.
State
- Fixed
PR #2
Severity
- Severity: Medium
≈
Likelihood: Low×
Impact: High Submitted by
Akshay Srivastav
Description
In
LYTPool
contract it is possible to burn LYTPools tokens which are currently in active withdrawal queue.Scenario:
- A market maker holds 100 pool tokens.
- Market maker requests a withdrawal of 50 tokens.
- Issuer calls
addAdjustmentAmount
and burns 60 tokens of market maker. - Now since 60 tokens have already been burned, the redemption of 50 tokens from step 2 cannot be performed. The
processRedemption
call reverts atL396
. - 40 pool tokens of market maker becomes unusable.
Recommendation
Add a
maxRedeemRequest
check inaddAdjustmentAmount
function.function addAdjustmentAmount( ... ) public ... { ... if (debit > 0) {- if (debit > assetBalanceOf(lender)) {- revert DebitGreaterThanAssets();- } uint256 shares = convertToShares(debit);+ if (shares > maxRedeemRequest(lender)) {+ revert BurnExceedsBalance();+ } _burn(lender, shares); emit AccountDebit(lender, debit); } else if (credit > 0) { ... } else { revert InvalidAccess(); } }
Low Risk8 findings
getActiveWithdraws can be gas consuming
State
- Acknowledged
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
ladboy233
Description
The
getActiveWithdraws
function iterates over all active withdrawal requests and returns the full list. If a large number of redeemers submit withdrawal requests, this approach can result in high gas consumption, making the function inefficient and potentially impractical to call on-chain.Recommendation
Introduce pagination (e.g., via index ranges or batching) to limit the number of withdrawal requests returned per call. This will reduce gas costs, improve scalability, and make the function safer to use when handling a large volume of requests.
Blocklisted users can create unprocessable redeem requests
Severity
- Severity: Low
≈
Likelihood: Low×
Impact: Low Submitted by
ladboy233
Description
Currently, blocklisted users are still able to create redeem requests, which are then added to the
activeWithdrawRequest
list. However, when the protocol attempts to process these requests, the transaction reverts at the step where it tries to burn the blocklisted lender’s token.This behavior leads to a situation where
getActiveWithdrawRequest
includes requests that can never be executed, causing inconsistencies and breaking assumptions for off-chain bots or automation that rely on this function to reflect executable withdrawals.Recommendation
Prevent blocklisted users from creating redeem requests in the first place. Adding a validation check before requests are added to
activeWithdrawRequest
will ensure the list only contains valid and executable entries, avoiding stuck or unprocessable states.LYTPoolServiceConfiguration.setLiquidityAsset duplicate liquidity asset entries can be added
State
- Fixed
PR #2
Severity
- Severity: Low
Submitted by
Akshay Srivastav
Description
In
LYTPoolServiceConfiguration.setLiquidityAsset
function duplicate liquidity asset entries can be added toliquidityAssetKeys
state.Recommendation
Consider reverting if
isLiquidityAsset[addr]
is already set tovalue
.LYTPoolAccessControlFactory.create: Missing paused status check of _serviceConfiguration
State
- Fixed
PR #2
Severity
- Severity: Low
Submitted by
Akshay Srivastav
Description
The
LYTPoolAccessControlFactory.create
function does not check thepaused
status of_serviceConfiguration
Recommendation
Consider adding the check.
LYTPool.deposit: Breaking checks-effects-interaction pattern
State
- Fixed
PR #2
Severity
- Severity: Low
Submitted by
Akshay Srivastav
Description
The
LYTPool.deposit
function breaks the CEI pattern and performs an external call before updating its internal storage states.Recommendation
Consider performing the
_mint
call before the_liquidityAsset.safeTransferFrom
call.LYTPoolServiceConfiguration: Incorrect IFactoryType enum datatype
State
- Fixed
PR #2
Severity
- Severity: Low
Submitted by
Akshay Srivastav
Description
The
IFactoryType
enum datatype does not containLYTPoolControllerFactory
type. Instead it incorrectly containsFeeCollector
type.Recommendation
Consider replacing
FeeCollector
withLYTPoolControllerFactory
inIFactoryType
enum.LYTPool.previewWithdrawRequest rounds down the shares amount
State
- Fixed
PR #2
Severity
- Severity: Low
Submitted by
Akshay Srivastav
Description
The
LYTPool.previewWithdrawRequest
function rounds down the calculatedshares
amount. This rounding direction favors the users instead of pool contract.As a security practice it is always recommended to always round in the direction which favors the pool contract.
Recommendation
Consider rounding up the
shares
value inpreviewWithdrawRequest
function.LYTPool: Market makers can hit the 100 active withdrawal request threshold
State
- Acknowledged
Severity
- Severity: Low
Submitted by
Akshay Srivastav
Description
The
LYTPool.requestRedeemExecute
function enforces a threshold of100
active withdrawal requests per market maker address. Since aLYTPool
will only have a limited number of whitelisted market makers which will perform all the pool token mints and burns, it is possible that those market makers may hit the100
active requests threshol which will block further redemption requests by such market makers.Recommendation
As market makers are whitelisted entities, the threshold of
100
requests can be removed or increased to a larger limit value.
Informational7 findings
setIndicativeAndCollateralRates missing event emission
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
ladboy233
Description
setIndicativeAndCollateralRates
missing event emission so the offchain service cannot track_indicativeInterestRate
and_collateralRate
change.Recommendation
function setIndicativeAndCollateralRates( uint256 _indicativeInterestRate, uint256 _collateralRate ) public onlyPoolController { _accounting.indicativeInterestRate = _indicativeInterestRate; _accounting.collateralRate = _collateralRate; emit IndicativeAndCollateralRatesChanged(_indicativeInterestRate, _collateralRate); }
LYPool.sol cannot trigger updatePoolData
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
ladboy233
Description
The
updatePoolData
function is protected by theonlyRegisteredPool
modifier. However, the LYPool.sol contract does not have a direct way to triggerupdatePoolData
.Recommendation
Introduce a function in the
controller
contract that allows the controller to call intoLYPool
to callupdatePoolData
Code implementations are not consistent with the spec
Description
The comments says that the functions listed below are only callable by protocol admin, but the modifier is
onlyIssuer
, notonlyProtocolAdmin
.addAdjustmentAmount
setFeeCollectorAddress
setIndicativeAndCollateralRates
Recommendation
Make sure Code implementations are consistent with the spec
LYTPoolAccessControl: Discrepancy in event names
State
- Fixed
PR #2
Severity
- Severity: Informational
Submitted by
Akshay Srivastav
Description
In
LYTPoolAccessControl
contract- the natspec of
allowParticipant
specifiesAllowedParticipantListUpdated
event butParticipantAllowed
event is emitted - the natspec of
removeParticipant
specifiesAllowedParticipantListUpdated
event butParticipantRemoved
event is emitted
Recommendation
Consider fixing the natspec comments.
Inconsistent version function across contracts
State
- Fixed
PR #2
Severity
- Severity: Informational
Submitted by
Akshay Srivastav
Description
Multiple protocol contracts implements
version
function but its implementation is inconsistent across those contracts. In some contractsversion
returns257
while in some it returns256
or512
.Recommendation
Consider removing the
version
function from all contracts or implement it in a consistent way across protocol.Incorrect Comments
State
- Fixed
PR #2
Severity
- Severity: Informational
Submitted by
Akshay Srivastav
Description
-
LYTPoolController.sol?lines=367,368
: Incorrect comment asactivatedAt
value is never updated.
Recommendation
Consider fixing the comments
LYTPool: Users can avoid pool token burn adjustments by transferring pool tokens
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
Akshay Srivastav
Description
The
LYTPool
tokens can be freely transferred by token holders. Those holders can avoid burn adjustments by transferring theirLYTPool
tokens to other addresses just before theaddAdjustmentAmount
transaction gets executed. Repeated execution of this mechanism can prevent burn adjustments from happening.Recommendation
This could be an accepted risk. If a mitigation is required then token transfers can be paused before making a burn adjustment.
Gas Optimizations2 findings
Unused Code
Description
These code instances are present in the codebase but are never used. Hence they can be removed.
-
LYTPool.sol?lines=197,197
:isPermittedLender
is not used inLYTPool.sol
. -
LYTPoolServiceConfiguration.sol?lines=52,59
: TheonlyProtocolAdmin
andonlyIssuer
modifiers are never used insideLYTPoolServiceConfiguration
contract. -
LYTPoolRegistry.sol?lines=24,24
: TheonlyOperator
modifier is never used in LYTPoolRegistry contract. -
LYTPoolController.sol?lines=81,83
: Unused modifiers:onlyProtocolAdminOrIssuer
,onlyProtocolAdminOrIssuerOrAutomation
&atState
. -
ILYTPoolStructures.sol?lines=50,55
:Initialized
andDisruptionOrDefault
states are never used. -
ILYTPoolStructures.sol?lines=10,10
:LYTPoolSettings
struct is never used. -
ILYTPoolStructures.sol?lines=67,67
:ILYTPoolWithdrawStage.Repaid
is never used. -
ILYTPoolStructures.sol?lines=73,73
:ILYTPoolDepositType.CrossChainDeposit
is never used. -
IPoolStructures.sol
:IPoolStructures.sol
file is never used. -
IERC4626.sol
:IERC4626.sol
file is never used.
Recommendation
Remove the unused code to save gas.
-
LYTPool.requestRedeemExecute: Redundant withdrawStateActive modifier
State
- Fixed
PR #2
Severity
- Severity: Gas optimization
Submitted by
Akshay Srivastav
Description
The
withdrawStateActive
modifier can be removed fromrequestRedeemExecute
function as it is already present onrequestRedeem
function.Recommendation
Consider removing the modifier from
requestRedeemExecute
function.