FortiBlox LogoFortiBlox Docs
AXIO Block Engine

Validator Incentive Program

Performance-weighted bonuses with early adopter tiers, loyalty multipliers, and heartbeat-based uptime tracking.

Validator Incentive Program

The Validator Incentive Program rewards validators who participate in the AXIO block engine with performance-weighted bonuses. Early adopters receive enhanced tiers, and continuous uptime earns loyalty multipliers. Going offline for more than 24 hours resets your loyalty streak.

1. What It Does

The program manages a reward pool funded by the Reward Distributor's validator incentive channel. When rewards are distributed, each validator's share is computed based on four factors:

  1. Base reward -- the per-epoch allocation from the incentive pool
  2. Early adopter tier bonus -- +25% for first 20 validators, +15% for next 30
  3. Loyalty multiplier -- scales with consecutive uptime (up to 1.5x at 90 days)
  4. Performance score -- uptime and bundle inclusion rate (rolling 7-day average)

These four factors combine multiplicatively. A tier-1 validator with 90-day loyalty and perfect performance earns significantly more than a late joiner with inconsistent uptime.

2. Early Adopter Tiers

Tiers are assigned at registration time based on the order validators join the program:

TierValidatorsBonusEffect
Tier 1First 20+25% (2,500 bps)Base reward multiplied by 1.25
Tier 2Next 30 (21--50)+15% (1,500 bps)Base reward multiplied by 1.15
No Tier51+0%Base reward only

Tier assignment is permanent. If you are the 15th validator to register, you receive Tier 1 for the lifetime of your registration. Tiers cannot be changed or transferred.

3. Loyalty Multiplier

The loyalty multiplier rewards consistent uptime. It is based on consecutive_active_slots -- the number of continuous slots the validator has been active without a gap exceeding 24 hours.

Consecutive UptimeMultiplierValue (bps)
< 30 days1.0x10,000
30 days1.1x11,000
60 days1.25x12,500
90 days1.5x15,000

24-Hour Offline Reset

If the gap between heartbeats or performance recordings exceeds 216,000 slots (~24 hours), the consecutive_active_slots counter resets to zero. The validator must build their loyalty streak from scratch.

gap = current_slot - last_active_slot

if gap > 216,000:  // 24 hours
    consecutive_active_slots = 0  // loyalty reset
else:
    consecutive_active_slots += gap  // loyalty grows

This reset applies in both the Heartbeat and RecordPerformance instructions.

4. Performance Scoring

Two metrics are tracked per validator with a rolling 7-day exponential moving average:

MetricDescriptionUpdate Formula
uptime_bpsUptime percentage (10,000 = 100%)new = old * 0.9 + current * 0.1
inclusion_rate_bpsBundle inclusion rate (10,000 = 100%)new = old * 0.9 + current * 0.1

Both start at 10,000 (100%) when a validator registers and adjust with each RecordPerformance call.

5. Reward Calculation

The compute_reward function combines all four factors:

reward = base_reward
       * (10,000 + tier_bonus_bps) / 10,000
       * loyalty_multiplier / 10,000
       * uptime_bps / 10,000
       * inclusion_rate_bps / 10,000

Example Calculations

Tier 1 validator, 90-day loyalty, perfect performance:

reward = 1,000,000
       * (10,000 + 2,500) / 10,000   = 1,250,000
       * 15,000 / 10,000              = 1,875,000
       * 10,000 / 10,000              = 1,875,000
       * 10,000 / 10,000              = 1,875,000 lamports

No-tier validator, 45-day loyalty, 95% uptime, 80% inclusion:

reward = 1,000,000
       * (10,000 + 0) / 10,000       = 1,000,000
       * 11,000 / 10,000              = 1,100,000
       * 9,500 / 10,000               = 1,045,000
       * 8,000 / 10,000               = 836,000 lamports

The tier-1 validator earns 2.24x the base reward. The late-joining validator with lower performance earns 0.84x.

6. PDA Derivation

AccountSeedsDescription
IncentiveConfig["incentive_config"]Global config, one per deployment
ValidatorRecord["validator_record", vote_account]Per-validator record, keyed by vote account
const [configPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("incentive_config")],
  VALIDATOR_INCENTIVE_PROGRAM_ID
);

const [recordPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("validator_record"), voteAccountPubkey.toBuffer()],
  VALIDATOR_INCENTIVE_PROGRAM_ID
);

7. Instructions

7.1 Initialize

Create the incentive config with default tier and loyalty parameters. Admin only.

Parameters: None.

Accounts:

#AccountSignerWritableDescription
0AdminYesNoProgram admin
1IncentiveConfig PDANoYesConfig account (created)
2Reward PoolNoNoPool account for incentive funds
3System ProgramNoNoSystem program

Default values set at initialization:

  • Tier 1: +25% bonus, first 20 validators
  • Tier 2: +15% bonus, validators 21--50
  • Loyalty: 1.1x at 30d, 1.25x at 60d, 1.5x at 90d

7.2 Register

