型recipes
three high-leverage patterns where continuous-time settlement unlocks something discrete-time can't do. all three follow the same skeleton: register protocol → cpi tick → use elapsed_ms in your math.
continuous funding
Most direct application. Replace your protocol's hourly funding cron with a per-block accrual using the kagura-vault formula.
1let tick = kagura_core::cpi::record_tick(cpi)?.get();2let accrued = position3 .checked_mul(rate_bps).checked_mul(tick.elapsed_ms)4 .checked_div(10_000 * 31_536_000_000)?;5balance += accrued;Use case: perp dex funding, lending interest, vesting unlock, fee streaming.
block-level liquidation guard
On every solana block your protocol can read margin health and act on it. kagura-core gives you the verified wall-clock delta. Pair with a price oracle and trigger deleverage before the cex even sees the wick.
1let tick = kagura_core::cpi::record_tick(cpi)?.get();2let mark = pyth::get_price(&ctx.accounts.pyth_feed)?;3 4let health = compute_health(position, mark);5if health < THRESHOLD && tick.elapsed_ms < MAX_RESPONSE_MS {6 deleverage(position, partial_close)?;7 emit!(ProtectiveLiquidation { tick: tick.tick_number, health });8}real-time options pricing
Continuous time eliminates the discrete gamma cliff at expiry. Premium accrues and decays smoothly between ticks instead of jumping at midnight UTC.
1let tick = kagura_core::cpi::record_tick(cpi)?.get();2 3// time_to_expiry decreases by elapsed_ms every call4let new_tte = state.time_to_expiry_ms.saturating_sub(tick.elapsed_ms as u64);5 6// black-scholes premium recomputed continuously7let premium = bs::call_premium(spot, strike, vol, new_tte, rate)?;8state.time_to_expiry_ms = new_tte;9state.last_premium = premium;continuous vesting
Token vesting where unlocked amount is exactly proportional to wall-clock elapsed since grant, attested on-chain.
1let tick = kagura_core::cpi::record_tick(cpi)?.get();2let total_elapsed = (tick.now_unix_ms - grant.start_ms) as u64;3let unlocked = grant.amount.min(4 grant.amount * total_elapsed.min(grant.duration_ms) / grant.duration_ms,5);6let claimable = unlocked.saturating_sub(grant.claimed);