hotshot_contract_adapter/
reward.rs

1use alloy::{
2    primitives::{Bytes, B256, U256},
3    sol_types::SolValue,
4};
5use derive_more::{From, Into};
6use serde::{Deserialize, Serialize};
7
8// Defined in espresso-types, but we don't want to depend on the crate just for this constant
9pub const REWARD_MERKLE_TREE_V2_HEIGHT: usize = 160;
10pub const OTHER_AUTH_ROOT_INPUTS_LEN: usize = 7;
11
12type RewardAuthDataRaw = (
13    [B256; REWARD_MERKLE_TREE_V2_HEIGHT],
14    [B256; OTHER_AUTH_ROOT_INPUTS_LEN],
15);
16
17#[derive(Clone, Debug, Eq, PartialEq, From)]
18pub struct RewardProofSiblings([B256; REWARD_MERKLE_TREE_V2_HEIGHT]);
19
20#[derive(Clone, Debug, Eq, PartialEq, From, Default)]
21pub struct RewardAuthRootInputs([B256; OTHER_AUTH_ROOT_INPUTS_LEN]);
22
23#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct RewardAuthData {
25    siblings: RewardProofSiblings,
26    auth_root_inputs: RewardAuthRootInputs,
27}
28
29impl RewardAuthData {
30    /// Create reward auth data from proof siblings.
31    ///
32    /// Auth root inputs (other than the reward merkle tree root) are currently
33    /// all zero placeholder values.
34    pub fn new(siblings: RewardProofSiblings) -> Self {
35        RewardAuthData {
36            siblings,
37            auth_root_inputs: Default::default(),
38        }
39    }
40}
41
42impl From<RewardAuthDataRaw> for RewardAuthData {
43    fn from((siblings, auth_root_inputs): RewardAuthDataRaw) -> Self {
44        RewardAuthData {
45            siblings: siblings.into(),
46            auth_root_inputs: auth_root_inputs.into(),
47        }
48    }
49}
50
51impl TryFrom<RewardAuthDataEncoded> for RewardAuthData {
52    type Error = alloy::sol_types::Error;
53
54    fn try_from(value: RewardAuthDataEncoded) -> Result<Self, Self::Error> {
55        let decoded: RewardAuthDataRaw = SolValue::abi_decode(&value.0)?;
56        Ok(decoded.into())
57    }
58}
59
60#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From, Into)]
61pub struct RewardAuthDataEncoded(Bytes);
62
63impl From<RewardAuthData> for RewardAuthDataEncoded {
64    fn from(value: RewardAuthData) -> Self {
65        Self(
66            (value.siblings.0, value.auth_root_inputs.0)
67                .abi_encode()
68                .into(),
69        )
70    }
71}
72
73#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
74pub struct RewardClaimInput {
75    pub lifetime_rewards: U256,
76    pub auth_data: RewardAuthDataEncoded,
77}
78
79#[cfg(test)]
80mod tests {
81    use std::array::from_fn;
82
83    use serde_json;
84
85    use super::*;
86
87    impl RewardAuthData {
88        fn random() -> Self {
89            (from_fn(|_| B256::random()), from_fn(|_| B256::random())).into()
90        }
91    }
92
93    impl RewardClaimInput {
94        fn random() -> Self {
95            Self {
96                lifetime_rewards: B256::random().into(),
97                auth_data: RewardAuthData::random().into(),
98            }
99        }
100    }
101
102    #[test]
103    fn test_reward_claim_input_roundtrip_json() {
104        let original = RewardClaimInput::random();
105        let json = serde_json::to_string(&original).unwrap();
106        let decoded: RewardClaimInput = serde_json::from_str(&json).unwrap();
107        assert_eq!(decoded, original);
108    }
109
110    #[test]
111    fn test_decode_abi_auth_data() {
112        let original = RewardClaimInput::random();
113        let auth_data = RewardAuthData::try_from(original.auth_data.clone()).unwrap();
114        let json = serde_json::to_string(&original).unwrap();
115        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
116        let auth_str = value.get("auth_data").unwrap().as_str().unwrap();
117        assert!(auth_str.starts_with("0x"));
118
119        // The auth data is sent "as-is" to the reward claim contract and needs to ABI decode to the
120        // inner types in order for the contract to process thenm.
121        let (siblings, auth_root_inputs): RewardAuthDataRaw =
122            SolValue::abi_decode(&alloy::hex::decode(&auth_str[2..]).unwrap()).unwrap();
123        assert_eq!(siblings, auth_data.siblings.0);
124        assert_eq!(auth_root_inputs, auth_data.auth_root_inputs.0);
125    }
126}