macro_rules! migrate_chain {
($account:expr, $program_id:expr, { $($old:ty => $new:ty : $f:expr),+ $(,)? }) => { ... };
($account:expr, $program_id:expr, payer = $payer:expr,
{ $($old:ty => $new:ty : $f:expr),+ $(,)? }) => { ... };
}Expand description
Typed multi-hop layout migration: probe-and-migrate each declared
hop in declaration order, so ONE call heals an account from ANY
declared starting version to the newest, the chain Quasar’s
pairwise Migration<From, To> cannot express in one instruction.
// In place (every hop's target must already fit the allocation):
let hops = hopper::migrate_chain!(account, ctx.program_id(), {
VaultV1 => VaultV2: widen,
VaultV2 => VaultV3: add_flag,
});
// With ONE up-front grow to the largest hop target, rent topped up
// from `payer` (see `ensure_fits_with_rent`):
let hops = hopper::migrate_chain!(account, ctx.program_id(), payer = payer_view, {
VaultV1 => VaultV2: widen,
VaultV2 => VaultV3: add_flag,
});Each hop probes the header for a fully-valid $old identity and,
only then, runs migrate_layout (which re-verifies under its own
borrow and carries the owner+writable security gate). An account
already at a later hop’s source version simply skips the earlier
hops; an account matching NO hop is left untouched and the chain
returns 0, the caller’s subsequent typed load rejects foreign
layouts exactly as before, so the chain is a healing pass, not a
validator. Evaluates to the number of hops applied (u32).
The expansion uses ?, so the surrounding function must return
Result<_, ProgramError> (or a compatible error).