Omniscia Steer Protocol Audit

PluginV1 Manual Review Findings

PluginV1 Manual Review Findings

PV1-01M: Incorrect Hook Implementations

TypeSeverityLocation
Logical FaultPluginV1.sol:
I-1: L247-L249
I-2: L251-L253
I-3: L256-L263
I-4: L266-L273
I-5: L315-L322
I-6: L325-L327
I-7: L330-L337

Description:

The referenced hook implementations are incorrect as they are not supported per the PluginV1::defaultPluginConfig function.

Example:

src/Adapters/Algebra/PluginV1.sol
247function beforeInitialize(address, uint160) external view onlyPool returns (bytes4) {
248 return IAlgebraPlugin.beforeInitialize.selector;
249}

Recommendation:

We advise them to either revert explicitly or to not be implemented at all, either of which we consider an adequate resolution to this exhibit.

Alleviation:

All redundant hook implementations have been safely omitted from the codebase.

PV1-02M: Inexistent Validation of Deposit Limitations

TypeSeverityLocation
Logical FaultPluginV1.sol:
I-1: L688
I-2: L698

Description:

The referenced EIP-4626 deposit operations will not evaluate whether the actual deposit is possible per the vault's maximum deposit limits, potentially resulting in an unwarranted Denial-of-Service.

Impact:

EIP-4626 vaults that have deposit limits will presently misbehave under the current PluginV1 implementation as they will directly affect the AMM as a Denial-of-Service error.

Example:

src/Adapters/Algebra/PluginV1.sol
670function makePositions(IPositionStrategy.LiquidityPositions[] memory _positions, uint128[] memory liquidities)
671 internal
672{
673 require(_positions.length == liquidities.length, "PAL");
674
675 for (uint256 i = 0; i < _positions.length; i++) {
676 if (liquidities[i] > 0) {
677 mintCallBackProtection = true;
678 IAlgebraPool(pool).mint(
679 address(this), address(this), _positions[i].lowerTick, _positions[i].upperTick, liquidities[i], ""
680 );
681 }
682 }
683 // Transfer remaining token0 to its vault
684 if (token0Vault != address(0)) {
685 uint256 remaining = _getBalance0();
686 if (remaining > 0) {
687 IERC20(token0).approve(token0Vault, remaining);
688 IERC4626(token0Vault).deposit(remaining, address(this));
689 total4626Deposit0 += remaining;
690 }
691 }
692
693 // Transfer remaining token1 to its vault
694 if (token1Vault != address(0)) {
695 uint256 remaining = _getBalance1();
696 if (remaining > 0) {
697 IERC20(token1).approve(token1Vault, remaining);
698 IERC4626(token1Vault).deposit(remaining, address(this));
699 total4626Deposit1 += remaining;
700 }
701 }
702}

Recommendation:

We advise the code to ensure that the respective deposits are possible and to cap the deposit amounts to the maximum possible if available, ensuring EIP-4626 deposit operations succeed properly.

Alleviation:

The code has been updated to interact with the vault's in a try-catch manner, ensuring that deposit limitations do not halt any position creations.

PV1-03M: Inexistent Validation of Fee Values

Description:

The individual fee values yielded by the IFeeManager must accumulate to FEE_DIVISOR, however, this trait is not validated in the codebase.

Impact:

Although the IFeeManager instance is expected to be competently managed, a single unit deviation in the proportions can result in significant discrepancies of the vault's balance evaluation mechanisms meriting a non-informational rating.

Example:

src/Adapters/Algebra/PluginV1.sol
775// Update accruedFees0 and accruedFees1 if needed
776// This part remains here as it involves storage variables
777IFeeManager.Fee[] memory fees = IFeeManager(feeManager).getFees(address(this));
778for (uint256 j; j != fees.length; ++j) {
779 accruedFees0[fees[j].feeIdentifier] = accruedFees0[fees[j].feeIdentifier]
780 + (FullMath.mulDiv(vars._totalProtocolFees0, fees[j].feeValue, FEE_DIVISOR));
781 accruedFees1[fees[j].feeIdentifier] = accruedFees1[fees[j].feeIdentifier]
782 + (FullMath.mulDiv(vars._totalProtocolFees1, fees[j].feeValue, FEE_DIVISOR));
783}

Recommendation:

We advise this trait to be validated whenever fees are disbursed, preventing overdisbursement of fees and thus erroneous tracking of the vault's balance.

Alleviation:

The Steer Protocol team clarified that the contract integrated already performs this validation.

As introducing the same validation across two contracts within the same ecosystem is redundant, we consider this exhibit nullified.