Register a validator for the incentive program. The validator self-registers by signing with their identity key. Tier is assigned based on registration order.

Parameters:

ParameterTypeDescription
reward_destinationPubkeyAccount to receive incentive payouts

Accounts:

#AccountSignerWritableDescription
0Validator IdentityYesNoValidator signing key
1IncentiveConfig PDANoYesConfig (counter incremented)
2ValidatorRecord PDANoYesRecord account (created)
3Vote AccountNoNoMust be owned by vote program
4System ProgramNoNoSystem program
5Clock SysvarNoNoFor registration timestamp

The vote account is verified to be owned by the Solana vote program. Initial performance metrics are set to 100% uptime and 100% inclusion rate.

7.3 RecordPerformance

Update a validator's performance metrics. Admin-only (authorized cranker). Also updates the loyalty streak and checks for 24-hour offline gaps.

Parameters:

ParameterTypeDescription
uptime_bpsu16Current uptime measurement (0--10,000)
inclusion_rate_bpsu16Current bundle inclusion rate (0--10,000)
bundles_included_deltau64Bundles included since last recording
tips_earned_deltau64Tips earned since last recording (lamports)

Accounts:

#AccountSignerWritableDescription
0Cranker (Admin)YesNoMust be config admin
1IncentiveConfig PDANoNoConfig (read for admin check)
2ValidatorRecord PDANoYesRecord (updated)
3Vote AccountNoNoFor identity verification
4Clock SysvarNoNoFor gap detection

Performance metrics use a rolling average: new = old * 0.9 + current * 0.1 (integer math: (old * 9 + current) / 10).

7.4 ClaimReward

Claim incentive rewards. Can be called by the admin (cranker distributing rewards) or by the validator directly.

Parameters:

ParameterTypeDescription
base_rewardu64Base reward amount for this epoch (lamports)

Accounts:

#AccountSignerWritableDescription
0CallerYesNoAdmin or validator
1IncentiveConfig PDANoYesConfig (accounting updated)
2ValidatorRecord PDANoYesRecord (claim tracking updated)
3Reward PoolNoYesSource of reward funds
4Reward DestinationNoYesMust match record's reward_destination
5System ProgramNoNoSystem program
6Clock SysvarNoNoFor claim timestamp

The actual payout is compute_reward(base_reward) -- not the raw base amount. The reward pool must have sufficient balance above rent-exempt minimum. If the pool is underfunded, the instruction fails with InsufficientFunds.

7.5 Heartbeat

Record that a validator is online. Updates last_active_slot and consecutive_active_slots. Can be called by the validator or their reward destination.

Parameters: None.

Accounts:

#AccountSignerWritableDescription
0SignerYesNoValidator identity or reward destination
1ValidatorRecord PDANoYesRecord (timestamps updated)
2Clock SysvarNoNoFor current slot

If the gap since last_active_slot exceeds 24 hours (216,000 slots), consecutive_active_slots resets to 0 and a log message is emitted. Otherwise, the gap is added to the consecutive counter.

Validators should send heartbeats at least once per day to maintain their loyalty streak. The recommended cadence is every epoch or every few hours.

8. Validator Record State

ValidatorRecord (119 bytes)

FieldTypeDescription
is_initializedboolInitialization flag
vote_accountPubkeyValidator's vote account
reward_destinationPubkeyPayout destination
registered_slotu64Registration timestamp
last_active_slotu64Last heartbeat/performance slot
consecutive_active_slotsu64Loyalty streak counter
uptime_bpsu16Rolling 7-day uptime (bps)
inclusion_rate_bpsu16Rolling 7-day inclusion rate (bps)
total_bundles_includedu64Lifetime bundles included
total_tips_earnedu64Lifetime tips earned (lamports)
tieru8Early adopter tier (1, 2, or 0)
total_incentive_claimedu64Lifetime incentive payouts
last_claim_slotu64Last claim timestamp
bumpu8PDA bump seed

9. Heartbeat Strategy

To maintain the maximum 1.5x loyalty multiplier, validators should implement an automated heartbeat. A simple approach:

import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";

const HEARTBEAT_INTERVAL_SLOTS = 54_000; // ~6 hours

async function heartbeatLoop(
  connection: Connection,
  validatorKey: Keypair,
  recordPda: PublicKey,
  programId: PublicKey,
) {
  while (true) {
    const heartbeatIx = createHeartbeatInstruction({
      signer: validatorKey.publicKey,
      validatorRecord: recordPda,
      programId,
    });

    const tx = new Transaction().add(heartbeatIx);
    tx.feePayer = validatorKey.publicKey;

    const sig = await connection.sendTransaction(tx, [validatorKey]);
    await connection.confirmTransaction(sig);

    console.log(`Heartbeat sent: ${sig}`);

    // Wait ~6 hours worth of slots
    await sleep(6 * 60 * 60 * 1000);
  }
}

Missing a single 24-hour window resets the entire loyalty streak. The heartbeat transaction costs minimal fees and should be treated as critical infrastructure.

Next Steps