chia_sdk_driver/primitives/action_layer/
reward_distributor.rs

1use chia_bls::Signature;
2use chia_protocol::{Bytes32, Coin, CoinSpend};
3use chia_puzzle_types::singleton::{LauncherSolution, SingletonArgs};
4use chia_puzzle_types::{
5    LineageProof, Proof,
6    singleton::{SingletonSolution, SingletonStruct},
7};
8use chia_sdk_types::puzzles::{
9    RawActionLayerSolution, ReserveFinalizerSolution, RewardDistributorCommitmentSlotValue,
10    RewardDistributorEntrySlotValue, RewardDistributorRewardSlotValue, RewardDistributorSlotNonce,
11    SlotInfo,
12};
13use chia_sdk_types::{Condition, Conditions};
14use clvm_traits::{FromClvm, clvm_list, match_tuple};
15use clvm_utils::{ToTreeHash, tree_hash};
16use clvmr::NodePtr;
17
18use crate::{
19    ActionLayer, ActionLayerSolution, ActionSingleton, Cat, CatSpend, DriverError, Layer, Puzzle,
20    RewardDistributorAddEntryAction, RewardDistributorAddIncentivesAction,
21    RewardDistributorCommitIncentivesAction, RewardDistributorInitiatePayoutAction,
22    RewardDistributorNewEpochAction, RewardDistributorRemoveEntryAction,
23    RewardDistributorStakeAction, RewardDistributorSyncAction, RewardDistributorUnstakeAction,
24    RewardDistributorWithdrawIncentivesAction, SingletonAction, SingletonLayer, Slot, Spend,
25    SpendContext,
26};
27
28use super::{Reserve, RewardDistributorConstants, RewardDistributorInfo, RewardDistributorState};
29
30#[derive(Debug, Clone)]
31pub struct RewardDistributorPendingSpendInfo {
32    pub actions: Vec<Spend>,
33
34    pub spent_reward_slots: Vec<RewardDistributorRewardSlotValue>,
35    pub spent_commitment_slots: Vec<RewardDistributorCommitmentSlotValue>,
36    pub spent_entry_slots: Vec<RewardDistributorEntrySlotValue>,
37
38    pub created_reward_slots: Vec<RewardDistributorRewardSlotValue>,
39    pub created_commitment_slots: Vec<RewardDistributorCommitmentSlotValue>,
40    pub created_entry_slots: Vec<RewardDistributorEntrySlotValue>,
41
42    pub latest_state: (NodePtr, RewardDistributorState),
43
44    pub signature: Signature,
45    pub other_cats: Vec<CatSpend>,
46}
47
48impl RewardDistributorPendingSpendInfo {
49    pub fn new(latest_state: RewardDistributorState) -> Self {
50        Self {
51            actions: vec![],
52            created_reward_slots: vec![],
53            created_commitment_slots: vec![],
54            created_entry_slots: vec![],
55            spent_reward_slots: vec![],
56            spent_commitment_slots: vec![],
57            spent_entry_slots: vec![],
58            latest_state: (NodePtr::NIL, latest_state),
59            signature: Signature::default(),
60            other_cats: vec![],
61        }
62    }
63
64    pub fn add_delta(&mut self, delta: RewardDistributorPendingSpendInfo) {
65        self.actions.extend(delta.actions);
66
67        self.spent_reward_slots.extend(delta.spent_reward_slots);
68        self.spent_commitment_slots
69            .extend(delta.spent_commitment_slots);
70        self.spent_entry_slots.extend(delta.spent_entry_slots);
71
72        self.created_reward_slots.extend(delta.created_reward_slots);
73        self.created_commitment_slots
74            .extend(delta.created_commitment_slots);
75        self.created_entry_slots.extend(delta.created_entry_slots);
76
77        self.latest_state = delta.latest_state;
78
79        // do not change pending signature
80        // or other cats
81    }
82}
83
84#[derive(Debug, Clone)]
85#[must_use]
86pub struct RewardDistributor {
87    pub coin: Coin,
88    pub proof: Proof,
89    pub info: RewardDistributorInfo,
90    pub reserve: Reserve,
91
92    pub pending_spend: RewardDistributorPendingSpendInfo,
93}
94
95impl RewardDistributor {
96    pub fn new(coin: Coin, proof: Proof, info: RewardDistributorInfo, reserve: Reserve) -> Self {
97        Self {
98            coin,
99            proof,
100            info,
101            reserve,
102            pending_spend: RewardDistributorPendingSpendInfo::new(info.state),
103        }
104    }
105}
106
107impl RewardDistributor {
108    #[allow(clippy::type_complexity)]
109    pub fn pending_info_delta_from_spend(
110        ctx: &mut SpendContext,
111        action_spend: Spend,
112        current_state_and_ephemeral: (NodePtr, RewardDistributorState),
113        constants: RewardDistributorConstants,
114    ) -> Result<RewardDistributorPendingSpendInfo, DriverError> {
115        let mut spent_reward_slots = vec![];
116        let mut spent_commitment_slots = vec![];
117        let mut spent_entry_slots = vec![];
118
119        let mut created_reward_slots = vec![];
120        let mut created_commitment_slots = vec![];
121        let mut created_entry_slots = vec![];
122
123        let new_epoch_action = RewardDistributorNewEpochAction::from_constants(&constants);
124        let new_epoch_hash = new_epoch_action.tree_hash();
125
126        let commit_incentives_action =
127            RewardDistributorCommitIncentivesAction::from_constants(&constants);
128        let commit_incentives_hash = commit_incentives_action.tree_hash();
129
130        let add_entry_action = RewardDistributorAddEntryAction::from_constants(&constants);
131        let add_entry_hash = add_entry_action.tree_hash();
132
133        let remove_entry_action = RewardDistributorRemoveEntryAction::from_constants(&constants);
134        let remove_entry_hash = remove_entry_action.tree_hash();
135
136        let stake_action = RewardDistributorStakeAction::from_constants(&constants);
137        let stake_hash = stake_action.tree_hash();
138
139        let unstake_action = RewardDistributorUnstakeAction::from_constants(&constants);
140        let unstake_hash = unstake_action.tree_hash();
141
142        let withdraw_incentives_action =
143            RewardDistributorWithdrawIncentivesAction::from_constants(&constants);
144        let withdraw_incentives_hash = withdraw_incentives_action.tree_hash();
145
146        let initiate_payout_action =
147            RewardDistributorInitiatePayoutAction::from_constants(&constants);
148        let initiate_payout_hash = initiate_payout_action.tree_hash();
149
150        let add_incentives_action =
151            RewardDistributorAddIncentivesAction::from_constants(&constants);
152        let add_incentives_hash = add_incentives_action.tree_hash();
153
154        let sync_action = RewardDistributorSyncAction::from_constants(&constants);
155        let sync_hash = sync_action.tree_hash();
156
157        let actual_solution = ctx.alloc(&clvm_list!(
158            current_state_and_ephemeral,
159            action_spend.solution
160        ))?;
161
162        let output = ctx.run(action_spend.puzzle, actual_solution)?;
163        let (new_state_and_ephemeral, _) =
164            ctx.extract::<match_tuple!((NodePtr, RewardDistributorState), NodePtr)>(output)?;
165
166        let raw_action_hash = ctx.tree_hash(action_spend.puzzle);
167
168        if raw_action_hash == new_epoch_hash {
169            created_reward_slots.push(RewardDistributorNewEpochAction::created_slot_value(
170                ctx,
171                action_spend.solution,
172            )?);
173            spent_reward_slots.push(RewardDistributorNewEpochAction::spent_slot_value(
174                ctx,
175                action_spend.solution,
176            )?);
177        } else if raw_action_hash == commit_incentives_hash {
178            let (comm, rews) = RewardDistributorCommitIncentivesAction::created_slot_values(
179                ctx,
180                constants.epoch_seconds,
181                action_spend.solution,
182            )?;
183
184            created_commitment_slots.push(comm);
185            created_reward_slots.extend(rews);
186            spent_reward_slots.push(RewardDistributorCommitIncentivesAction::spent_slot_value(
187                ctx,
188                action_spend.solution,
189            )?);
190        } else if raw_action_hash == add_entry_hash {
191            created_entry_slots.push(RewardDistributorAddEntryAction::created_slot_value(
192                ctx,
193                &current_state_and_ephemeral.1,
194                action_spend.solution,
195            )?);
196        } else if raw_action_hash == stake_hash {
197            created_entry_slots.push(RewardDistributorStakeAction::created_slot_value(
198                ctx,
199                &current_state_and_ephemeral.1,
200                action_spend.solution,
201            )?);
202        } else if raw_action_hash == remove_entry_hash {
203            spent_entry_slots.push(RewardDistributorRemoveEntryAction::spent_slot_value(
204                ctx,
205                action_spend.solution,
206            )?);
207        } else if raw_action_hash == unstake_hash {
208            spent_entry_slots.push(RewardDistributorUnstakeAction::spent_slot_value(
209                ctx,
210                action_spend.solution,
211            )?);
212        } else if raw_action_hash == withdraw_incentives_hash {
213            let (rew, cmt) = RewardDistributorWithdrawIncentivesAction::spent_slot_values(
214                ctx,
215                action_spend.solution,
216            )?;
217
218            spent_reward_slots.push(rew);
219            spent_commitment_slots.push(cmt);
220            created_reward_slots.push(
221                RewardDistributorWithdrawIncentivesAction::created_slot_value(
222                    ctx,
223                    constants.withdrawal_share_bps,
224                    action_spend.solution,
225                )?,
226            );
227        } else if raw_action_hash == initiate_payout_hash {
228            created_entry_slots.push(RewardDistributorInitiatePayoutAction::created_slot_value(
229                ctx,
230                &current_state_and_ephemeral.1,
231                action_spend.solution,
232            )?);
233            spent_entry_slots.push(RewardDistributorInitiatePayoutAction::spent_slot_value(
234                ctx,
235                action_spend.solution,
236            )?);
237        } else if raw_action_hash != add_incentives_hash && raw_action_hash != sync_hash {
238            // delegated state action has no effect on slots
239            return Err(DriverError::InvalidMerkleProof);
240        }
241
242        Ok(RewardDistributorPendingSpendInfo {
243            actions: vec![action_spend],
244            spent_reward_slots,
245            spent_commitment_slots,
246            spent_entry_slots,
247            created_reward_slots,
248            created_commitment_slots,
249            created_entry_slots,
250            latest_state: new_state_and_ephemeral,
251            signature: Signature::default(),
252            other_cats: vec![],
253        })
254    }
255
256    pub fn pending_info_from_spend(
257        ctx: &mut SpendContext,
258        inner_solution: NodePtr,
259        initial_state: RewardDistributorState,
260        constants: RewardDistributorConstants,
261    ) -> Result<RewardDistributorPendingSpendInfo, DriverError> {
262        let mut pending_spend_info = RewardDistributorPendingSpendInfo::new(initial_state);
263
264        let inner_solution =
265            ActionLayer::<RewardDistributorState, NodePtr>::parse_solution(ctx, inner_solution)?;
266
267        for raw_action in &inner_solution.action_spends {
268            let delta = Self::pending_info_delta_from_spend(
269                ctx,
270                *raw_action,
271                pending_spend_info.latest_state,
272                constants,
273            )?;
274
275            pending_spend_info.add_delta(delta);
276        }
277
278        Ok(pending_spend_info)
279    }
280
281    pub fn from_spend(
282        ctx: &mut SpendContext,
283        spend: &CoinSpend,
284        reserve_lineage_proof: Option<LineageProof>,
285        constants: RewardDistributorConstants,
286    ) -> Result<Option<Self>, DriverError> {
287        let coin = spend.coin;
288        let puzzle_ptr = ctx.alloc(&spend.puzzle_reveal)?;
289        let puzzle = Puzzle::parse(ctx, puzzle_ptr);
290        let solution_ptr = ctx.alloc(&spend.solution)?;
291
292        let Some(info) = RewardDistributorInfo::parse(ctx, puzzle, constants)? else {
293            return Ok(None);
294        };
295
296        let solution = ctx.extract::<SingletonSolution<NodePtr>>(solution_ptr)?;
297        let proof = solution.lineage_proof;
298
299        let pending_spend =
300            Self::pending_info_from_spend(ctx, solution.inner_solution, info.state, constants)?;
301
302        let inner_solution =
303            RawActionLayerSolution::<NodePtr, NodePtr, ReserveFinalizerSolution>::from_clvm(
304                ctx,
305                solution.inner_solution,
306            )?;
307
308        let reserve = Reserve::new(
309            inner_solution.finalizer_solution.reserve_parent_id,
310            reserve_lineage_proof.unwrap_or(LineageProof {
311                parent_parent_coin_info: Bytes32::default(),
312                parent_inner_puzzle_hash: Bytes32::default(),
313                parent_amount: 0,
314            }), // dummy default value
315            constants.reserve_asset_id,
316            SingletonStruct::new(info.constants.launcher_id)
317                .tree_hash()
318                .into(),
319            0,
320            info.state.total_reserves,
321        );
322
323        Ok(Some(RewardDistributor {
324            coin,
325            proof,
326            info,
327            reserve,
328            pending_spend,
329        }))
330    }
331
332    pub fn child_lineage_proof(&self) -> LineageProof {
333        LineageProof {
334            parent_parent_coin_info: self.coin.parent_coin_info,
335            parent_inner_puzzle_hash: self.info.inner_puzzle_hash().into(),
336            parent_amount: self.coin.amount,
337        }
338    }
339
340    pub fn from_parent_spend(
341        ctx: &mut SpendContext,
342        parent_spend: &CoinSpend,
343        constants: RewardDistributorConstants,
344    ) -> Result<Option<Self>, DriverError>
345    where
346        Self: Sized,
347    {
348        let Some(parent_registry) = Self::from_spend(ctx, parent_spend, None, constants)? else {
349            return Ok(None);
350        };
351
352        let new_info = parent_registry
353            .info
354            .with_state(parent_registry.pending_spend.latest_state.1);
355
356        Ok(Some(RewardDistributor {
357            coin: Coin::new(
358                parent_registry.coin.coin_id(),
359                new_info.puzzle_hash().into(),
360                1,
361            ),
362            proof: Proof::Lineage(parent_registry.child_lineage_proof()),
363            info: new_info,
364            reserve: parent_registry.reserve.child(new_info.state.total_reserves),
365            pending_spend: RewardDistributorPendingSpendInfo::new(new_info.state),
366        }))
367    }
368
369    pub fn child(&self, child_state: RewardDistributorState) -> Self {
370        let new_info = self.info.with_state(child_state);
371        let new_coin = Coin::new(self.coin.coin_id(), new_info.puzzle_hash().into(), 1);
372        let new_reserve = self.reserve.child(child_state.total_reserves);
373
374        RewardDistributor {
375            coin: new_coin,
376            proof: Proof::Lineage(self.child_lineage_proof()),
377            info: new_info,
378            reserve: new_reserve,
379            pending_spend: RewardDistributorPendingSpendInfo::new(new_info.state),
380        }
381    }
382
383    #[allow(clippy::type_complexity)]
384    pub fn from_launcher_solution(
385        ctx: &mut SpendContext,
386        launcher_coin: Coin,
387        launcher_solution: NodePtr,
388    ) -> Result<Option<(RewardDistributorConstants, RewardDistributorState, Coin)>, DriverError>
389    where
390        Self: Sized,
391    {
392        let Ok(launcher_solution) =
393            ctx.extract::<LauncherSolution<(u64, RewardDistributorConstants)>>(launcher_solution)
394        else {
395            return Ok(None);
396        };
397
398        let launcher_id = launcher_coin.coin_id();
399        let (first_epoch_start, constants) = launcher_solution.key_value_list;
400
401        if constants != constants.with_launcher_id(launcher_id) {
402            return Err(DriverError::Custom(
403                "Distributor constants invalid".to_string(),
404            ));
405        }
406
407        let distributor_eve_coin =
408            Coin::new(launcher_id, launcher_solution.singleton_puzzle_hash, 1);
409
410        let initial_state = RewardDistributorState::initial(first_epoch_start);
411
412        Ok(Some((constants, initial_state, distributor_eve_coin)))
413    }
414
415    #[allow(clippy::type_complexity)]
416    pub fn from_eve_coin_spend(
417        ctx: &mut SpendContext,
418        constants: RewardDistributorConstants,
419        initial_state: RewardDistributorState,
420        eve_coin_spend: &CoinSpend,
421        reserve_parent_id: Bytes32,
422        reserve_lineage_proof: LineageProof,
423    ) -> Result<Option<(RewardDistributor, Slot<RewardDistributorRewardSlotValue>)>, DriverError>
424    where
425        Self: Sized,
426    {
427        let eve_coin_puzzle_ptr = ctx.alloc(&eve_coin_spend.puzzle_reveal)?;
428        let eve_coin_puzzle = Puzzle::parse(ctx, eve_coin_puzzle_ptr);
429        let Some(eve_coin_puzzle) = SingletonLayer::<NodePtr>::parse_puzzle(ctx, eve_coin_puzzle)?
430        else {
431            return Err(DriverError::Custom("Eve coin not a singleton".to_string()));
432        };
433
434        let eve_coin_inner_puzzle_hash = tree_hash(ctx, eve_coin_puzzle.inner_puzzle);
435
436        let eve_coin_solution_ptr = ctx.alloc(&eve_coin_spend.solution)?;
437        let eve_coin_output = ctx.run(eve_coin_puzzle_ptr, eve_coin_solution_ptr)?;
438        let eve_coin_output = ctx.extract::<Conditions<NodePtr>>(eve_coin_output)?;
439
440        let Some(Condition::CreateCoin(odd_create_coin)) = eve_coin_output.into_iter().find(|c| {
441            if let Condition::CreateCoin(create_coin) = c {
442                // singletons with amount != 1 are weird and I don't support them
443                create_coin.amount % 2 == 1
444            } else {
445                false
446            }
447        }) else {
448            return Err(DriverError::Custom(
449                "Eve coin did not create a coin".to_string(),
450            ));
451        };
452
453        let new_coin = Coin::new(
454            eve_coin_spend.coin.coin_id(),
455            odd_create_coin.puzzle_hash,
456            odd_create_coin.amount,
457        );
458        let lineage_proof = LineageProof {
459            parent_parent_coin_info: eve_coin_spend.coin.parent_coin_info,
460            parent_inner_puzzle_hash: eve_coin_inner_puzzle_hash.into(),
461            parent_amount: eve_coin_spend.coin.amount,
462        };
463        let reserve = Reserve::new(
464            reserve_parent_id,
465            reserve_lineage_proof,
466            constants.reserve_asset_id,
467            SingletonStruct::new(constants.launcher_id)
468                .tree_hash()
469                .into(),
470            0,
471            0,
472        );
473        let new_distributor = RewardDistributor::new(
474            new_coin,
475            Proof::Lineage(lineage_proof),
476            RewardDistributorInfo::new(initial_state, constants),
477            reserve,
478        );
479
480        if SingletonArgs::curry_tree_hash(
481            constants.launcher_id,
482            new_distributor.info.inner_puzzle_hash(),
483        ) != new_distributor.coin.puzzle_hash.into()
484        {
485            return Err(DriverError::Custom(
486                "Distributor singleton puzzle hash mismatch".to_string(),
487            ));
488        }
489
490        let slot_value = RewardDistributorRewardSlotValue {
491            epoch_start: initial_state.round_time_info.epoch_end,
492            next_epoch_initialized: false,
493            rewards: 0,
494        };
495
496        let slot = Slot::new(
497            lineage_proof,
498            SlotInfo::from_value(
499                constants.launcher_id,
500                RewardDistributorSlotNonce::REWARD.to_u64(),
501                slot_value,
502            ),
503        );
504
505        Ok(Some((new_distributor, slot)))
506    }
507
508    pub fn set_pending_signature(&mut self, signature: Signature) {
509        self.pending_spend.signature = signature;
510    }
511
512    pub fn set_pending_other_cats(&mut self, other_cats: Vec<CatSpend>) {
513        self.pending_spend.other_cats = other_cats;
514    }
515}
516
517impl ActionSingleton for RewardDistributor {
518    type State = RewardDistributorState;
519    type Constants = RewardDistributorConstants;
520}
521
522impl RewardDistributor {
523    pub fn finish_spend(
524        self,
525        ctx: &mut SpendContext,
526        other_cat_spends: Vec<CatSpend>,
527    ) -> Result<(Self, Signature), DriverError> {
528        let layers = self.info.into_layers(ctx)?;
529
530        let puzzle = layers.construct_puzzle(ctx)?;
531
532        let action_puzzle_hashes = self
533            .pending_spend
534            .actions
535            .iter()
536            .map(|a| ctx.tree_hash(a.puzzle).into())
537            .collect::<Vec<Bytes32>>();
538
539        let finalizer_solution = ctx.alloc(&ReserveFinalizerSolution {
540            reserve_parent_id: self.reserve.coin.parent_coin_info,
541        })?;
542
543        let child = self.child(self.pending_spend.latest_state.1);
544        let solution = layers.construct_solution(
545            ctx,
546            SingletonSolution {
547                lineage_proof: self.proof,
548                amount: self.coin.amount,
549                inner_solution: ActionLayerSolution {
550                    proofs: layers
551                        .inner_puzzle
552                        .get_proofs(
553                            &RewardDistributorInfo::action_puzzle_hashes(&self.info.constants),
554                            &action_puzzle_hashes,
555                        )
556                        .ok_or(DriverError::Custom(
557                            "Couldn't build proofs for one or more actions".to_string(),
558                        ))?,
559                    action_spends: self.pending_spend.actions,
560                    finalizer_solution,
561                },
562            },
563        )?;
564
565        let my_spend = Spend::new(puzzle, solution);
566        ctx.spend(self.coin, my_spend)?;
567
568        let cat_spend = self.reserve.cat_spend_for_reserve_finalizer_controller(
569            ctx,
570            self.info.state,
571            self.info.inner_puzzle_hash().into(),
572            solution,
573        )?;
574
575        let mut cat_spends = other_cat_spends;
576        cat_spends.push(cat_spend);
577        cat_spends.extend(self.pending_spend.other_cats);
578        Cat::spend_all(ctx, &cat_spends)?;
579
580        Ok((child, self.pending_spend.signature))
581    }
582
583    pub fn new_action<A>(&self) -> A
584    where
585        A: SingletonAction<Self>,
586    {
587        A::from_constants(&self.info.constants)
588    }
589
590    pub fn created_slot_value_to_slot<SlotValue>(
591        &self,
592        slot_value: SlotValue,
593        nonce: RewardDistributorSlotNonce,
594    ) -> Slot<SlotValue>
595    where
596        SlotValue: Copy + ToTreeHash,
597    {
598        Slot::new(
599            LineageProof {
600                parent_parent_coin_info: self.coin.parent_coin_info,
601                parent_inner_puzzle_hash: self.info.inner_puzzle_hash().into(),
602                parent_amount: self.coin.amount,
603            },
604            SlotInfo::from_value(self.info.constants.launcher_id, nonce.to_u64(), slot_value),
605        )
606    }
607
608    pub fn insert_action_spend(
609        &mut self,
610        ctx: &mut SpendContext,
611        action_spend: Spend,
612    ) -> Result<(), DriverError> {
613        let delta = Self::pending_info_delta_from_spend(
614            ctx,
615            action_spend,
616            self.pending_spend.latest_state,
617            self.info.constants,
618        )?;
619
620        self.pending_spend.add_delta(delta);
621
622        Ok(())
623    }
624
625    pub fn actual_reward_slot_value(
626        &self,
627        slot: Slot<RewardDistributorRewardSlotValue>,
628    ) -> Slot<RewardDistributorRewardSlotValue> {
629        let mut slot = slot;
630
631        for slot_value in &self.pending_spend.created_reward_slots {
632            if slot_value.epoch_start == slot.info.value.epoch_start {
633                slot = self
634                    .created_slot_value_to_slot(*slot_value, RewardDistributorSlotNonce::REWARD);
635            }
636        }
637
638        slot
639    }
640
641    pub fn actual_entry_slot_value(
642        &self,
643        slot: Slot<RewardDistributorEntrySlotValue>,
644    ) -> Slot<RewardDistributorEntrySlotValue> {
645        let mut slot = slot;
646
647        for slot_value in &self.pending_spend.created_entry_slots {
648            if slot_value.payout_puzzle_hash == slot.info.value.payout_puzzle_hash {
649                slot =
650                    self.created_slot_value_to_slot(*slot_value, RewardDistributorSlotNonce::ENTRY);
651            }
652        }
653
654        slot
655    }
656
657    pub fn actual_commitment_slot_value(
658        &self,
659        slot: Slot<RewardDistributorCommitmentSlotValue>,
660    ) -> Slot<RewardDistributorCommitmentSlotValue> {
661        let mut slot = slot;
662
663        for slot_value in &self.pending_spend.created_commitment_slots {
664            if slot_value.epoch_start == slot.info.value.epoch_start {
665                slot = self.created_slot_value_to_slot(
666                    *slot_value,
667                    RewardDistributorSlotNonce::COMMITMENT,
668                );
669            }
670        }
671
672        slot
673    }
674}