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#[derive(Clone, Debug, Eq, PartialEq, From)]
9pub struct RewardProofSiblings([B256; 160]);
10
11#[derive(Clone, Debug, Eq, PartialEq, From, Default)]
12pub struct RewardAuthRootInputs([B256; 7]);
13
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct RewardAuthData {
16    siblings: RewardProofSiblings,
17    auth_root_inputs: RewardAuthRootInputs,
18}
19
20impl RewardAuthData {
21    pub fn new(siblings: RewardProofSiblings, auth_root_inputs: RewardAuthRootInputs) -> Self {
22        RewardAuthData {
23            siblings,
24            auth_root_inputs,
25        }
26    }
27}
28
29impl From<([B256; 160], [B256; 7])> for RewardAuthData {
30    fn from((siblings, auth_root_inputs): ([B256; 160], [B256; 7])) -> Self {
31        RewardAuthData {
32            siblings: siblings.into(),
33            auth_root_inputs: auth_root_inputs.into(),
34        }
35    }
36}
37
38impl TryFrom<RewardAuthDataEncoded> for RewardAuthData {
39    type Error = alloy::sol_types::Error;
40
41    fn try_from(value: RewardAuthDataEncoded) -> Result<Self, Self::Error> {
42        let decoded: ([B256; 160], [B256; 7]) = SolValue::abi_decode(&value.0)?;
43        Ok(decoded.into())
44    }
45}
46
47#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From, Into)]
48pub struct RewardAuthDataEncoded(Bytes);
49
50impl From<RewardAuthData> for RewardAuthDataEncoded {
51    fn from(value: RewardAuthData) -> Self {
52        Self(
53            (value.siblings.0, value.auth_root_inputs.0)
54                .abi_encode()
55                .into(),
56        )
57    }
58}
59
60#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
61pub struct RewardClaimInput {
62    pub lifetime_rewards: U256,
63    pub auth_data: RewardAuthDataEncoded,
64}
65
66#[cfg(test)]
67mod tests {
68    use serde_json;
69
70    use super::*;
71
72    fn test_input() -> RewardClaimInput {
73        let siblings = RewardProofSiblings([B256::random(); 160]);
74        let auth_root_inputs = RewardAuthRootInputs([B256::random(); 7]);
75        RewardClaimInput {
76            lifetime_rewards: B256::random().into(),
77            auth_data: RewardAuthData::new(siblings, auth_root_inputs).into(),
78        }
79    }
80
81    #[test]
82    fn test_reward_claim_input_roundtrip_json() {
83        let original = test_input();
84        let json = serde_json::to_string(&original).unwrap();
85        let decoded: RewardClaimInput = serde_json::from_str(&json).unwrap();
86        assert_eq!(decoded, original);
87    }
88
89    #[test]
90    fn test_decode_abi_auth_data() {
91        let original = test_input();
92        let auth_data = RewardAuthData::try_from(original.auth_data.clone()).unwrap();
93        let json = serde_json::to_string(&original).unwrap();
94        let value: serde_json::Value = serde_json::from_str(&json).unwrap();
95        let auth_str = value.get("auth_data").unwrap().as_str().unwrap();
96        assert!(auth_str.starts_with("0x"));
97
98        // The auth data is sent "as-is" to the reward claim contract and need
99        // to ABI decode to the inner types in order for the contract to process
100        // them.
101        let (siblings, auth_root_inputs): ([B256; 160], [B256; 7]) =
102            SolValue::abi_decode(&alloy::hex::decode(&auth_str[2..]).unwrap()).unwrap();
103        assert_eq!(siblings, auth_data.siblings.0);
104        assert_eq!(auth_root_inputs, auth_data.auth_root_inputs.0);
105    }
106}