Skip to main content
The fixed-rate vault calculates underlying asset value per share from the configured APR and elapsed time. Interest does not increase the number of shares in your wallet. It increases the rate used to convert shares into assets. The default APR is 2% annually. The operating target is a regular update every 24 hours for USDT (6 decimals) and every hour for JPYC (18 decimals). This page explains the architecture and operating model. For addresses, approval targets, function calls, and testing, use the fixed-rate vault integration guide.
These intervals are operating targets for the off-chain keeper. Contracts do not automatically select a cadence from decimals or execute themselves when a timer expires: the confirmed rate changes only after a successful keeper poke transaction. Kairos testnet has no automated keeper, so coordinate rate-update and redemption tests with the SuperEarn team.

Funds and contract architecture

Users call Router to deposit and submit withdrawal requests to Queue. The path that invests assets is separate from the path that determines the rate applied to user shares. The diagram summarizes asset movement. Router calls Teller, which instructs Vault to mint shares. The keeper moves and recalls investment assets through Gateway. Gateway holds CustomVault shares and temporarily holds underlying during deposits. Normal CustomVault withdrawals pay underlying directly back to Vault. The user deposit call path is VedaRouter → Teller → Vault. The rate-update path is VedaKeeper.poke → Controller.poke → Accountant.updateExchangeRate. Manager and role configuration constrain the calls allowed to move Vault assets. The fixed rate does not copy strategy NAV (net asset value). Controller follows the configured APR formula even when strategy returns change. The operator must manage assets and liquidity to support redemptions at that rate. Increasing the rate does not itself create tokens or investment profits.

How fixed APR is applied

The target APY is 2% (governance-configurable). The rate follows a fixed curve anchored to absolute time, so a keeper poke sets:
  • anchorRate, anchorTime: the fixed reference point (last rate change or deployment), not the previous rate.
  • ratePerSecond: a per-second compounding factor (27-decimal fixed point) derived from the target APY, where ratePerSecond^(365 days) = 1 + APY. Computed with integer exponentiation (rpow); fractional rate units are discarded (rounded down).
  • now - anchorTime: absolute seconds since the anchor — the only time input.
  • lastPokeAt: the last successful push timestamp; it gates the keeper’s minimum interval but does not change the rate value.
Because the rate depends only on absolute elapsed time, poke cadence no longer changes the realized APY: a late or irregular poke lands the confirmed rate on the exact same curve, with no drift. aprBps() / targetApyBps() read the effective APY back on-chain (2%). The rate is expressed in base units of Accountant’s base asset. Accountant.decimals() equals the base token’s decimals. A JPYC-based vault’s rate has 18 decimals; a USDT-based vault’s rate has 6 decimals. This is separate from both vaults having 18-decimal shares. In both cases, convert a share amount to asset value using floor(shares × rate / 10^18). The result is in the corresponding base asset’s smallest units. The default target APY is 2%, but governance can change it via setRatePerSecond. If time has elapsed, it first settles that period at the old rate, then re-anchors and applies the new rate prospectively, so a change never retro-applies. Governance changes can therefore update the rate outside the regular schedule. Realized annual growth equals the configured APY regardless of the poke schedule; integer rounding only shaves sub-unit amounts at the floor of the rate.

How stepped updates differ from PPS

In the existing variable-yield vault, PPS is calculated from free funds and share supply at the observation time. After strategy profits are reported, the calculation reflects profit unlocking based on lastReport and lockedProfitDegradation. PPS can therefore change with time without another report transaction. The unlock duration is configurable. This does not mean every unreported strategy profit is immediately recognized. See valuation, harvest and accrual reporting. In the fixed-rate vault, Accountant.getRate() returns a stored number. Time passing, a read, a deposit, or a withdrawal request does not itself increase that number. A successful scheduled poke recognizes the elapsed period in a single update.
The confirmed rate remains flat from 0 to 24 hours and rises at the 24-hour and 48-hour checkpoints. A separate projected rate grows between checkpoints.

The solid line is the confirmed rate; the dashed line is the projected rate if poked at that time. This chart and the table below use 2% APR, a 6-decimal asset, an initial rate of 1,000,000, and a 24-hour cadence. JPYC uses a shorter update cadence.

