Skip to main content

casper_rust_wasm_sdk/sdk/rpcs/
get_reward.rs

1#[cfg(all(feature = "js", target_arch = "wasm32"))]
2use crate::types::identifier::block_identifier::BlockIdentifier;
3use crate::{
4    types::{public_key::PublicKey, sdk_error::SdkError, verbosity::Verbosity},
5    SDK,
6};
7use casper_client::{
8    cli::get_reward as get_reward_cli, get_reward as get_reward_lib,
9    rpcs::results::GetRewardResult as _GetRewardResult, rpcs::EraIdentifier, JsonRpcId,
10    SuccessResponse,
11};
12#[cfg(all(feature = "js", target_arch = "wasm32"))]
13use casper_types::EraId;
14#[cfg(all(feature = "js", target_arch = "wasm32"))]
15use gloo_utils::format::JsValueSerdeExt;
16use rand::RngExt;
17#[cfg(all(feature = "js", target_arch = "wasm32"))]
18use serde::{Deserialize, Serialize};
19#[cfg(all(feature = "js", target_arch = "wasm32"))]
20use wasm_bindgen::prelude::*;
21
22/// Wrapper struct for the `GetRewardResult` from casper_client.
23#[cfg(all(feature = "js", target_arch = "wasm32"))]
24#[derive(Debug, Deserialize, Serialize)]
25#[cfg_attr(feature = "js", wasm_bindgen)]
26pub struct GetRewardResult(_GetRewardResult);
27
28#[cfg(all(feature = "js", target_arch = "wasm32"))]
29impl From<GetRewardResult> for _GetRewardResult {
30    fn from(result: GetRewardResult) -> Self {
31        result.0
32    }
33}
34
35#[cfg(all(feature = "js", target_arch = "wasm32"))]
36impl From<_GetRewardResult> for GetRewardResult {
37    fn from(result: _GetRewardResult) -> Self {
38        GetRewardResult(result)
39    }
40}
41
42#[cfg(all(feature = "js", target_arch = "wasm32"))]
43#[cfg_attr(feature = "js", wasm_bindgen)]
44impl GetRewardResult {
45    /// Gets the API version as a JsValue.
46    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
47    #[cfg(feature = "js")]
48    pub fn api_version(&self) -> JsValue {
49        JsValue::from_serde(&self.0.api_version).unwrap()
50    }
51
52    /// Gets the reward amount as a JsValue.
53    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
54    #[cfg(feature = "js")]
55    pub fn reward_amount(&self) -> JsValue {
56        JsValue::from_serde(&self.0.reward_amount).unwrap()
57    }
58
59    /// Gets the era id as a JsValue.
60    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
61    #[cfg(feature = "js")]
62    pub fn era_id(&self) -> JsValue {
63        JsValue::from_serde(&self.0.era_id).unwrap()
64    }
65
66    /// Gets the delegation rate.
67    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
68    pub fn delegation_rate(&self) -> u8 {
69        self.0.delegation_rate
70    }
71
72    /// Gets the switch block hash as a JsValue.
73    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
74    #[cfg(feature = "js")]
75    pub fn switch_block_hash(&self) -> JsValue {
76        JsValue::from_serde(&self.0.switch_block_hash).unwrap()
77    }
78
79    /// Converts the GetRewardResult to a JsValue.
80    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "toJson"))]
81    #[cfg(feature = "js")]
82    pub fn to_json(&self) -> JsValue {
83        JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
84    }
85}
86
87/// Options for the `get_reward` method.
88#[derive(Debug, Deserialize, Clone, Default, Serialize)]
89#[cfg(all(feature = "js", target_arch = "wasm32"))]
90#[cfg_attr(
91    feature = "js",
92    wasm_bindgen(js_name = "getRewardOptions", getter_with_clone)
93)]
94pub struct GetRewardOptions {
95    pub validator_public_key: Option<PublicKey>,
96    pub validator_public_key_as_string: Option<String>,
97    pub delegator_public_key: Option<PublicKey>,
98    pub delegator_public_key_as_string: Option<String>,
99    pub maybe_era_id: Option<u64>,
100    pub maybe_era_id_as_string: Option<String>,
101    pub maybe_block_identifier: Option<BlockIdentifier>,
102    pub rpc_address: Option<String>,
103    pub verbosity: Option<Verbosity>,
104}
105
106#[cfg(all(feature = "js", target_arch = "wasm32"))]
107#[cfg_attr(feature = "js", wasm_bindgen)]
108impl SDK {
109    /// Parses reward options from a JsValue.
110    #[cfg(feature = "js")]
111    pub fn get_reward_options(&self, options: JsValue) -> Result<GetRewardOptions, JsError> {
112        options
113            .into_serde::<GetRewardOptions>()
114            .map_err(|err| JsError::new(&format!("Error deserializing options: {err:?}")))
115    }
116
117    /// Retrieves validator/delegator reward via JSON-RPC `info_get_reward`.
118    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "get_reward"))]
119    #[cfg(feature = "js")]
120    pub async fn get_reward_js_alias(
121        &self,
122        options: Option<GetRewardOptions>,
123    ) -> Result<GetRewardResult, JsError> {
124        let GetRewardOptions {
125            validator_public_key,
126            validator_public_key_as_string,
127            delegator_public_key,
128            delegator_public_key_as_string,
129            maybe_era_id,
130            maybe_era_id_as_string,
131            maybe_block_identifier,
132            verbosity,
133            rpc_address,
134        } = options.unwrap_or_default();
135
136        let validator = if let Some(pk) = validator_public_key {
137            pk
138        } else if let Some(hex) = validator_public_key_as_string {
139            PublicKey::new(&hex).map_err(|err| JsError::new(&format!("{err:?}")))?
140        } else {
141            return Err(JsError::new(
142                "validator_public_key or validator_public_key_as_string is required",
143            ));
144        };
145
146        let delegator = if let Some(pk) = delegator_public_key {
147            Some(pk)
148        } else if let Some(hex) = delegator_public_key_as_string {
149            Some(PublicKey::new(&hex).map_err(|err| JsError::new(&format!("{err:?}")))?)
150        } else {
151            None
152        };
153
154        let maybe_era_identifier = if let Some(era_id) = maybe_era_id {
155            Some(EraIdentifier::Era(EraId::new(era_id)))
156        } else if let Some(era_str) = maybe_era_id_as_string {
157            if era_str.is_empty() {
158                None
159            } else {
160                let era_id: u64 = era_str
161                    .parse()
162                    .map_err(|err| JsError::new(&format!("Invalid era id: {err:?}")))?;
163                Some(EraIdentifier::Era(EraId::new(era_id)))
164            }
165        } else {
166            maybe_block_identifier.map(|block| EraIdentifier::Block(block.into()))
167        };
168
169        let result = self
170            .get_reward(
171                validator,
172                delegator,
173                maybe_era_identifier,
174                verbosity,
175                rpc_address,
176            )
177            .await;
178        match result {
179            Ok(data) => Ok(data.result.into()),
180            Err(err) => {
181                let err = &format!("Error occurred with {err:?}");
182                Err(JsError::new(err))
183            }
184        }
185    }
186
187    /// JavaScript alias for `get_reward`.
188    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "info_get_reward"))]
189    #[deprecated(note = "This function is an alias. Please use `get_reward` instead.")]
190    #[allow(deprecated)]
191    #[cfg(feature = "js")]
192    pub async fn info_get_reward(
193        &self,
194        options: Option<GetRewardOptions>,
195    ) -> Result<GetRewardResult, JsError> {
196        self.get_reward_js_alias(options).await
197    }
198}
199
200impl SDK {
201    /// Retrieves reward information (`info_get_reward`).
202    ///
203    /// # Arguments
204    ///
205    /// * `validator` - Validator public key.
206    /// * `maybe_delegator` - Optional delegator public key (validator reward when `None`).
207    /// * `maybe_era_identifier` - Optional era or block identifier (`None` = last finalized era).
208    /// * `verbosity` - Optional verbosity.
209    /// * `rpc_address` - Optional RPC URL.
210    pub async fn get_reward(
211        &self,
212        validator: PublicKey,
213        maybe_delegator: Option<PublicKey>,
214        maybe_era_identifier: Option<EraIdentifier>,
215        verbosity: Option<Verbosity>,
216        rpc_address: Option<String>,
217    ) -> Result<SuccessResponse<_GetRewardResult>, SdkError> {
218        let random_id = rand::rng().random::<u64>().to_string();
219        get_reward_lib(
220            JsonRpcId::from(random_id),
221            &self.get_rpc_address(rpc_address),
222            self.get_verbosity(verbosity).into(),
223            maybe_era_identifier,
224            validator.into(),
225            maybe_delegator.map(Into::into),
226        )
227        .await
228        .map_err(SdkError::from)
229    }
230
231    /// CLI-string convenience wrapper around `info_get_reward`.
232    pub async fn get_reward_as_string(
233        &self,
234        validator: &str,
235        maybe_delegator: Option<&str>,
236        maybe_era_id: Option<&str>,
237        verbosity: Option<Verbosity>,
238        rpc_address: Option<String>,
239    ) -> Result<SuccessResponse<_GetRewardResult>, SdkError> {
240        let random_id = rand::rng().random::<u64>().to_string();
241        get_reward_cli(
242            &random_id,
243            &self.get_rpc_address(rpc_address),
244            self.get_verbosity(verbosity).into(),
245            maybe_era_id.unwrap_or(""),
246            validator,
247            maybe_delegator.unwrap_or(""),
248        )
249        .await
250        .map_err(SdkError::from)
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[tokio::test]
259    async fn test_get_reward_with_none_rpc_address() {
260        let sdk = SDK::new(None, None, None);
261        let error_message = "failed to parse node address as valid URL";
262        let result = sdk
263            .get_reward_as_string(
264                "01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
265                None,
266                None,
267                None,
268                None,
269            )
270            .await;
271        assert!(result.is_err());
272        let err_string = result.err().unwrap().to_string();
273        assert!(
274            err_string.contains(error_message)
275                || err_string.contains("Failed")
276                || err_string.contains("parse")
277                || err_string.contains("PublicKey")
278                || err_string.contains("Validator")
279        );
280    }
281
282    #[tokio::test]
283    async fn test_get_reward_with_error() {
284        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
285        let result = sdk
286            .get_reward_as_string(
287                "01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
288                None,
289                None,
290                None,
291                None,
292            )
293            .await;
294        assert!(result.is_err());
295    }
296}