Summary
The vault's active adapter is static, changed only manually via set_adapter/migrate_adapter. Existing routing logic (selectBestVault in packages/stellar-sdk-helpers/src/routing.ts, recommendedVaultId in packages/api-core/src/vaults.ts) only steers new deposits toward the best available option at deposit time; it never moves capital already deposited. migrate_adapter (packages/contracts/vault/src/lib.rs, shipped in #464/#507) already exists and atomically moves the vault's entire position to a new adapter in one transaction, with a slippage-bounded value-preservation check, but nothing calls it automatically. Today an admin has to notice a rate change and trigger it by hand.
Motivation
Automatic routing to the best available yield, without requiring a human to notice a rate change and manually trigger a migration, is the core value proposition of a yield aggregator. Without it, Meridian's yield allocation is manual, capital doesn't actually move to wherever yield is best unless someone is watching and acting.
Proposed Solution
An off-chain keeper (same shape as the existing Blend accrue keeper in #466/PR #484: a scheduled cron endpoint with retry handling and structured failure reporting) that periodically compares live rates across the protocols this vault's adapters can target (Blend, DeFindex), and calls migrate_adapter when the improvement clears a configured threshold, avoiding needless churn from chasing tiny, fee-losing differences.
This does not need any new authorization mechanism. migrate_adapter already exists, is already admin-gated, and, critically, already never touches any individual depositor's mUSDC balance, it operates on the vault's aggregate position, denominated in shares that are priced against total vault value, not reissued per protocol. So triggering it automatically benefits every depositor simultaneously with a single admin/keeper-signed call, no per-user consent, delegation, or signature is needed, because no individual user's tokens are ever moved.
(An earlier version of this issue proposed a MeridianRouter contract with per-user delegated authorization, letting a keeper move an individual depositor's position between separate vault deployments on their behalf. That approach ran into a hard constraint: Stellar's token contracts require the token holder's own signature for any transfer or burn, with no allowance/delegation primitive, so a keeper could never actually act without the depositor present, defeating the point. It also assumed multiple vault deployments would exist per protocol, which was never the intended design. That work was reverted; MeridianRouter no longer exists in the codebase.)
Scope
| Field |
Value |
| Area |
API |
| Protocol affected |
Both |
| Network |
testnet |
| Breaking change? |
No (additive: a new scheduled job, no contract changes needed) |
Alternatives Considered
Manual admin-triggered migrate_adapter (today's state): works, but depends on a human noticing a rate change and acting, which is exactly the dependency this issue exists to remove.
Acceptance Criteria
Summary
The vault's active adapter is static, changed only manually via
set_adapter/migrate_adapter. Existing routing logic (selectBestVaultinpackages/stellar-sdk-helpers/src/routing.ts,recommendedVaultIdinpackages/api-core/src/vaults.ts) only steers new deposits toward the best available option at deposit time; it never moves capital already deposited.migrate_adapter(packages/contracts/vault/src/lib.rs, shipped in #464/#507) already exists and atomically moves the vault's entire position to a new adapter in one transaction, with a slippage-bounded value-preservation check, but nothing calls it automatically. Today an admin has to notice a rate change and trigger it by hand.Motivation
Automatic routing to the best available yield, without requiring a human to notice a rate change and manually trigger a migration, is the core value proposition of a yield aggregator. Without it, Meridian's yield allocation is manual, capital doesn't actually move to wherever yield is best unless someone is watching and acting.
Proposed Solution
An off-chain keeper (same shape as the existing Blend accrue keeper in #466/PR #484: a scheduled cron endpoint with retry handling and structured failure reporting) that periodically compares live rates across the protocols this vault's adapters can target (Blend, DeFindex), and calls
migrate_adapterwhen the improvement clears a configured threshold, avoiding needless churn from chasing tiny, fee-losing differences.This does not need any new authorization mechanism.
migrate_adapteralready exists, is already admin-gated, and, critically, already never touches any individual depositor's mUSDC balance, it operates on the vault's aggregate position, denominated in shares that are priced against total vault value, not reissued per protocol. So triggering it automatically benefits every depositor simultaneously with a single admin/keeper-signed call, no per-user consent, delegation, or signature is needed, because no individual user's tokens are ever moved.(An earlier version of this issue proposed a
MeridianRoutercontract with per-user delegated authorization, letting a keeper move an individual depositor's position between separate vault deployments on their behalf. That approach ran into a hard constraint: Stellar's token contracts require the token holder's own signature for any transfer or burn, with no allowance/delegation primitive, so a keeper could never actually act without the depositor present, defeating the point. It also assumed multiple vault deployments would exist per protocol, which was never the intended design. That work was reverted;MeridianRouterno longer exists in the codebase.)Scope
Alternatives Considered
Manual admin-triggered
migrate_adapter(today's state): works, but depends on a human noticing a rate change and acting, which is exactly the dependency this issue exists to remove.Acceptance Criteria
migrate_adapteronly when the improvement clears a configured minimum threshold, to avoid fee-losing churn from marginal differencesmax_slippage_bpson everymigrate_adaptercall, never10_000(unlimited) in automated operationmigrate_adapteralready documentsmigrate_adapteritself reverts (e.g. slippage exceeded)