KAGURA 神楽

KAGURA is a continuous-time DeFi primitive on Solana. Two on-chain Anchor programs: a tick-attestation registry, and a USDC funding vault that compounds yield every block instead of every hour.

contact: hello@kagura.network
神楽KAGURA/ docs
v0.1.0 · pre-deployment← back to sitegithub →
concepts·tick attestation

tick attestation

kagura-core is a tiny registry. it holds a tick counter and a last-tick timestamp per registered protocol, and returns elapsed_ms to its callers via cpi.

the shape of an attestation

Every call to record_tick returns a TickResult containing three fields:

fieldtypemeaning
now_unix_msi64the solana clock at the moment of attestation, in milliseconds since unix epoch.
elapsed_msu32the wall-clock delta since the last tick on this protocol, clamped to u32::MAX.
tick_numberu64monotonic counter of attestations for this protocol, starting at 1.

the account graph

Two seeded PDAs participate in any tick:

state.rs (excerpt)rust
1#[account]2pub struct KaguraConfig {3    pub authority: Pubkey,4    pub total_protocols: u64,5    pub total_ticks: u128,6    pub created_at: i64,7    pub bump: u8,8}9 10#[account]11pub struct ProtocolRegistration {12    pub authority: Pubkey,13    pub name: String,           // <= 32 chars14    pub tick_interval_ms: u32,  // 50..=60_00015    pub last_tick_unix_ms: i64,16    pub total_ticks: u64,17    pub paused: bool,18    pub created_at: i64,19    pub bump: u8,20}

KaguraConfig is global (one per program). Its only job is to count how many protocols are registered and how many ticks have ever been recorded. PDA seeds:

seedsrust
1config:    [b"kagura-config"]2protocol:  [b"kagura-protocol", authority.key().as_ref()]

lifecycle

  1. once per network: initialize_config seeds the global config pda. The first caller becomes the config authority.
  2. once per protocol: register_protocol(name, tick_interval_ms) creates a registration pda. tick_interval_ms must be in [50, 60_000].
  3. continuous: record_tickis called on a schedule (by the operator's ticker bot or, soon, by anyone). Each call updates last_tick_unix_ms, increments total_ticks, and emits a TickEmitted event.
  4. at any time: set_paused(true|false) is callable by the protocol authority. While paused, record_tick errors with ProtocolPaused and consumers must handle the gap.

errors

errorraised when
Unauthorizedsigner does not match protocol.authority.
InvalidTickIntervaltick_interval_ms outside [50, 60_000].
ProtocolPausedrecord_tick called while paused = true.
ClockRegressionthe solana clock returned now < last_tick_unix_ms (should never happen on mainnet; defensive check).
MathOverflowtotal_ticks arithmetic overflowed (would require a u64 of ticks; ~50ms × 2^64 ≈ 29 trillion years).

events

kagura-core emits one event:

state.rsrust
1#[event]2pub struct TickEmitted {3    pub protocol: Pubkey,4    pub tick_number: u64,5    pub now_unix_ms: i64,6    pub elapsed_ms: u32,7}

Indexers should subscribe to this event to build dashboards, alerts, or chainwide tick charts. Every kagura-vault::tick_funding call produces one of these.

the cpi contract

For consumer programs, the cpi shape is fixed. See integration/cpi for the full Anchor cpi example. The short version:

caller programrust
1use kagura_core::cpi::accounts::RecordTick;2use kagura_core::program::KaguraCore;3 4let cpi = CpiContext::new(5    ctx.accounts.kagura_core_program.to_account_info(),6    RecordTick {7        authority: ctx.accounts.authority.to_account_info(),8        config:    ctx.accounts.kagura_config.to_account_info(),9        protocol:  ctx.accounts.kagura_protocol.to_account_info(),10    },11);12let tick = kagura_core::cpi::record_tick(cpi)?.get();