This is not separate interest accounting from each user’s deposit timestamp. A deposit uses the current confirmed rate, and Router does not automatically poke. The next checkpoint applies to all shares, including shares deposited shortly before that update. Do not present interest prorated to an individual user’s holding time as identical to confirmed contract value. Create new requests with safeUpdateAtomicRequest, the official Accountant, and discount=0. It reads and stores the confirmed rate at request execution; the call does not itself poke. Cancel using ordinary updateAtomicRequest with a zero amount. A withdrawal request also stores its own atomicPrice. A later rate increase does not automatically increase an existing request’s payout. To request at a new rate, cancel the unwanted existing request and submit another using the latest confirmed rate.

Default keeper operating rules

Use an update cadence appropriate to the base asset’s precision. These intervals are not withdrawal waiting periods or settlement guarantees: rate updates and withdrawal settlement are separate jobs. The hourly and 24-hour cadences are operating schedules; the current contracts do not automatically enforce updates at those intervals. minInterval sets a minimum allowed spacing, not an automatic timer. A shorter cadence requires both Controller’s minInterval and Accountant’s minimumUpdateDelayInSeconds to allow updates at that frequency. Higher precision does not bypass those restrictions, pauses, or rate-change bounds. Confirm a price change from a successful on-chain checkpoint, not the timetable. The schedule does not promise execution at a particular UTC time or withdrawal completion within that interval. If a poke violates Accountant bounds or pause conditions, the whole transaction reverts and both the rate and checkpoint timestamp remain unchanged. After prolonged downtime, accumulated growth may exceed the allowed change and require operator attention. Do not display a failed update as recognized interest. Settlement does not automatically poke. To test a redemption at an updated rate, use successful poke → read latest rate → user submits request → settlement. Existing requests can still settle at their own fixed prices; waiting for a rate update does not protect them from settlement.

USDT: 6 decimals and a 24-hour cadence

The confirmed rate is a function of absolute elapsed time, so poke cadence does not change the realized APY — poking hourly, every 24 hours, or catching up after a long gap all land the rate on the same curve. The table uses an initial rate of 1_000_000, a constant 2% target APY, and 365 days without changes. Every cadence reaches the same confirmed rate (here 1_000_000 → 1_019_999); the “first poke” column differs only in step size, not in the annual total. At this 6-decimal scale, integer rounding is a one-time sub-unit floor at each read, not a cadence-dependent loss: a one-unit increase first appears about 1,593 seconds after the anchor. A successful poke advances lastPokeAt even when the rounded rate does not increase. The 24-hour default is chosen for gas and a visible daily step, not to protect the APY.

JPYC: 18 decimals and an hourly cadence

For JPYC with a rate near 10^18, the sub-unit discarded at each read is below 10^-18 JPYC per share, so short intervals lose effectively nothing. At 10^18 and a 2% target APY, the first update after exactly one hour adds 2_260_576_447_357 rate units (0.000002260576447357 JPYC per share). The default operating target is one poke per hour; even 10-minute intervals are feasible precision-wise, but update restrictions, gas, and operating costs still apply. Because the curve is anchored to absolute time, updating hourly, every 10 minutes, or after a gap all yield the same ~2.000% over 365 days — cadence does not compound differently and does not drift from the target. More frequent updates still leave getRate() unchanged between successful pokes. JPYC has a stepwise confirmed price targeting hourly updates, not a continuously changing one. Existing withdrawal requests’ atomicPrice values do not update automatically either.

What integrations should display and test

  • Confirmed rate and update time: display getRate() with lastPokeAt(). Connect actual checkpoints with horizontal steps on charts.
  • Projected current rate: label previewNextRate() separately as a projection. Do not substitute it for confirmed balance value, deposit quotes, or completed payouts.
  • Expected next scheduled update: add the vault’s operating cadence (24 hours for USDT, one hour for JPYC) to the last checkpoint and label it as an estimate. Maintain this cadence as deployment-specific operational configuration; do not infer automatic execution from decimals or minInterval alone. Do not advance the confirmed rate merely because that time has arrived. Do not show a scheduled next update on Kairos, where there is no automated keeper.
  • Delayed updates and pending withdrawals: if the cadence is exceeded, show the last successful update and its delayed status. Define the app’s acceptable quote age separately and coordinate stale-rate handling with the team. Distinguish request confirmation, pending settlement, and completed payment.
On Kairos, users can mint mockUSDT, deposit, submit requests, and read state themselves. Coordinate rate-update or redemption tests through contact@superearn.io, following the testnet integration instructions. Mainnet addresses will be added to the integration guide after the production deployment.