Skip to content

[Feature] Implement real rate sources for the migration keeper #511

Description

@collinsezedike

Summary

The migration keeper added in #469 (packages/stellar-sdk-helpers/src/migration-keeper.ts) compares rates across a vault's candidate protocol adapters and calls migrate_adapter when a candidate clears a configured improvement threshold. Rate comparison is deliberately pluggable via a RateSourceFn, and the shipped default always returns null ("rate unknown"), so the keeper currently never migrates anything. This issue is the actual rate-source implementation that default is standing in for.

Motivation

Without a real rate source, the migration keeper is inert: it has the full mechanism (discovery, threshold check, slippage-bounded migrate_adapter call, retry and deadline handling, structured failure reporting) but no way to decide when a migration is actually warranted. This is the last piece needed for the vault to genuinely auto-route to the best available yield, the core value proposition #469 exists for.

Proposed Solution

Neither adapter contract exposes a ready-made comparable rate today:

  • BlendAdapter (packages/contracts/blend-adapter/src/lib.rs) exposes total_assets() (a point-in-time USDC value) and, via get_pool(), the underlying pool's raw Reserve data: utilization (data.b_supply/d_supply), and the kinked-curve parameters r_base/r_one/r_two/r_three/util/max_util/reactivity. A real rate source needs to reimplement Blend's three-slope interest rate formula off-chain from these inputs.
  • DefindexAdapter exposes get_asset_amounts_per_shares(), a share-price snapshot. Deriving an annualized rate needs a second sample over time; there's no history stored anywhere for this today, this likely needs either a persistence layer (out of scope for a stateless keeper as currently designed) or a different approach, e.g. reading DeFindex's own reported strategy APY if it exposes one.

Implement RateSourceFn (the type is already defined in migration-keeper.ts) for both protocols and wire it into runMigrationKeeper's default, replacing the always-null stub.

Scope

Field Value
Area SDK
Protocol affected Both
Network testnet
Breaking change? No (fills in an existing pluggable seam, no signature changes)

Alternatives Considered

Leaving the default as-is indefinitely: safe (the keeper simply never migrates), but means #469's actual stated goal, automatic yield routing, never happens in practice regardless of how the rest of the mechanism is tested.

Acceptance Criteria

  • Blend rate source correctly computes the current supply rate from Reserve data, verified against Blend's own real testnet reserve behavior, not just internal consistency
  • DeFindex rate source produces a comparable annualized rate, with an explicit answer to where the time-series sample lives (or an alternative approach if DeFindex exposes a usable rate directly)
  • Both are wired in as runMigrationKeeper's default RateSourceFn, replacing the always-null stub
  • The migration keeper genuinely triggers a migrate_adapter call under real conditions (verified on testnet, not just against injected fakes in unit tests), this is the acceptance bar for [Feature] Automatic protocol-yield routing via migrate_adapter keeper #469 itself being functionally done, not just this issue
  • Existing migration keeper test suite still passes; new tests cover the rate formulas themselves against known inputs/outputs

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignenhancementNew feature or requesthardComplex implementation spanning multiple packages or involving Soroban contractsprotocol-integrationInvolves reading from or writing to Blend or DeFindex on-chainsdkInvolves Blend or DeFindex SDK helpers in packages/stellar-sdk-helperssorobanInvolves Soroban smart contract invocations or Soroban RPC callsstellarRequires knowledge of Stellar concepts (accounts, transactions, assets)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions