@@ -27,8 +27,8 @@ use svm_hash::{merkle::MerkleProof, sha2::Hash};
2727use crate :: {
2828 instruction:: {
2929 account:: DequeueFillsCpiAccounts , ContributorRewardsConfiguration ,
30- DistributionMerkleRootKind , ProgramConfiguration , ProgramFlagConfiguration ,
31- RevenueDistributionInstructionData ,
30+ DistributionMerkleRootKind , ProgramConfiguration , ProgramFeatureConfiguration ,
31+ ProgramFlagConfiguration , RevenueDistributionInstructionData ,
3232 } ,
3333 state:: {
3434 self , CommunityBurnRateParameters , ContributorRewards , Distribution , Journal ,
@@ -542,6 +542,25 @@ fn try_configure_program(accounts: &[AccountInfo], setting: ProgramConfiguration
542542 . distribution_parameters
543543 . initialization_grace_period_minutes = grace_period_minutes;
544544 }
545+ ProgramConfiguration :: FeatureActivation {
546+ feature,
547+ activation_epoch,
548+ } => {
549+ if activation_epoch == 0 {
550+ msg ! ( "Cannot activate feature at epoch zero" ) ;
551+ return Err ( ProgramError :: InvalidInstructionData ) ;
552+ }
553+
554+ match feature {
555+ ProgramFeatureConfiguration :: SolanaValidatorDebtWriteOff => {
556+ msg ! (
557+ "Set Solana validator debt write-off feature activation epoch: {}" ,
558+ activation_epoch
559+ ) ;
560+ program_config. debt_write_off_feature_activation_epoch = activation_epoch;
561+ }
562+ }
563+ }
545564 }
546565
547566 Ok ( ( ) )
@@ -1199,14 +1218,14 @@ fn try_distribute_rewards(
11991218 ZeroCopyMutAccount :: < Distribution > :: try_next_accounts ( & mut accounts_iter, Some ( & ID ) ) ?;
12001219 msg ! ( "DZ epoch: {}" , distribution. dz_epoch) ;
12011220
1202- // Make sure 2Z tokens have been swept.
1203- if !distribution. has_swept_2z_tokens ( ) {
1204- msg ! ( "Distribution has not swept 2Z tokens" ) ;
1221+ if distribution. are_all_rewards_distributed ( ) {
1222+ msg ! ( "All rewards have already been distributed" ) ;
12051223 return Err ( ProgramError :: InvalidAccountData ) ;
12061224 }
12071225
1208- if distribution. distributed_rewards_count == distribution. total_contributors {
1209- msg ! ( "All rewards have already been distributed" ) ;
1226+ // Make sure 2Z tokens have been swept.
1227+ if !distribution. has_swept_2z_tokens ( ) {
1228+ msg ! ( "Distribution has not swept 2Z tokens" ) ;
12101229 return Err ( ProgramError :: InvalidAccountData ) ;
12111230 }
12121231
@@ -1801,6 +1820,22 @@ fn try_enable_solana_validator_debt_write_off(accounts: &[AccountInfo]) -> Progr
18011820 // Make sure the program is not paused.
18021821 program_config. try_require_unpaused ( ) ?;
18031822
1823+ // Cannot enable write-offs before the activation epoch.
1824+ if !program_config. is_debt_write_off_feature_activated ( ) {
1825+ let activation_epoch = program_config. debt_write_off_feature_activation_epoch ;
1826+
1827+ if activation_epoch == 0 {
1828+ msg ! ( "Debt write-off feature activation epoch not configured" ) ;
1829+ } else {
1830+ msg ! (
1831+ "Debt write-off feature activates at epoch {}" ,
1832+ activation_epoch
1833+ ) ;
1834+ }
1835+
1836+ return Err ( ProgramError :: InvalidAccountData ) ;
1837+ }
1838+
18041839 // Account 1 must be the distribution.
18051840 let mut distribution =
18061841 ZeroCopyMutAccount :: < Distribution > :: try_next_accounts ( & mut accounts_iter, Some ( & ID ) ) ?;
@@ -1933,6 +1968,8 @@ fn try_write_off_solana_validator_debt(
19331968 return Err ( ProgramError :: InvalidAccountData ) ;
19341969 }
19351970
1971+ distribution. solana_validator_write_off_count += 1 ;
1972+
19361973 // Bits indicating whether debt has been written off for specific leaf
19371974 // indices are stored in the distribution's remaining data.
19381975 let write_off_bitmap_range =
@@ -2833,9 +2870,8 @@ fn try_process_remaining_data_leaf_index(
28332870///
28342871/// # Why are we migrating?
28352872///
2836- /// The program deployed on Solana mainnet-beta had a bug that did not properly
2837- /// track the 2Z token account balance on the journal. This instruction
2838- /// processor will correct the journal to fix the balance.
2873+ /// After the last migration, the migrated bit is set to true. This instruction
2874+ /// will reset this bit to false.
28392875fn try_migrate_program_accounts ( accounts : & [ AccountInfo ] ) -> ProgramResult {
28402876 msg ! ( "Migrate program accounts" ) ;
28412877
@@ -2844,10 +2880,6 @@ fn try_migrate_program_accounts(accounts: &[AccountInfo]) -> ProgramResult {
28442880 // program).
28452881 // - 1: The program's owner (i.e., upgrade authority).
28462882 // - 2: Program config.
2847- // - 3: Journal.
2848- //
2849- // Remaining accounts are distribution accounts where tokens have been
2850- // swept.
28512883 let mut accounts_iter = accounts. iter ( ) . enumerate ( ) ;
28522884
28532885 // Account 0 must be the program data belonging to this program.
@@ -2859,59 +2891,8 @@ fn try_migrate_program_accounts(accounts: &[AccountInfo]) -> ProgramResult {
28592891 let mut program_config =
28602892 ZeroCopyMutAccount :: < ProgramConfig > :: try_next_accounts ( & mut accounts_iter, Some ( & ID ) ) ?;
28612893
2862- if program_config. is_migrated ( ) {
2863- msg ! ( "Program has already been migrated. Nothing to do" ) ;
2864- return Ok ( ( ) ) ;
2865- }
2866-
2867- program_config. set_is_migrated ( true ) ;
2868-
2869- // Account 3 must be the journal.
2870- let mut journal =
2871- ZeroCopyMutAccount :: < Journal > :: try_next_accounts ( & mut accounts_iter, Some ( & ID ) ) ?;
2872-
2873- // Copy balance, which is actually the lifetime amount.
2874- journal. lifetime_swapped_2z_amount = Uint :: from ( journal. swap_2z_destination_balance ) ;
2875- msg ! (
2876- "Fixed lifetime swapped 2Z amount to {}" ,
2877- journal. lifetime_swapped_2z_amount
2878- ) ;
2879-
2880- let first_epoch = 31 ;
2881- let until_epoch = journal. next_dz_epoch_to_sweep_tokens . value ( ) ;
2882-
2883- // Iterate over all epochs with rewards. We need to make sure the
2884- // distribution accounts are passed in the order we expect.
2885- for epoch in first_epoch..until_epoch {
2886- let distribution =
2887- ZeroCopyAccount :: < Distribution > :: try_next_accounts ( & mut accounts_iter, Some ( & ID ) ) ?;
2888-
2889- if distribution. dz_epoch != epoch {
2890- msg ! ( "Invalid distribution epoch: {}" , distribution. dz_epoch) ;
2891- return Err ( ProgramError :: InvalidAccountData ) ;
2892- }
2893-
2894- // Be extra sure that the distribution has swept 2Z tokens. This check
2895- // should never fail.
2896- if !distribution. has_swept_2z_tokens ( ) {
2897- msg ! ( "Distribution has not swept 2Z tokens for epoch {}" , epoch) ;
2898- return Err ( ProgramError :: InvalidAccountData ) ;
2899- }
2900-
2901- let collected_2z_amount = distribution. collected_2z_converted_from_sol ;
2902- msg ! ( " Epoch {}: {} 2Z" , epoch, collected_2z_amount) ;
2903-
2904- // Catch underflow here.
2905- journal. swap_2z_destination_balance = journal
2906- . swap_2z_destination_balance
2907- . checked_sub ( collected_2z_amount)
2908- . unwrap ( ) ;
2909- }
2910-
2911- msg ! (
2912- "Fixed journal swap 2Z destination balance to {}" ,
2913- journal. swap_2z_destination_balance
2914- ) ;
2894+ program_config. set_is_migrated ( false ) ;
2895+ msg ! ( "Set flag is_migrated to false" ) ;
29152896
29162897 Ok ( ( ) )
29172898}
0 commit comments