Skip to content

Make slots_per_year configurable instead of a constant - #122

Merged
mikemaccana merged 2 commits into
mainfrom
claude/solana-slot-times-docs-enb21m
Aug 14, 2026
Merged

Make slots_per_year configurable instead of a constant#122
mikemaccana merged 2 commits into
mainfrom
claude/solana-slot-times-docs-enb21m

Conversation

@mikemaccana

Copy link
Copy Markdown
Collaborator

Summary

Move slots_per_year from a hardcoded constant to a configurable parameter in reserve configuration. This allows reserves to adapt to protocol changes in slot time without requiring code updates, preventing borrowers from being charged incorrect wall-clock rates when Solana lowers the slot time.

Key Changes

  • Anchor lending program:

    • Remove SLOTS_PER_YEAR constant from constants.rs
    • Add slots_per_year: u64 field to ReserveConfig struct
    • Update ReserveConfig::validate() to reject zero values
    • Modify interest accrual calculations to use config.slots_per_year instead of the constant
    • Add SLOTS_PER_YEAR test constant to common/mod.rs and update default_config()
    • Add comprehensive tests: rejects_zero_slots_per_year and slots_per_year_scales_the_per_slot_rate
  • Quasar lending program:

    • Remove SLOTS_PER_YEAR constant from constants.rs
    • Add slots_per_year: u64 field to Reserve state struct
    • Add new update_slots_per_year instruction (discriminator 12) for owner-only retuning
    • Update initialize_reserve to accept slots_per_year parameter
    • Modify borrow_rate_per_slot() and accrue_factor() to accept slots_per_year parameter
    • Add test retuning_slots_per_year_rescales_accrual demonstrating the feature
    • Update README and CHANGELOG with configuration guidance
  • Documentation updates:

    • Clarify in multiple constants.rs files that slot-based staleness windows tighten automatically as protocol lowers slot time
    • Add guidance in READMEs about reading current slot time from cluster and keeping reserves in sync
    • Update CHANGELOGs with detailed rationale and testing information

Implementation Details

  • The update_slots_per_year instruction accrues interest at the old rate before storing the new divisor, ensuring slots already elapsed are charged at the rate that was in force for them
  • Validation rejects zero to prevent division by zero in rate calculations
  • Test fixtures use 78,840,000 slots/year (2.5 slots/second × 60 × 60 × 24 × 365), representing a 400ms slot time
  • The change is backward-compatible for new reserves; existing reserves would need migration

https://claude.ai/code/session_01TCc6nwbMzXsG3fEom23KXW

claude added 2 commits August 14, 2026 02:43
Turning a reserve's APR into the per-slot rate interest actually accrues at
needs a slots-per-year divisor, and that divisor is the cluster's slot time in
disguise. Both ports carried it as SLOTS_PER_YEAR = 78_840_000, fixed at a
400ms slot. Solana is lowering the slot time, and a lower one raises the
wall-clock rate every borrower pays with no line of either program changing
and nothing in the config to show for it.

Anchor: ReserveConfig gains slots_per_year, validate() rejects zero, and the
owner retunes through the existing update_reserve_config.

Quasar: Reserve gains slots_per_year, initialize_reserve takes it, and a new
owner-only update_slots_per_year (discriminator 12) retunes it. That handler
accrues at the old figure before storing the new one, so slots already elapsed
are charged at the rate that was in force for them rather than repriced.

Also drop the "at 400ms/slot" gloss from the price-staleness comments in
lending, prop-amm and perpetual-futures. The window is counted in slots on
purpose; what it comes to in seconds follows the cluster and tightens on its
own.
`funding_rate_per_slot` was set at initialize_pool and never again. The rate is
quoted per slot, so what a position costs per hour depends on the cluster's
slot time as well as on the rate. Solana lowers the slot time over time, and a
pool that outlives a reduction charges the heavier side more per hour than it
was set up to, with no way to correct it short of a new pool.

Add set_funding_rate to both ports (Quasar discriminator 7), owner-only. It
advances the funding index at the old rate before storing the new one, so slots
already elapsed are charged at the rate that was in force for them rather than
repriced by the new one.

The tests pin that settlement rather than just the write: a position held
across a retune to double the rate pays one window at the old rate and one at
the new, which is exactly 1.5x what a position opened afterwards pays over a
single window. Comparing two windows in the same pool cancels the size and
price scaling, so the assertion is on the rates alone.
@mikemaccana
mikemaccana merged commit e6fc9b1 into main Aug 14, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants