Overview
AaveMigrator moves a leveraged Aave position from one subvault to another without unwinding it. It handles one collateral and one debt asset, and moves the position in small steps so that neither subvault’s health factor drops below a set minimum.
Each step, the target subvault borrows the debt asset and hands it to the source subvault. The source repays that amount of debt and withdraws collateral of equal value straight to the target, and the target supplies it back to Aave. Each side’s equity (collateral minus debt) stays roughly the same, and the contract checks the change against a tolerance. The debt and collateral end up in the target.
One AaveMigrator instance serves one source subvault, one target subvault and one collateral/debt pair. All of these are set in the constructor and cannot be changed.
Constructor parameters
The constructor reads the collateral aToken, debt aToken and variable debt token from the pool. All values are exposed as public immutables.
maxAllowedError_ uses the Aave oracle’s base currency. On USD markets that has 8 decimals, so 1e6 is $0.01.
Requirements
- The
AaveMigratorcontract calls both subvaults throughSubvault.call, so it must hold the verifierCALLER_ROLEon the vault. - It always sends an
ONCHAIN_COMPACTverification payload, so the calls below must be allowed withallowCallsin each subvault’s verifier. A Merkle-only permission is not enough.
- The target must already have borrowing capacity in Aave, because every step starts with a borrow from the target. In practice, the target needs some collateral supplied in advance.
- If the position relies on an Aave e-mode category, enable the same category on the target first. Otherwise its borrowing capacity and health factor are lower.
Function: migrate
percentageD6 of the source’s current debt, together with collateral of the same value, to the target.
Execution steps:
- Reverts with
InvalidMaxUtilizationD18orInvalidPercentageD6if an argument is out of range, and withAlreadyMigratedif the migrator has finished or been killed. - Calls
calculateStepsto get the number of steps, the debt and collateral per step and the expected remaining source debt. - Checks that both health factors are at or above their minimums and records each subvault’s equity.
- Runs each step:
- The target borrows
debtPerStepof the debt asset at variable rate. - The target transfers it to the source.
- The source approves the pool and repays
debtPerStep. - The source withdraws
collateralPerStepof collateral, sent straight to the target. - The target approves the pool and supplies the collateral.
- The target borrows
- Checks both health factors again and adds up how much each subvault’s equity changed. Reverts with
CumulativeErrorTooHighif the total exceedsmaxAllowedError. - Reverts with
TooHighDeviationif the source’s remaining debt differs from the expected amount by more thanmaxAllowedErrorin value. - If the source’s remaining debt is worth
maxAllowedErroror less, marks the migration as finished and emitsMigrated. Otherwise emitsPartiallyMigrated, and the owner can callmigrateagain.
approve, the subvault resets any existing allowance to 0, which some tokens require.
Function: calculateSteps
migrate uses:
debtToMigrateispercentageD6of the source’s variable debt. Reverts withInsufficientDebtif it is belowminAllowedMigratingDebt.- Reverts with
InvalidDebtAfterMigrationif the leftover debt is worth more thanmaxAllowedErrorbut is belowminAllowedMigratingDebt. That would leave debt too large to ignore and too small to migrate later. - The largest step is the target’s available borrowing capacity times
maxUtilizationD18, converted to the debt asset. It is also capped by the debt asset liquidity available in the Aave reserve. iterationsisdebtToMigratedivided by the largest step, rounded up. Reverts withTooManyIterationsif it exceedsMAX_ALLOWED_ITERATIONS(30) or no borrowing is possible.debtPerStepisdebtToMigrate / iterations, andcollateralPerStepis collateral of the same value at oracle prices. Reverts withNotEnoughCollateralif the source does not hold enough collateral for all steps.
migrate to choose a maxUtilizationD18 and percentageD6 that fit within 30 steps.
Function: checkHFAndGetEquity
HealthFactorIsTooLow if holder’s Aave health factor is below the minimum. Otherwise returns its equity, total collateral minus total debt, in the Aave base currency.
Function: kill
Owner only. Permanently disables migrate and emits Killed. Use it to retire the migrator, for example after a partial migration that you do not want to continue.
Events
cumulativeError is the total equity change across both subvaults, in the Aave base currency.
Errors
Notes
- Assets move between the subvaults through Aave, not through
Vault.pushAssetsorVault.pullAssets. TheRiskManagersubvault balances are not updated by a migration. - The vault’s total equity stays the same within
maxAllowedError, so a migration should not change the vault’s reported value. Only the split between subvaults changes.