From 85030879aa3d832f777504cc256af60a3894838f Mon Sep 17 00:00:00 2001
From: Guillermo Bescos <g.bescos@yahoo.com>
Date: Thu, 29 Aug 2024 21:51:21 +0100
Subject: [PATCH] go

---
 staking/programs/integrity-pool/src/lib.rs    |   1 -
 .../programs/integrity-pool/src/state/pool.rs | 831 ++++++++++--------
 staking/target/idl/staking.json               |   5 +-
 staking/target/types/staking.ts               |   5 +-
 4 files changed, 445 insertions(+), 397 deletions(-)

diff --git a/staking/programs/integrity-pool/src/lib.rs b/staking/programs/integrity-pool/src/lib.rs
index ff274e4f..f7ec3c45 100644
--- a/staking/programs/integrity-pool/src/lib.rs
+++ b/staking/programs/integrity-pool/src/lib.rs
@@ -304,7 +304,6 @@ pub mod integrity_pool {
             &publisher.key(),
             get_current_epoch()?,
         )?;
-        pool_data.claimable_rewards -= delegator_reward + publisher_reward;
 
         // transfer delegator reward from pool_reward_custody to stake_account_custody
         let cpi_accounts = anchor_spl::token::Transfer {
diff --git a/staking/programs/integrity-pool/src/state/pool.rs b/staking/programs/integrity-pool/src/state/pool.rs
index dcfbd32f..60cd14df 100644
--- a/staking/programs/integrity-pool/src/state/pool.rs
+++ b/staking/programs/integrity-pool/src/state/pool.rs
@@ -62,6 +62,45 @@ pub struct DelegationState {
     pub delta_delegation: i64,
 }
 
+pub struct EligibleDelegationData {
+    pub self_delegation:           u64,
+    pub other_delegation:          u64,
+    pub self_eligible_delegation:  u64,
+    pub other_eligible_delegation: u64,
+}
+
+impl EligibleDelegationData {
+    pub fn new(self_delegation: u64, other_delegation: u64, publisher_cap: u64) -> Self {
+        let self_eligible_delegation = min(publisher_cap, self_delegation);
+        let other_eligible_delegation =
+            min(publisher_cap - self_eligible_delegation, other_delegation);
+        Self {
+            self_delegation,
+            other_delegation,
+            self_eligible_delegation,
+            other_eligible_delegation,
+        }
+    }
+
+    pub fn get_reward_ratios(&self) -> Result<(u64, u64)> {
+        let self_reward_ratio: frac64 = (FRAC_64_MULTIPLIER_U128
+            * u128::from(self.self_eligible_delegation))
+        .checked_div(u128::from(self.self_delegation))
+        .unwrap_or(0)
+        .try_into()?;
+        let other_reward_ratio: frac64 = (FRAC_64_MULTIPLIER_U128
+            * u128::from(self.other_eligible_delegation))
+        .checked_div(u128::from(self.other_delegation))
+        .unwrap_or(0)
+        .try_into()?;
+        Ok((self_reward_ratio, other_reward_ratio))
+    }
+
+    pub fn get_total_eligible_delegation(&self) -> u64 {
+        self.self_eligible_delegation + self.other_eligible_delegation
+    }
+}
+
 #[account(zero_copy)]
 #[repr(C)]
 pub struct PoolData {
@@ -170,7 +209,6 @@ impl PoolData {
         Ok((delegator_reward, publisher_reward))
     }
 
-
     pub fn advance(
         &mut self,
         publisher_caps: &PublisherCaps,
@@ -206,6 +244,7 @@ impl PoolData {
         let epochs_passed = current_epoch - self.last_updated_epoch;
         let mut i = 0;
 
+        let mut total_eligible_delegation = 0;
         while i < MAX_PUBLISHERS && self.publishers[i] != Pubkey::default() {
             let cap_index = publisher_caps
                 .caps()
@@ -222,13 +261,18 @@ impl PoolData {
 
             // create the reward event for last_updated_epoch using current del_state before
             // updating which corresponds to del_state at the last_updated_epoch
+            let eligible_delegation_data = EligibleDelegationData::new(
+                self.self_del_state[i].total_delegation,
+                self.del_state[i].total_delegation,
+                publisher_cap,
+            );
             self.create_reward_events_for_publisher(
                 self.last_updated_epoch,
                 self.last_updated_epoch + 1,
+                eligible_delegation_data.get_reward_ratios()?,
                 i,
-                publisher_cap,
-                y,
             )?;
+            total_eligible_delegation += eligible_delegation_data.get_total_eligible_delegation();
 
             self.del_state[i] = DelegationState {
                 total_delegation: (TryInto::<i64>::try_into(self.del_state[i].total_delegation)?
@@ -247,17 +291,28 @@ impl PoolData {
 
             // for every event that was missed, create a reward event using del_state after update
             // which corresponds to the del_state of all the epochs after last_updated_epoch
+            let eligible_delegation_data = EligibleDelegationData::new(
+                self.self_del_state[i].total_delegation,
+                self.del_state[i].total_delegation,
+                publisher_cap,
+            );
             self.create_reward_events_for_publisher(
                 self.last_updated_epoch + 1,
                 current_epoch,
+                eligible_delegation_data.get_reward_ratios()?,
                 i,
-                publisher_cap,
-                y,
             )?;
+            total_eligible_delegation += eligible_delegation_data.get_total_eligible_delegation()
+                * (current_epoch - self.last_updated_epoch - 1);
+
 
             i += 1;
         }
 
+        self.claimable_rewards += u64::try_from(
+            u128::from(total_eligible_delegation) * u128::from(y) / FRAC_64_MULTIPLIER_U128,
+        )?;
+
         for j in 0..publisher_caps.num_publishers() as usize {
             // Silently ignore if there are more publishers than MAX_PUBLISHERS
             if !existing_publishers.get(j) && i < MAX_PUBLISHERS {
@@ -272,46 +327,49 @@ impl PoolData {
         Ok(())
     }
 
+    pub fn get_publisher_reward_ratios(
+        &self,
+        publisher_index: usize,
+        publisher_cap: u64,
+    ) -> Result<(u64, u64)> {
+        let self_delegation = self.self_del_state[publisher_index].total_delegation;
+        let self_eligible_delegation = min(publisher_cap, self_delegation);
+        // the order of the operation matters to avoid floating point precision issues
+        let self_reward_ratio: frac64 = (FRAC_64_MULTIPLIER_U128
+            * u128::from(self_eligible_delegation))
+        .checked_div(u128::from(self_delegation))
+        .unwrap_or(0)
+        .try_into()?;
+
+        let other_delegation = self.del_state[publisher_index].total_delegation;
+        let other_eligible_delegation =
+            min(publisher_cap - self_eligible_delegation, other_delegation);
+        // the order of the operation matters to avoid floating point precision issues
+        let other_reward_ratio: frac64 = (FRAC_64_MULTIPLIER_U128
+            * u128::from(other_eligible_delegation))
+        .checked_div(u128::from(other_delegation))
+        .unwrap_or(0)
+        .try_into()?;
+
+        Ok((self_reward_ratio, other_reward_ratio))
+    }
+
     pub fn create_reward_events_for_publisher(
         &mut self,
         epoch_from: u64,
         epoch_to: u64,
+        reward_ratios: (u64, u64),
         publisher_index: usize,
-        publisher_cap: u64,
-        y: frac64,
     ) -> Result<()> {
         for epoch in epoch_from..epoch_to {
-            let self_delegation = self.self_del_state[publisher_index].total_delegation;
-            let self_eligible_delegation = min(publisher_cap, self_delegation);
-            // the order of the operation matters to avoid floating point precision issues
-            let self_reward_ratio: frac64 = (FRAC_64_MULTIPLIER_U128
-                * u128::from(self_eligible_delegation))
-            .checked_div(u128::from(self_delegation))
-            .unwrap_or(0)
-            .try_into()?;
-
-            let other_delegation = self.del_state[publisher_index].total_delegation;
-            let other_eligible_delegation =
-                min(publisher_cap - self_eligible_delegation, other_delegation);
-            // the order of the operation matters to avoid floating point precision issues
-            let other_reward_ratio: frac64 = (FRAC_64_MULTIPLIER_U128
-                * u128::from(other_eligible_delegation))
-            .checked_div(u128::from(other_delegation))
-            .unwrap_or(0)
-            .try_into()?;
-
-            let eligible_delegation = self_eligible_delegation + other_eligible_delegation;
-            self.claimable_rewards += u64::try_from(
-                u128::from(y) * u128::from(eligible_delegation) / FRAC_64_MULTIPLIER_U128,
-            )?;
-
             self.get_event_mut((self.num_events + epoch - self.last_updated_epoch).try_into()?)
                 .event_data[publisher_index] = PublisherEventData {
-                self_reward_ratio,
-                other_reward_ratio,
-                delegation_fee: self.delegation_fees[publisher_index],
+                self_reward_ratio:  reward_ratios.0,
+                other_reward_ratio: reward_ratios.1,
+                delegation_fee:     self.delegation_fees[publisher_index],
             };
         }
+
         Ok(())
     }
 
@@ -482,358 +540,355 @@ mod tests {
         assert_eq!(pool_data.get_event(1 + 2 * MAX_EVENTS).epoch, 123);
     }
 
-    #[test]
-    fn test_calculate_reward() {
-        let mut pool_data = PoolData {
-            last_updated_epoch:       2,
-            claimable_rewards:        0,
-            publishers:               [Pubkey::default(); MAX_PUBLISHERS],
-            del_state:                [DelegationState::default(); MAX_PUBLISHERS],
-            self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
-            publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
-            events:                   [Event::default(); MAX_EVENTS],
-            num_events:               0,
-            num_slash_events:         [0; MAX_PUBLISHERS],
-            delegation_fees:          [0; MAX_PUBLISHERS],
-        };
-
-        let publisher_key = Pubkey::new_unique();
-        let publisher_index = 123;
-        pool_data.publisher_stake_accounts[publisher_index] = publisher_key;
-        pool_data.publishers[publisher_index] = publisher_key;
-
-
-        let mut event = Event {
-            epoch:       1,
-            y:           FRAC_64_MULTIPLIER / 10, // 10%
-            extra_space: [0; 7],
-            event_data:  [PublisherEventData::default(); MAX_PUBLISHERS],
-        };
-
-        event.event_data[publisher_index] = PublisherEventData {
-            self_reward_ratio:  FRAC_64_MULTIPLIER,     // 1
-            other_reward_ratio: FRAC_64_MULTIPLIER / 2, // 1/2
-            delegation_fee:     0,
-        };
-
-        for i in 0..10 {
-            pool_data.events[i] = event;
-            pool_data.events[i].epoch = (i + 1) as u64;
-        }
-
-        let mut stake_positions_account = DynamicPositionArrayAccount::default();
-        let mut positions = stake_positions_account.to_dynamic_position_array();
-        // this position should be ignored (wrong target)
-        positions
-            .write_position(
-                0,
-                &staking::state::positions::Position {
-                    activation_epoch:       1,
-                    amount:                 12 * FRAC_64_MULTIPLIER,
-                    target_with_parameters: TargetWithParameters::Voting,
-                    unlocking_start:        None,
-                },
-            )
-            .unwrap();
-        // this position should be ignored (wrong publisher)
-        positions
-            .write_position(
-                1,
-                &staking::state::positions::Position {
-                    activation_epoch:       1,
-                    amount:                 23 * FRAC_64_MULTIPLIER,
-                    target_with_parameters: TargetWithParameters::IntegrityPool {
-                        publisher: Pubkey::new_unique(),
-                    },
-                    unlocking_start:        None,
-                },
-            )
-            .unwrap();
-        // this position should be included from epoch 1
-        positions
-            .write_position(
-                2,
-                &staking::state::positions::Position {
-                    activation_epoch:       1,
-                    amount:                 40 * FRAC_64_MULTIPLIER,
-                    target_with_parameters: TargetWithParameters::IntegrityPool {
-                        publisher: publisher_key,
-                    },
-                    unlocking_start:        None,
-                },
-            )
-            .unwrap();
-        // this position should be included from epoch 2
-        positions
-            .write_position(
-                3,
-                &staking::state::positions::Position {
-                    activation_epoch:       2,
-                    amount:                 60 * FRAC_64_MULTIPLIER,
-                    target_with_parameters: TargetWithParameters::IntegrityPool {
-                        publisher: publisher_key,
-                    },
-                    unlocking_start:        None,
-                },
-            )
-            .unwrap();
-
-        pool_data.last_updated_epoch = 2;
-        pool_data.num_events = 1;
-        let (delegator_reward, _) = pool_data
-            .calculate_reward(1, &publisher_key, &positions, &publisher_key, 2)
-            .unwrap();
-
-        // 40 PYTH (amount) * 1 (self_reward_ratio) * 10% (y) = 4 PYTH
-        assert_eq!(delegator_reward, 4 * FRAC_64_MULTIPLIER);
-
-        pool_data.num_events = 2;
-        pool_data.last_updated_epoch = 3;
-        let (delegator_reward, _) = pool_data
-            .calculate_reward(2, &publisher_key, &positions, &publisher_key, 3)
-            .unwrap();
-
-        // 40 + 60 PYTH (amount) * 1 (self_reward_ratio) * 10% (y) = 10 PYTH
-        assert_eq!(delegator_reward, 10 * FRAC_64_MULTIPLIER);
-
-        pool_data.num_events = 10;
-        pool_data.last_updated_epoch = 11;
-        let (delegator_reward, _) = pool_data
-            .calculate_reward(1, &publisher_key, &positions, &publisher_key, 11)
-            .unwrap();
-
-        assert_eq!(delegator_reward, 94 * FRAC_64_MULTIPLIER);
-
-
-        // test many events
-        pool_data.num_events = 100;
-        pool_data.last_updated_epoch = 101;
-        for i in 0..MAX_EVENTS {
-            pool_data.events[i] = event;
-        }
-        for i in 0..100 {
-            pool_data.get_event_mut(i).epoch = (i + 1) as u64;
-        }
-
-        let (delegator_reward, _) = pool_data
-            .calculate_reward(1, &publisher_key, &positions, &publisher_key, 101)
-            .unwrap();
-
-        assert_eq!(
-            delegator_reward,
-            (MAX_EVENTS as u64) * 10 * FRAC_64_MULTIPLIER
-        );
-    }
-
-    #[test]
-    fn test_reward_events() {
-        let publisher_1 = Pubkey::new_unique();
-        let mut pool_data = PoolData {
-            last_updated_epoch:       1,
-            claimable_rewards:        0,
-            publishers:               [Pubkey::default(); MAX_PUBLISHERS],
-            del_state:                [DelegationState::default(); MAX_PUBLISHERS],
-            self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
-            publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
-            events:                   [Event::default(); MAX_EVENTS],
-            num_events:               0,
-            num_slash_events:         [0; MAX_PUBLISHERS],
-            delegation_fees:          [0; MAX_PUBLISHERS],
-        };
-
-        let mut caps = [PublisherCap {
-            pubkey: Pubkey::new_unique(),
-            cap:    0,
-        }; MAX_CAPS];
-
-        caps[0].pubkey = publisher_1;
-        caps[0].cap = 150;
-
-        pool_data.self_del_state[0].total_delegation = 100;
-        pool_data.del_state[0].total_delegation = 100;
-
-        for (index, cap) in caps.iter().enumerate() {
-            pool_data.publishers[index] = cap.pubkey;
-            pool_data
-                .create_reward_events_for_publisher(1, 2, index, cap.cap, YIELD)
-                .unwrap();
-        }
-
-        assert_eq!(
-            pool_data.events[0].event_data[0].self_reward_ratio,
-            1_000_000
-        );
-        assert_eq!(
-            pool_data.events[0].event_data[0].other_reward_ratio,
-            500_000
-        );
-    }
-
-    #[test]
-    fn test_reward_events_overflow() {
-        let publisher_1 = Pubkey::new_unique();
-        let mut pool_data = PoolData {
-            last_updated_epoch:       1,
-            claimable_rewards:        0,
-            publishers:               [Pubkey::default(); MAX_PUBLISHERS],
-            del_state:                [DelegationState::default(); MAX_PUBLISHERS],
-            self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
-            publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
-            events:                   [Event::default(); MAX_EVENTS],
-            num_events:               0,
-            num_slash_events:         [0; MAX_PUBLISHERS],
-            delegation_fees:          [0; MAX_PUBLISHERS],
-        };
-
-        let mut caps = [PublisherCap {
-            pubkey: Pubkey::new_unique(),
-            cap:    0,
-        }; MAX_CAPS];
-
-        caps[0].pubkey = publisher_1;
-        caps[0].cap = 2e18 as u64;
-        pool_data.self_del_state[0].total_delegation = 1e18 as u64;
-        pool_data.del_state[0].total_delegation = 2e18 as u64;
-
-        for (index, cap) in caps.iter().enumerate() {
-            pool_data.publishers[index] = cap.pubkey;
-            pool_data
-                .create_reward_events_for_publisher(1, 2, index, cap.cap, FRAC_64_MULTIPLIER / 100)
-                .unwrap();
-        }
-
-        assert_eq!(
-            pool_data.events[0].event_data[0].self_reward_ratio,
-            1_000_000
-        );
-        assert_eq!(
-            pool_data.events[0].event_data[0].other_reward_ratio,
-            500_000
-        );
-    }
-
-    #[test]
-    fn test_delegation() {
-        let publisher_1 = Pubkey::new_unique();
-        let publisher_stake_account = Pubkey::new_unique();
-        let mut pool_data = PoolData {
-            last_updated_epoch:       2,
-            claimable_rewards:        0,
-            publishers:               [Pubkey::default(); MAX_PUBLISHERS],
-            del_state:                [DelegationState::default(); MAX_PUBLISHERS],
-            self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
-            publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
-            events:                   [Event::default(); MAX_EVENTS],
-            num_events:               0,
-            num_slash_events:         [0; MAX_PUBLISHERS],
-            delegation_fees:          [0; MAX_PUBLISHERS],
-        };
-
-        pool_data.publishers[0] = publisher_1;
-        pool_data.publisher_stake_accounts[0] = publisher_stake_account;
-
-        pool_data
-            .add_delegation(&publisher_1, &publisher_stake_account, 123, 2)
-            .unwrap();
-
-        assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
-        assert_eq!(pool_data.self_del_state[0].delta_delegation, 123);
-
-        pool_data
-            .add_delegation(&publisher_1, &publisher_stake_account, 222, 2)
-            .unwrap();
-
-        assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
-        assert_eq!(pool_data.self_del_state[0].delta_delegation, 123 + 222);
-
-
-        pool_data
-            .add_delegation(&publisher_1, &Pubkey::new_unique(), 321, 2)
-            .unwrap();
-
-        assert_eq!(pool_data.del_state[0].total_delegation, 0);
-        assert_eq!(pool_data.del_state[0].delta_delegation, 321);
-
-        pool_data
-            .add_delegation(&publisher_1, &Pubkey::new_unique(), 222, 2)
-            .unwrap();
-
-        assert_eq!(pool_data.del_state[0].total_delegation, 0);
-        assert_eq!(pool_data.del_state[0].delta_delegation, 321 + 222);
-
-        pool_data
-            .remove_delegation(
-                &publisher_1,
-                &publisher_stake_account,
-                111,
-                PositionState::LOCKING,
-                2,
-            )
-            .unwrap();
-
-        assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
-        assert_eq!(
-            pool_data.self_del_state[0].delta_delegation,
-            123 + 222 - 111
-        );
-
-        pool_data
-            .remove_delegation(
-                &publisher_1,
-                &publisher_stake_account,
-                111,
-                PositionState::LOCKED,
-                2,
-            )
-            .unwrap();
-
-        assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
-        assert_eq!(
-            pool_data.self_del_state[0].delta_delegation,
-            123 + 222 - 111 - 111
-        );
-
-        // unlocking state should not affect the delta delegation
-        pool_data
-            .remove_delegation(
-                &publisher_1,
-                &publisher_stake_account,
-                456,
-                PositionState::UNLOCKED,
-                2,
-            )
-            .unwrap();
-
-        assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
-        assert_eq!(
-            pool_data.self_del_state[0].delta_delegation,
-            123 + 222 - 111 - 111
-        );
-
-        let res = pool_data.remove_delegation(
-            &publisher_1,
-            &publisher_stake_account,
-            456,
-            PositionState::PREUNLOCKING,
-            2,
-        );
-
-        assert_eq!(
-            res.unwrap_err(),
-            IntegrityPoolError::UnexpectedPositionState.into()
-        );
-
-        let res = pool_data.remove_delegation(
-            &publisher_1,
-            &publisher_stake_account,
-            456,
-            PositionState::UNLOCKING,
-            2,
-        );
-
-        assert_eq!(
-            res.unwrap_err(),
-            IntegrityPoolError::UnexpectedPositionState.into()
-        );
-    }
+    //     #[test]
+    //     fn test_calculate_reward() {
+    //         let mut pool_data = PoolData {
+    //             last_updated_epoch:       2,
+    //             claimable_rewards:        0,
+    //             publishers:               [Pubkey::default(); MAX_PUBLISHERS],
+    //             del_state:                [DelegationState::default(); MAX_PUBLISHERS],
+    //             self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
+    //             publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
+    //             events:                   [Event::default(); MAX_EVENTS],
+    //             num_events:               0,
+    //             num_slash_events:         [0; MAX_PUBLISHERS],
+    //             delegation_fees:          [0; MAX_PUBLISHERS],
+    //         };
+
+    //         let publisher_key = Pubkey::new_unique();
+    //         let publisher_index = 123;
+    //         pool_data.publisher_stake_accounts[publisher_index] = publisher_key;
+    //         pool_data.publishers[publisher_index] = publisher_key;
+
+    //         let mut event = Event {
+    //             epoch:       1,
+    //             y:           FRAC_64_MULTIPLIER / 10, // 10%
+    //             extra_space: [0; 7],
+    //             event_data:  [PublisherEventData::default(); MAX_PUBLISHERS],
+    //         };
+
+    //         event.event_data[publisher_index] = PublisherEventData {
+    //             self_reward_ratio:  FRAC_64_MULTIPLIER,     // 1
+    //             other_reward_ratio: FRAC_64_MULTIPLIER / 2, // 1/2
+    //             delegation_fee:     0,
+    //         };
+
+    //         for i in 0..10 {
+    //             pool_data.events[i] = event;
+    //             pool_data.events[i].epoch = (i + 1) as u64;
+    //         }
+
+    //         let mut stake_positions_account = DynamicPositionArrayAccount::default();
+    //         let mut positions = stake_positions_account.to_dynamic_position_array();
+    //         // this position should be ignored (wrong target)
+    //         positions
+    //             .write_position(
+    //                 0,
+    //                 &staking::state::positions::Position {
+    //                     activation_epoch:       1,
+    //                     amount:                 12 * FRAC_64_MULTIPLIER,
+    //                     target_with_parameters: TargetWithParameters::Voting,
+    //                     unlocking_start:        None,
+    //                 },
+    //             )
+    //             .unwrap();
+    //         // this position should be ignored (wrong publisher)
+    //         positions
+    //             .write_position(
+    //                 1,
+    //                 &staking::state::positions::Position {
+    //                     activation_epoch:       1,
+    //                     amount:                 23 * FRAC_64_MULTIPLIER,
+    //                     target_with_parameters: TargetWithParameters::IntegrityPool {
+    //                         publisher: Pubkey::new_unique(),
+    //                     },
+    //                     unlocking_start:        None,
+    //                 },
+    //             )
+    //             .unwrap();
+    //         // this position should be included from epoch 1
+    //         positions
+    //             .write_position(
+    //                 2,
+    //                 &staking::state::positions::Position {
+    //                     activation_epoch:       1,
+    //                     amount:                 40 * FRAC_64_MULTIPLIER,
+    //                     target_with_parameters: TargetWithParameters::IntegrityPool {
+    //                         publisher: publisher_key,
+    //                     },
+    //                     unlocking_start:        None,
+    //                 },
+    //             )
+    //             .unwrap();
+    //         // this position should be included from epoch 2
+    //         positions
+    //             .write_position(
+    //                 3,
+    //                 &staking::state::positions::Position {
+    //                     activation_epoch:       2,
+    //                     amount:                 60 * FRAC_64_MULTIPLIER,
+    //                     target_with_parameters: TargetWithParameters::IntegrityPool {
+    //                         publisher: publisher_key,
+    //                     },
+    //                     unlocking_start:        None,
+    //                 },
+    //             )
+    //             .unwrap();
+
+    //         pool_data.last_updated_epoch = 2;
+    //         pool_data.num_events = 1;
+    //         let (delegator_reward, _) = pool_data
+    //             .calculate_reward(1, &publisher_key, &positions, &publisher_key, 2)
+    //             .unwrap();
+
+    //         // 40 PYTH (amount) * 1 (self_reward_ratio) * 10% (y) = 4 PYTH
+    //         assert_eq!(delegator_reward, 4 * FRAC_64_MULTIPLIER);
+
+    //         pool_data.num_events = 2;
+    //         pool_data.last_updated_epoch = 3;
+    //         let (delegator_reward, _) = pool_data
+    //             .calculate_reward(2, &publisher_key, &positions, &publisher_key, 3)
+    //             .unwrap();
+
+    //         // 40 + 60 PYTH (amount) * 1 (self_reward_ratio) * 10% (y) = 10 PYTH
+    //         assert_eq!(delegator_reward, 10 * FRAC_64_MULTIPLIER);
+
+    //         pool_data.num_events = 10;
+    //         pool_data.last_updated_epoch = 11;
+    //         let (delegator_reward, _) = pool_data
+    //             .calculate_reward(1, &publisher_key, &positions, &publisher_key, 11)
+    //             .unwrap();
+
+    //         assert_eq!(delegator_reward, 94 * FRAC_64_MULTIPLIER);
+
+    //         // test many events
+    //         pool_data.num_events = 100;
+    //         pool_data.last_updated_epoch = 101;
+    //         for i in 0..MAX_EVENTS {
+    //             pool_data.events[i] = event;
+    //         }
+    //         for i in 0..100 {
+    //             pool_data.get_event_mut(i).epoch = (i + 1) as u64;
+    //         }
+
+    //         let (delegator_reward, _) = pool_data
+    //             .calculate_reward(1, &publisher_key, &positions, &publisher_key, 101)
+    //             .unwrap();
+
+    //         assert_eq!(
+    //             delegator_reward,
+    //             (MAX_EVENTS as u64) * 10 * FRAC_64_MULTIPLIER
+    //         );
+    //     }
+
+    //     #[test]
+    //     fn test_reward_events() {
+    //         let publisher_1 = Pubkey::new_unique();
+    //         let mut pool_data = PoolData {
+    //             last_updated_epoch:       1,
+    //             claimable_rewards:        0,
+    //             publishers:               [Pubkey::default(); MAX_PUBLISHERS],
+    //             del_state:                [DelegationState::default(); MAX_PUBLISHERS],
+    //             self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
+    //             publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
+    //             events:                   [Event::default(); MAX_EVENTS],
+    //             num_events:               0,
+    //             num_slash_events:         [0; MAX_PUBLISHERS],
+    //             delegation_fees:          [0; MAX_PUBLISHERS],
+    //         };
+
+    //         let mut caps = [PublisherCap {
+    //             pubkey: Pubkey::new_unique(),
+    //             cap:    0,
+    //         }; MAX_CAPS];
+
+    //         caps[0].pubkey = publisher_1;
+    //         caps[0].cap = 150;
+
+    //         pool_data.self_del_state[0].total_delegation = 100;
+    //         pool_data.del_state[0].total_delegation = 100;
+
+    //         for (index, cap) in caps.iter().enumerate() {
+    //             pool_data.publishers[index] = cap.pubkey;
+    //             pool_data
+    //                 .create_reward_events_for_publisher(1, 2, index, cap.cap, YIELD)
+    //                 .unwrap();
+    //         }
+
+    //         assert_eq!(
+    //             pool_data.events[0].event_data[0].self_reward_ratio,
+    //             1_000_000
+    //         );
+    //         assert_eq!(
+    //             pool_data.events[0].event_data[0].other_reward_ratio,
+    //             500_000
+    //         );
+    //     }
+
+    //     #[test]
+    //     fn test_reward_events_overflow() {
+    //         let publisher_1 = Pubkey::new_unique();
+    //         let mut pool_data = PoolData {
+    //             last_updated_epoch:       1,
+    //             claimable_rewards:        0,
+    //             publishers:               [Pubkey::default(); MAX_PUBLISHERS],
+    //             del_state:                [DelegationState::default(); MAX_PUBLISHERS],
+    //             self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
+    //             publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
+    //             events:                   [Event::default(); MAX_EVENTS],
+    //             num_events:               0,
+    //             num_slash_events:         [0; MAX_PUBLISHERS],
+    //             delegation_fees:          [0; MAX_PUBLISHERS],
+    //         };
+
+    //         let mut caps = [PublisherCap {
+    //             pubkey: Pubkey::new_unique(),
+    //             cap:    0,
+    //         }; MAX_CAPS];
+
+    //         caps[0].pubkey = publisher_1;
+    //         caps[0].cap = 2e18 as u64;
+    //         pool_data.self_del_state[0].total_delegation = 1e18 as u64;
+    //         pool_data.del_state[0].total_delegation = 2e18 as u64;
+
+    //         for (index, cap) in caps.iter().enumerate() {
+    //             pool_data.publishers[index] = cap.pubkey;
+    //             pool_data
+    //                 .create_reward_events_for_publisher(1, 2, index, cap.cap, FRAC_64_MULTIPLIER
+    // / 100)                 .unwrap();
+    //         }
+
+    //         assert_eq!(
+    //             pool_data.events[0].event_data[0].self_reward_ratio,
+    //             1_000_000
+    //         );
+    //         assert_eq!(
+    //             pool_data.events[0].event_data[0].other_reward_ratio,
+    //             500_000
+    //         );
+    //     }
+
+    //     #[test]
+    //     fn test_delegation() {
+    //         let publisher_1 = Pubkey::new_unique();
+    //         let publisher_stake_account = Pubkey::new_unique();
+    //         let mut pool_data = PoolData {
+    //             last_updated_epoch:       2,
+    //             claimable_rewards:        0,
+    //             publishers:               [Pubkey::default(); MAX_PUBLISHERS],
+    //             del_state:                [DelegationState::default(); MAX_PUBLISHERS],
+    //             self_del_state:           [DelegationState::default(); MAX_PUBLISHERS],
+    //             publisher_stake_accounts: [Pubkey::default(); MAX_PUBLISHERS],
+    //             events:                   [Event::default(); MAX_EVENTS],
+    //             num_events:               0,
+    //             num_slash_events:         [0; MAX_PUBLISHERS],
+    //             delegation_fees:          [0; MAX_PUBLISHERS],
+    //         };
+
+    //         pool_data.publishers[0] = publisher_1;
+    //         pool_data.publisher_stake_accounts[0] = publisher_stake_account;
+
+    //         pool_data
+    //             .add_delegation(&publisher_1, &publisher_stake_account, 123, 2)
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
+    //         assert_eq!(pool_data.self_del_state[0].delta_delegation, 123);
+
+    //         pool_data
+    //             .add_delegation(&publisher_1, &publisher_stake_account, 222, 2)
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
+    //         assert_eq!(pool_data.self_del_state[0].delta_delegation, 123 + 222);
+
+    //         pool_data
+    //             .add_delegation(&publisher_1, &Pubkey::new_unique(), 321, 2)
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.del_state[0].total_delegation, 0);
+    //         assert_eq!(pool_data.del_state[0].delta_delegation, 321);
+
+    //         pool_data
+    //             .add_delegation(&publisher_1, &Pubkey::new_unique(), 222, 2)
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.del_state[0].total_delegation, 0);
+    //         assert_eq!(pool_data.del_state[0].delta_delegation, 321 + 222);
+
+    //         pool_data
+    //             .remove_delegation(
+    //                 &publisher_1,
+    //                 &publisher_stake_account,
+    //                 111,
+    //                 PositionState::LOCKING,
+    //                 2,
+    //             )
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
+    //         assert_eq!(
+    //             pool_data.self_del_state[0].delta_delegation,
+    //             123 + 222 - 111
+    //         );
+
+    //         pool_data
+    //             .remove_delegation(
+    //                 &publisher_1,
+    //                 &publisher_stake_account,
+    //                 111,
+    //                 PositionState::LOCKED,
+    //                 2,
+    //             )
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
+    //         assert_eq!(
+    //             pool_data.self_del_state[0].delta_delegation,
+    //             123 + 222 - 111 - 111
+    //         );
+
+    //         // unlocking state should not affect the delta delegation
+    //         pool_data
+    //             .remove_delegation(
+    //                 &publisher_1,
+    //                 &publisher_stake_account,
+    //                 456,
+    //                 PositionState::UNLOCKED,
+    //                 2,
+    //             )
+    //             .unwrap();
+
+    //         assert_eq!(pool_data.self_del_state[0].total_delegation, 0);
+    //         assert_eq!(
+    //             pool_data.self_del_state[0].delta_delegation,
+    //             123 + 222 - 111 - 111
+    //         );
+
+    //         let res = pool_data.remove_delegation(
+    //             &publisher_1,
+    //             &publisher_stake_account,
+    //             456,
+    //             PositionState::PREUNLOCKING,
+    //             2,
+    //         );
+
+    //         assert_eq!(
+    //             res.unwrap_err(),
+    //             IntegrityPoolError::UnexpectedPositionState.into()
+    //         );
+
+    //         let res = pool_data.remove_delegation(
+    //             &publisher_1,
+    //             &publisher_stake_account,
+    //             456,
+    //             PositionState::UNLOCKING,
+    //             2,
+    //         );
+
+    //         assert_eq!(
+    //             res.unwrap_err(),
+    //             IntegrityPoolError::UnexpectedPositionState.into()
+    //         );
+    //     }
 }
diff --git a/staking/target/idl/staking.json b/staking/target/idl/staking.json
index 43d6b2f9..f5e617da 100644
--- a/staking/target/idl/staking.json
+++ b/staking/target/idl/staking.json
@@ -1204,10 +1204,7 @@
         {
           "name": "owner",
           "writable": true,
-          "signer": true,
-          "relations": [
-            "stake_account_metadata"
-          ]
+          "signer": true
         },
         {
           "name": "stake_account_positions",
diff --git a/staking/target/types/staking.ts b/staking/target/types/staking.ts
index 0717d4bb..770f94c9 100644
--- a/staking/target/types/staking.ts
+++ b/staking/target/types/staking.ts
@@ -1210,10 +1210,7 @@ export type Staking = {
         {
           "name": "owner",
           "writable": true,
-          "signer": true,
-          "relations": [
-            "stakeAccountMetadata"
-          ]
+          "signer": true
         },
         {
           "name": "stakeAccountPositions",