PV1-04M: Insufficient Sanitization of Percentages

TypeSeverityLocation
Input SanitizationPluginV1.sol:
I-1: L420
I-2: L423

Description:

The referenced percentage configurations are meant to be at most 100 yet are not sanitized in this regard.

Impact:

A misconfiguration of the vault will result in unfulfillable liquidity provisions and consistent tend requirements.

Example:

src/Adapters/Algebra/PluginV1.sol
419if (params.token0Vault != address(0) || params.token1Vault != address(0)) {
420 require(params.poolLiquidityPercentage != 0);
421 poolLiquidityPercentage = params.poolLiquidityPercentage;
422}
423maximumUsedPercent = params.maximumUsedPercent;

Recommendation:

We advise proper sanitization to be imposed, ensuring they are less than the maximum value permitted so as to avoid unwarranted revert errors.

Alleviation:

Both percentage values are now correctly validated as being at most equal to 100, addressing this exhibit.

PV1-05M: Potentially Incorrect Deposit Mechanism

TypeSeverityLocation
Logical FaultPluginV1.sol:L484

Description:

The PluginV1::deposit function will not deposit to the EIP-4626 vaults that might be attached to the plugin if no positions are defined.

Impact:

The capital deployment of the system is inefficient if EIP-4626 vaults exist that can accept liquidity but the plugin has been defined with no active positions.

Example:

src/Adapters/Algebra/PluginV1.sol
483// If there are no positions, calculate positions
484if (_positions.length != 0) {
485 (uint160 sqrtRatioX96,,,) = _getPoolState(); //maybe make a function to getSqrtRatioX96()
486 (amount0Used, amount1Used) =
487 IHelper(helper).getTokensForPoolLiquidity(amount0Used, amount1Used, poolLiquidityPercentage);
488 (,, uint128[] memory liquidities) =
489 IHelper(helper).calcTokenAmounts(PRECISION, sqrtRatioX96, amount0Used, amount1Used, _positions);
490 makePositions(_positions, liquidities);
491}

Recommendation:

We advise the code to properly deploy liquidity to EIP-4626 vaults even if no positions are defined to ensure that a plugin can be created with no active liquidity positions yet with active EIP-4626 vaults.

Alleviation:

The code was updated to delegate rebalancing to swap hooks rather than deposits, rendering this exhibit no longer applicable.

PV1-06M: Rounding of Fee Distribution

Description:

The fee distribution mechanism of the PluginV1::burnAndCollect function will round each proportional disbursement of fee cuts, resulting in funds being locked in the contract as "total fees" which will never be redeemed.

Impact:

The totalFees0 and totalFees1 values meant to track the fees due for various parties will not properly represent the actual values that have been debited as the proportional calculations will round downward resulting in a potential error of fees.length per each fee processing operation.

Example:

src/Adapters/Algebra/PluginV1.sol
775// Update accruedFees0 and accruedFees1 if needed
776// This part remains here as it involves storage variables
777IFeeManager.Fee[] memory fees = IFeeManager(feeManager).getFees(address(this));
778for (uint256 j; j != fees.length; ++j) {
779 accruedFees0[fees[j].feeIdentifier] = accruedFees0[fees[j].feeIdentifier]
780 + (FullMath.mulDiv(vars._totalProtocolFees0, fees[j].feeValue, FEE_DIVISOR));
781 accruedFees1[fees[j].feeIdentifier] = accruedFees1[fees[j].feeIdentifier]
782 + (FullMath.mulDiv(vars._totalProtocolFees1, fees[j].feeValue, FEE_DIVISOR));
783}

Recommendation:

We advise the code to disburse the remainder of the cuts at the last iteration without calculating a proportion, ensuring that the disbursement of fees is done in full regardless of the underlying cut values.

Alleviation:

The code was updated to properly disburse the full amount remaining to the last fee recipient, addressing this exhibit.

PV1-07M: Inexistent Distinction of Algebra Plugins

Description:

All PluginV1 deployments will possess the same EIP-20 name and symbol rendering them indistinguishable.

Impact:

All off-chain PluginV1 representations will use the same symbol and name which enables phishing attacks and increases the chance of user mistakes.

Example:

src/Adapters/Algebra/PluginV1.sol
439__ERC20_init(
440 "STEER_ALGEBRA_HOOK",
441 "STEERAH"
442);

Recommendation:

We advise a different name and symbol to be utilized per deployment, potentially by utilizing a combination of the underlying tokens and their attached EIP-4626 vaults.

Alleviation:

The total vault count is now incorporated into the name and symbol of each PluginV1 instance, addressing this exhibit.