Organization
- @lista-dao
Engagement Type
Cantina Solo
Period
-
Repositories
Researchers
Findings
High Risk
1 findings
1 fixed
0 acknowledged
Low Risk
1 findings
1 fixed
0 acknowledged
Informational
3 findings
0 fixed
3 acknowledged
High Risk1 finding
Incorrect caller propagation in withdrawFor prevents migration of BTCB and wBETH positions
Severity
- Severity: High
Submitted by
slowfi
Description
The function
withdrawForfrom contractInteractionforwardsmsg.senderas thecallerargument into_withdraw.For collateral types without an associated Helio provider, such as BTCB and wBETH,
_withdrawenforces thatcaller == participant. However, during the migration flow,withdrawForis invoked by thePositionMigratorcontract on behalf of the user, somsg.senderis the migrator rather than the position owner.As a result, the direct
Interactionwithdrawal path for BTCB and wBETH always reverts withInteraction/Caller must be the same address as participant, making these in-scope collateral types non-migratable through the intended flow.At the same time, because
withdrawForonly checks that the caller is the migrator and receives the user address as an input parameter, the function remains callable by the migrator for arbitrary participants, but the internal caller model is still incompatible with directInteractioncollaterals. This breaks the migration path specifically for assets that rely onInteractionrather than an external provider for collateral release.Proof of Concept
The following test shows that a BTCB position cannot be migrated because
withdrawForpropagates the migrator ascaller, causing_withdrawto revert in the direct collateral path:function test_poc_fork_migratorCannotWithdrawDirectInteractionCollateral() public { uint256 debtBefore = interaction.borrowed(BTCB, USER_BTCB); uint256 collateralBefore = _cdpCollateral(USER_BTCB); assertGt(debtBefore, 0, "expected active BTCB debt on the fork"); assertGt(collateralBefore, 0, "expected active BTCB collateral on the fork"); assertEq(interaction.helioProviders(BTCB), address(0), "expected direct Interaction collateral"); vm.prank(USER_BTCB); vm.expectRevert(bytes("Interaction/Caller must be the same address as participant")); migrator.migratePosition(btcbMigrationParams, false, 0); assertEq(interaction.borrowed(BTCB, USER_BTCB), debtBefore, "debt changed despite revert"); assertEq(_cdpCollateral(USER_BTCB), collateralBefore, "collateral changed despite revert"); assertEq(IERC20(BTCB).balanceOf(address(migrator)), 0, "migrator should not receive BTCB");}Recommendation
Consider to adapt
withdrawForso that the migration-specific flow does not reuse the regular caller assumptions enforced by_withdrawfor directInteractioncollaterals.Consider to introduce a dedicated migration withdrawal path, or to pass the effective participant context in a way that remains compatible with BTCB and wBETH withdrawals while preserving the intended access control guarantees.
Low Risk1 finding
Migration reverts for positions with zero CDP debt
Description
The function
migratePositionfrom contractPositionMigratoralways initiates the migration flow by calling:MOOLAH.flashLoan(LISUSD, cdpDebt, data);where
cdpDebtis obtained from the user’s CDP position.If a user has zero outstanding debt,
cdpDebtwill be equal to zero. In this case, the migration still attempts to execute a flash loan with a zero amount.Depending on the implementation of
Moolah.flashLoan, zero-amount flash loans are typically not supported and may revert. As a result, users with fully repaid CDP positions cannot migrate their collateral using this flow, even though they may still hold collateral in the system.This creates an unintended restriction where positions without debt cannot be migrated through the provided mechanism.
Recommendation
Consider to handle the zero-debt case explicitly by bypassing the flash loan flow and directly executing the collateral withdrawal and supply steps.
Alternatively, consider to validate that
cdpDebt > 0before initiating the migration and provide a clear revert reason or separate migration path for zero-debt positions.
Informational3 findings
Authorization trust model assumption
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
slowfi
Description
The migration flow requires users to authorize the
PositionMigratorcontract viaMoolah.setAuthorization(migrationToolAddr, true)before executing the migration.The
PositionMigratorcontract is implemented as an upgradeable UUPS proxy, where upgrades are controlled by an account holdingDEFAULT_ADMIN_ROLEthrough the_authorizeUpgradefunction.As a result, users grant authorization to a proxy address whose implementation can change over time, rather than to a fixed and immutable contract. This proxy is also set as the privileged migrator in the CDP system, enabling it to repay debt and withdraw collateral on behalf of users.
Since the authorization is tied to the proxy address, it remains valid across future upgrades. Any new implementation introduced by the admin would retain the previously granted permissions without requiring additional user approval.
Recommendation
Consider to clearly communicate this behavior to users, highlighting that the authorization is granted to an upgradeable contract controlled by an administrative role.
Additionally, consider to provide mechanisms to limit the scope or duration of the authorization, or allow users to easily revoke permissions after migration.
Lista DAO: Acknowledged. The
DEFAULT_ADMIN_ROLEforPositionMigratorproxy will be assigned to aTimelockcontract with a 1-day delay, ensuring that any implementation upgrade is subject to a public waiting period before execution. Additionally, users will be able to revoke their authorization to the migrator directly through the frontend after migration is complete.Cantina Managed: Acknowledged by Lista DAO team.
releaseFor introduces inconsistent LP accounting assumptions when withdrawing on behalf of users
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
slowfi
Description
The function
releaseForfrom contractSlisBNBProviderenables the migrator to withdraw LP-backed collateral on behalf of a user by calling:_withdrawLp(_account, _amount);This introduces a different execution model compared to the original design of
_withdrawLp, where the account being withdrawn was assumed to be the one being properly synchronized before any burn or accounting updates.In the migration flow,
msg.senderis the migrator, while_accountrepresents the user. As a result, the withdrawal logic operates on user balances that may not have been recently synchronized against mutable parameters such asexchangeRateanduserLpRate.If these parameters have changed since the last user interaction, the burn logic may use updated rates against stale stored balances. This can lead to reverts or inconsistent LP accounting depending on how much the rates have drifted.
Recommendation
Consider to ensure that user LP balances are explicitly synchronized within the migration withdrawal path before applying any burn or accounting logic.
Additionally, consider to avoid relying on external synchronization processes to guarantee correctness of on-chain migration flows.
Lista DAO: Acknowledged. Whenever the
exchangeRatechanges, backend automatically invokessyncUserLpfor affected users, ensuring that both user LP balances and reserve LP accounting are up to date prior to any migration withdrawal.Cantina Managed: Acknowledged by Lista DAO team.
BNB migration fallback uses the CDP side BNB amount instead of the released slisBNB amount
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
slowfi
Description
The function
onMoolahFlashLoanfrom contractPositionMigratorhandles BNB-backed migrations by releasing CDP collateral throughreleaseInTokenFor, which returns the collateral in the form ofslisBNB.However, if the target Moolah market has no collateral provider configured, the fallback branch supplies
data.collateralAmountas the collateral amount. This value represents the original BNB-side amount from the CDP position, not the actual amount ofslisBNBreceived by the migrator after the conversion.These two amounts are not guaranteed to match. As a result, the fallback branch may attempt to supply more
slisBNBthan the migrator actually holds, causing the migration to revert.This issue may remain latent while the target
slisBNBmarket is configured with the expected collateral provider, since in that case the provider branch is used instead of the fallback path. However, the fallback logic is still inconsistent with the asset amount actually received in the BNB migration flow.Recommendation
Consider to use the actual
slisBNBamount released byreleaseInTokenForwhen executing the no-provider fallback branch.Additionally, consider to avoid reusing the original CDP-side BNB amount after the collateral has already been converted into
slisBNB.Lista DAO: Acknowledged. We will ensure that the
slisBNBProvideris configured for all Moolah markets where the collateral token isslisBNB, guaranteeing that the fallback branch is never reached in theBNBmigration flow.Cantina Managed: Acknowledged by Lista DAO team.