near_api/stake.rs
1use std::collections::BTreeMap;
2
3use near_api_types::{
4 AccountId, Data, EpochReference, NearGas, NearToken, Reference,
5 stake::{RewardFeeFraction, StakingPoolInfo, UserStakeBalance},
6};
7use near_openapi_client::types::{RpcError, RpcQueryResponse};
8
9use crate::{
10 NetworkConfig,
11 advanced::{
12 ResponseHandler, RpcBuilder, query_request::QueryRequest, query_rpc::SimpleQueryRpc,
13 validator_rpc::SimpleValidatorRpc,
14 },
15 common::{
16 query::{
17 CallResultHandler, MultiQueryHandler, MultiRequestBuilder, PostprocessHandler,
18 RequestBuilder, RpcType, RpcValidatorHandler, ViewStateHandler,
19 },
20 utils::{from_base64, near_data_to_near_token, to_base64},
21 },
22 config::RetryResponse,
23 contract::Contract,
24 errors::{BuilderError, QueryCreationError, QueryError, SendRequestError},
25 transactions::ConstructTransaction,
26};
27
28type Result<T> = core::result::Result<T, BuilderError>;
29
30/// A wrapper struct that simplifies interactions with the [Staking Pool](https://github.com/near/core-contracts/tree/master/staking-pool) standard on behalf of the account.
31///
32/// Delegation is a wrapper that provides the functionality to manage user account stake in
33/// the staking pool.
34#[derive(Clone, Debug)]
35pub struct Delegation(pub AccountId);
36
37impl Delegation {
38 /// Prepares a new contract query (`get_account_staked_balance`) for fetching the staked balance ([NearToken]) of the account on the staking pool.
39 ///
40 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
41 ///
42 /// ## Example
43 /// ```rust,no_run
44 /// use near_api::*;
45 ///
46 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
47 /// let balance = Staking::delegation("alice.testnet".parse()?)
48 /// .view_staked_balance("pool.testnet".parse()?)?
49 /// .fetch_from_testnet()
50 /// .await?;
51 /// println!("Staked balance: {:?}", balance);
52 /// # Ok(())
53 /// # }
54 /// ```
55 pub fn view_staked_balance(
56 &self,
57 pool: AccountId,
58 ) -> Result<RequestBuilder<PostprocessHandler<NearToken, CallResultHandler<u128>>>> {
59 Ok(Contract(pool)
60 .call_function(
61 "get_account_staked_balance",
62 serde_json::json!({
63 "account_id": self.0.clone(),
64 }),
65 )?
66 .read_only()
67 .map(near_data_to_near_token))
68 }
69
70 /// Prepares a new contract query (`get_account_unstaked_balance`) for fetching the unstaked(free, not used for staking) balance ([NearToken]) of the account on the staking pool.
71 ///
72 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
73 ///
74 /// ## Example
75 /// ```rust,no_run
76 /// use near_api::*;
77 ///
78 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
79 /// let balance = Staking::delegation("alice.testnet".parse()?)
80 /// .view_unstaked_balance("pool.testnet".parse()?)?
81 /// .fetch_from_testnet()
82 /// .await?;
83 /// println!("Unstaked balance: {:?}", balance);
84 /// # Ok(())
85 /// # }
86 /// ```
87 pub fn view_unstaked_balance(
88 &self,
89 pool: AccountId,
90 ) -> Result<RequestBuilder<PostprocessHandler<NearToken, CallResultHandler<u128>>>> {
91 Ok(Contract(pool)
92 .call_function(
93 "get_account_unstaked_balance",
94 serde_json::json!({
95 "account_id": self.0.clone(),
96 }),
97 )?
98 .read_only()
99 .map(near_data_to_near_token))
100 }
101
102 /// Prepares a new contract query (`get_account_total_balance`) for fetching the total balance ([NearToken]) of the account (free + staked) on the staking pool.
103 ///
104 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
105 ///
106 /// ## Example
107 /// ```rust,no_run
108 /// use near_api::*;
109 ///
110 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
111 /// let balance = Staking::delegation("alice.testnet".parse()?)
112 /// .view_total_balance("pool.testnet".parse()?)?
113 /// .fetch_from_testnet()
114 /// .await?;
115 /// println!("Total balance: {:?}", balance);
116 /// # Ok(())
117 /// # }
118 /// ```
119 pub fn view_total_balance(
120 &self,
121 pool: AccountId,
122 ) -> Result<RequestBuilder<PostprocessHandler<NearToken, CallResultHandler<u128>>>> {
123 Ok(Contract(pool)
124 .call_function(
125 "get_account_total_balance",
126 serde_json::json!({
127 "account_id": self.0.clone(),
128 }),
129 )?
130 .read_only()
131 .map(near_data_to_near_token))
132 }
133
134 /// Returns a full information about the staked balance ([UserStakeBalance]) of the account on the staking pool.
135 ///
136 /// This is a complex query that requires 3 calls (get_account_staked_balance, get_account_unstaked_balance, get_account_total_balance) to the staking pool contract.
137 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
138 ///
139 /// ## Example
140 /// ```rust,no_run
141 /// use near_api::*;
142 ///
143 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
144 /// let balance = Staking::delegation("alice.testnet".parse()?)
145 /// .view_balance("pool.testnet".parse()?)?
146 /// .fetch_from_testnet()
147 /// .await?;
148 /// println!("Balance: {:?}", balance);
149 /// # Ok(())
150 /// # }
151 /// ```
152 #[allow(clippy::type_complexity)]
153 pub fn view_balance(
154 &self,
155 pool: AccountId,
156 ) -> Result<
157 MultiRequestBuilder<
158 PostprocessHandler<
159 UserStakeBalance,
160 MultiQueryHandler<(
161 CallResultHandler<NearToken>,
162 CallResultHandler<NearToken>,
163 CallResultHandler<NearToken>,
164 )>,
165 >,
166 >,
167 > {
168 let postprocess = MultiQueryHandler::default();
169
170 let multiquery = MultiRequestBuilder::new(postprocess, Reference::Optimistic)
171 .add_query_builder(self.view_staked_balance(pool.clone())?)
172 .add_query_builder(self.view_unstaked_balance(pool.clone())?)
173 .add_query_builder(self.view_total_balance(pool)?)
174 .map(
175 |(staked, unstaked, total): (Data<NearToken>, Data<NearToken>, Data<NearToken>)| {
176 UserStakeBalance {
177 staked: staked.data,
178 unstaked: unstaked.data,
179 total: total.data,
180 }
181 },
182 );
183 Ok(multiquery)
184 }
185
186 /// Prepares a new contract query (`is_account_unstaked_balance_available`) for checking if the unstaked balance of the account is available for withdrawal.
187 ///
188 /// Some pools configures minimum withdrawal period in epochs, so the balance is not available for withdrawal immediately.
189 ///
190 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
191 ///
192 /// ## Example
193 /// ```rust,no_run
194 /// use near_api::*;
195 ///
196 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
197 /// let is_available = Staking::delegation("alice.testnet".parse()?)
198 /// .is_account_unstaked_balance_available_for_withdrawal("pool.testnet".parse()?)?
199 /// .fetch_from_testnet()
200 /// .await?;
201 /// println!("Is available: {:?}", is_available);
202 /// # Ok(())
203 /// # }
204 /// ```
205 pub fn is_account_unstaked_balance_available_for_withdrawal(
206 &self,
207 pool: AccountId,
208 ) -> Result<RequestBuilder<CallResultHandler<bool>>> {
209 Ok(Contract(pool)
210 .call_function(
211 "is_account_unstaked_balance_available",
212 serde_json::json!({
213 "account_id": self.0.clone(),
214 }),
215 )?
216 .read_only())
217 }
218
219 /// Prepares a new transaction contract call (`deposit`) for depositing funds into the staking pool.
220 /// Please note that your deposit is not staked, and it will be allocated as unstaked (free) balance.
221 ///
222 /// Please note that this call will deposit your account tokens into the contract, so you will not be able to use them for other purposes.
223 ///
224 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
225 ///
226 /// ## Example
227 /// ```rust,no_run
228 /// use near_api::*;
229 ///
230 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
231 /// let result = Staking::delegation("alice.testnet".parse()?)
232 /// .deposit("pool.testnet".parse()?, NearToken::from_near(1))?
233 /// .with_signer(Signer::new(Signer::from_ledger())?)
234 /// .send_to_testnet()
235 /// .await?;
236 /// # Ok(())
237 /// # }
238 /// ```
239 pub fn deposit(&self, pool: AccountId, amount: NearToken) -> Result<ConstructTransaction> {
240 Ok(Contract(pool)
241 .call_function("deposit", ())?
242 .transaction()
243 .gas(NearGas::from_tgas(50))
244 .deposit(amount)
245 .with_signer_account(self.0.clone()))
246 }
247
248 /// Prepares a new transaction contract call (`deposit_and_stake`) for depositing funds into the staking pool and staking them.
249 ///
250 /// Please note that this call will deposit your account tokens into the contract, so you will not be able to use them for other purposes.
251 /// Also, after you have staked your funds, if you decide to withdraw them, you might need to wait for the two lockup period to end.
252 /// * Mandatory lockup before able to unstake
253 /// * Optional lockup before able to withdraw (depends on the pool configuration)
254 ///
255 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
256 ///
257 /// ## Example
258 /// ```rust,no_run
259 /// use near_api::*;
260 ///
261 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
262 /// let result = Staking::delegation("alice.testnet".parse()?)
263 /// .deposit_and_stake("pool.testnet".parse()?, NearToken::from_near(1))?
264 /// .with_signer(Signer::new(Signer::from_ledger())?)
265 /// .send_to_testnet()
266 /// .await?;
267 /// # Ok(())
268 /// # }
269 /// ```
270 pub fn deposit_and_stake(
271 &self,
272 pool: AccountId,
273 amount: NearToken,
274 ) -> Result<ConstructTransaction> {
275 Ok(Contract(pool)
276 .call_function("deposit_and_stake", ())?
277 .transaction()
278 .gas(NearGas::from_tgas(50))
279 .deposit(amount)
280 .with_signer_account(self.0.clone()))
281 }
282
283 /// Prepares a new transaction contract call (`stake`) for staking funds into the staking pool.
284 ///
285 /// Please note that this call will use your unstaked balance. This means that you have to have enough balance already deposited into the contract.
286 /// This won't use your native account tokens, but just reallocate your balance inside the contract.
287 /// Please also be aware that once you have staked your funds, you might not be able to withdraw them until the lockup periods end.
288 /// * Mandatory lockup before able to unstake
289 /// * Optional lockup before able to withdraw (depends on the pool configuration)
290 ///
291 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
292 ///
293 /// ## Example
294 /// ```rust,no_run
295 /// use near_api::*;
296 ///
297 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
298 /// let result = Staking::delegation("alice.testnet".parse()?)
299 /// .stake("pool.testnet".parse()?, NearToken::from_near(1))?
300 /// .with_signer(Signer::new(Signer::from_ledger())?)
301 /// .send_to_testnet()
302 /// .await?;
303 /// # Ok(())
304 /// # }
305 /// ```
306 pub fn stake(&self, pool: AccountId, amount: NearToken) -> Result<ConstructTransaction> {
307 let args = serde_json::json!({
308 "amount": amount,
309 });
310
311 Ok(Contract(pool)
312 .call_function("stake", args)?
313 .transaction()
314 .gas(NearGas::from_tgas(50))
315 .with_signer_account(self.0.clone()))
316 }
317
318 /// Prepares a new transaction contract call (`stake_all`) for staking all available unstaked balance into the staking pool.
319 ///
320 /// Please note that once you have staked your funds, you might not be able to withdraw them until the lockup periods end.
321 /// * Mandatory lockup before able to unstake
322 /// * Optional lockup before able to withdraw (depends on the pool configuration)
323 ///
324 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
325 ///
326 /// ## Example
327 /// ```rust,no_run
328 /// use near_api::*;
329 ///
330 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
331 /// Staking::delegation("alice.testnet".parse()?)
332 /// .stake_all("pool.testnet".parse()?)?
333 /// .with_signer(Signer::new(Signer::from_ledger())?)
334 /// .send_to_testnet()
335 /// .await?;
336 /// # Ok(())
337 /// # }
338 /// ```
339 pub fn stake_all(&self, pool: AccountId) -> Result<ConstructTransaction> {
340 Ok(Contract(pool)
341 .call_function("stake_all", ())?
342 .transaction()
343 .gas(NearGas::from_tgas(50))
344 .with_signer_account(self.0.clone()))
345 }
346
347 /// Prepares a new transaction contract call (`unstake`) for unstaking funds and returning them to your unstaked balance.
348 ///
349 /// The contract will check if the minimum epoch height condition is met.
350 ///
351 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
352 ///
353 /// ## Example
354 /// ```rust,no_run
355 /// use near_api::*;
356 ///
357 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
358 /// let result = Staking::delegation("alice.testnet".parse()?)
359 /// .unstake("pool.testnet".parse()?, NearToken::from_near(1))?
360 /// .with_signer(Signer::new(Signer::from_ledger())?)
361 /// .send_to_testnet()
362 /// .await?;
363 /// # Ok(())
364 /// # }
365 /// ```
366 pub fn unstake(&self, pool: AccountId, amount: NearToken) -> Result<ConstructTransaction> {
367 let args = serde_json::json!({
368 "amount": amount,
369 });
370
371 Ok(Contract(pool)
372 .call_function("unstake", args)?
373 .transaction()
374 .gas(NearGas::from_tgas(50))
375 .with_signer_account(self.0.clone()))
376 }
377
378 /// Prepares a new transaction contract call (`unstake_all`) for unstaking all available staked balance and returning them to your unstaked balance.
379 ///
380 /// The contract will check if the minimum epoch height condition is met.
381 ///
382 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
383 ///
384 /// ## Example
385 /// ```rust,no_run
386 /// use near_api::*;
387 ///
388 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
389 /// let result = Staking::delegation("alice.testnet".parse()?)
390 /// .unstake_all("pool.testnet".parse()?)?
391 /// .with_signer(Signer::new(Signer::from_ledger())?)
392 /// .send_to_testnet()
393 /// .await?;
394 /// # Ok(())
395 /// # }
396 /// ```
397 pub fn unstake_all(&self, pool: AccountId) -> Result<ConstructTransaction> {
398 Ok(Contract(pool)
399 .call_function("unstake_all", ())?
400 .transaction()
401 .gas(NearGas::from_tgas(50))
402 .with_signer_account(self.0.clone()))
403 }
404
405 /// Prepares a new transaction contract call (`withdraw`) for withdrawing funds from the staking pool into your account.
406 ///
407 /// Some pools configures minimum withdrawal period in epochs, so the balance is not available for withdrawal immediately.
408 ///
409 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
410 ///
411 /// ## Example
412 /// ```rust,no_run
413 /// use near_api::*;
414 ///
415 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
416 /// let result = Staking::delegation("alice.testnet".parse()?)
417 /// .withdraw("pool.testnet".parse()?, NearToken::from_near(1))?
418 /// .with_signer(Signer::new(Signer::from_ledger())?)
419 /// .send_to_testnet()
420 /// .await?;
421 /// # Ok(())
422 /// # }
423 /// ```
424 pub fn withdraw(&self, pool: AccountId, amount: NearToken) -> Result<ConstructTransaction> {
425 let args = serde_json::json!({
426 "amount": amount,
427 });
428
429 Ok(Contract(pool)
430 .call_function("withdraw", args)?
431 .transaction()
432 .gas(NearGas::from_tgas(50))
433 .with_signer_account(self.0.clone()))
434 }
435
436 /// Prepares a new transaction contract call (`withdraw_all`) for withdrawing all available staked balance from the staking pool into your account.
437 ///
438 /// Some pools configures minimum withdrawal period in epochs, so the balance is not available for withdrawal immediately.
439 ///
440 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
441 ///
442 /// ## Example
443 /// ```rust,no_run
444 /// use near_api::*;
445 ///
446 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
447 /// let result = Staking::delegation("alice.testnet".parse()?)
448 /// .withdraw_all("pool.testnet".parse()?)?
449 /// .with_signer(Signer::new(Signer::from_ledger())?)
450 /// .send_to_testnet()
451 /// .await?;
452 /// # Ok(())
453 /// # }
454 /// ```
455 pub fn withdraw_all(&self, pool: AccountId) -> Result<ConstructTransaction> {
456 Ok(Contract(pool)
457 .call_function("withdraw_all", ())?
458 .transaction()
459 .gas(NearGas::from_tgas(50))
460 .with_signer_account(self.0.clone()))
461 }
462}
463
464/// Staking-related interactions with the NEAR Protocol and the staking pools.
465///
466/// The [`Staking`] struct provides methods to interact with NEAR staking, including querying staking pools, validators, and delegators,
467/// as well as delegating and withdrawing from staking pools.
468///
469/// # Examples
470///
471/// ```rust,no_run
472/// use near_api::*;
473///
474/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
475/// let staking_pools = Staking::active_staking_pools().fetch_from_testnet().await?;
476/// println!("Staking pools: {:?}", staking_pools);
477/// # Ok(())
478/// # }
479/// ```
480#[derive(Clone, Debug)]
481pub struct Staking {}
482
483impl Staking {
484 /// Returns a list of active staking pools ([std::collections::BTreeSet]<[AccountId]>]) by querying the staking pools factory contract.
485 ///
486 /// Please note that it might fail on the mainnet as the staking pool factory is super huge.
487 ///
488 /// ## Example
489 /// ```rust,no_run
490 /// use near_api::*;
491 ///
492 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
493 /// let staking_pools = Staking::active_staking_pools().fetch_from_testnet().await?;
494 /// println!("Staking pools: {:?}", staking_pools);
495 /// # Ok(())
496 /// # }
497 /// ```
498 pub fn active_staking_pools() -> RpcBuilder<ActiveStakingPoolQuery, ActiveStakingHandler> {
499 RpcBuilder::new(
500 ActiveStakingPoolQuery,
501 Reference::Optimistic,
502 ActiveStakingHandler,
503 )
504 }
505
506 /// Returns a list of validators and their stake ([near_api_types::RpcValidatorResponse]) for the current epoch.
507 ///
508 /// ## Example
509 /// ```rust,no_run
510 /// use near_api::*;
511 ///
512 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
513 /// let validators = Staking::epoch_validators_info().fetch_from_testnet().await?;
514 /// println!("Validators: {:?}", validators);
515 /// # Ok(())
516 /// # }
517 /// ```
518 pub fn epoch_validators_info() -> RequestBuilder<RpcValidatorHandler> {
519 RequestBuilder::new(
520 SimpleValidatorRpc,
521 EpochReference::Latest,
522 RpcValidatorHandler,
523 )
524 }
525
526 /// Returns a map of validators and their stake ([BTreeMap<AccountId, NearToken>]) for the current epoch.
527 ///
528 /// ## Example
529 /// ```rust,no_run
530 /// use near_api::*;
531 ///
532 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
533 /// let validators = Staking::validators_stake().fetch_from_testnet().await?;
534 /// println!("Validators: {:?}", validators);
535 /// # Ok(())
536 /// # }
537 /// ```
538 pub fn validators_stake()
539 -> RequestBuilder<PostprocessHandler<BTreeMap<AccountId, NearToken>, RpcValidatorHandler>> {
540 RequestBuilder::new(
541 SimpleValidatorRpc,
542 EpochReference::Latest,
543 RpcValidatorHandler,
544 )
545 .map(|validator_response| {
546 validator_response
547 .current_proposals
548 .into_iter()
549 .map(|validator_stake_view| {
550 (validator_stake_view.account_id, validator_stake_view.stake)
551 })
552 .chain(validator_response.current_validators.into_iter().map(
553 |current_epoch_validator_info| {
554 (
555 current_epoch_validator_info.account_id,
556 current_epoch_validator_info.stake,
557 )
558 },
559 ))
560 .chain(validator_response.next_validators.into_iter().map(
561 |next_epoch_validator_info| {
562 (
563 next_epoch_validator_info.account_id,
564 next_epoch_validator_info.stake,
565 )
566 },
567 ))
568 .collect::<BTreeMap<_, NearToken>>()
569 })
570 }
571
572 /// Prepares a new contract query (`get_reward_fee_fraction`) for fetching the reward fee fraction of the staking pool ([Data]<[RewardFeeFraction]>).
573 ///
574 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
575 ///
576 /// ## Example
577 /// ```rust,no_run
578 /// use near_api::*;
579 ///
580 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
581 /// let reward_fee = Staking::staking_pool_reward_fee("pool.testnet".parse()?)
582 /// .fetch_from_testnet().await?;
583 /// println!("Reward fee: {:?}", reward_fee);
584 /// # Ok(())
585 /// # }
586 /// ```
587 pub fn staking_pool_reward_fee(
588 pool: AccountId,
589 ) -> RequestBuilder<CallResultHandler<RewardFeeFraction>> {
590 Contract(pool)
591 .call_function("get_reward_fee_fraction", ())
592 .expect("arguments are not expected")
593 .read_only()
594 }
595
596 /// Prepares a new contract query (`get_number_of_accounts`) for fetching the number of delegators of the staking pool ([Data]<[u64]>).
597 ///
598 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
599 ///
600 /// ## Example
601 /// ```rust,no_run
602 /// use near_api::*;
603 ///
604 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
605 /// let delegators = Staking::staking_pool_delegators("pool.testnet".parse()?)
606 /// .fetch_from_testnet()
607 /// .await?;
608 /// println!("Delegators: {:?}", delegators);
609 /// # Ok(())
610 /// # }
611 /// ```
612 pub fn staking_pool_delegators(pool: AccountId) -> RequestBuilder<CallResultHandler<u64>> {
613 Contract(pool)
614 .call_function("get_number_of_accounts", ())
615 .expect("arguments are not expected")
616 .read_only()
617 }
618
619 /// Prepares a new contract query (`get_total_staked_balance`) for fetching the total stake of the staking pool ([NearToken]).
620 ///
621 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
622 ///
623 /// ## Example
624 /// ```rust,no_run
625 /// use near_api::*;
626 ///
627 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
628 /// let total_stake = Staking::staking_pool_total_stake("pool.testnet".parse()?)
629 /// .fetch_from_testnet()
630 /// .await?;
631 /// println!("Total stake: {:?}", total_stake);
632 /// # Ok(())
633 /// # }
634 /// ```
635 pub fn staking_pool_total_stake(
636 pool: AccountId,
637 ) -> RequestBuilder<PostprocessHandler<NearToken, CallResultHandler<u128>>> {
638 Contract(pool)
639 .call_function("get_total_staked_balance", ())
640 .expect("arguments are not expected")
641 .read_only()
642 .map(near_data_to_near_token)
643 }
644
645 /// Returns a full information about the staking pool ([StakingPoolInfo]).
646 ///
647 /// This is a complex query that requires 3 calls (get_reward_fee_fraction, get_number_of_accounts, get_total_staked_balance) to the staking pool contract.
648 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
649 ///
650 /// ## Example
651 /// ```rust,no_run
652 /// use near_api::*;
653 ///
654 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
655 /// let staking_pool_info = Staking::staking_pool_info("pool.testnet".parse()?)
656 /// .fetch_from_testnet()
657 /// .await?;
658 /// println!("Staking pool info: {:?}", staking_pool_info);
659 /// # Ok(())
660 /// # }
661 /// ```
662 #[allow(clippy::type_complexity)]
663 pub fn staking_pool_info(
664 pool: AccountId,
665 ) -> MultiRequestBuilder<
666 PostprocessHandler<
667 StakingPoolInfo,
668 MultiQueryHandler<(
669 CallResultHandler<RewardFeeFraction>,
670 CallResultHandler<u64>,
671 CallResultHandler<u128>,
672 )>,
673 >,
674 > {
675 let pool_clone = pool.clone();
676 let handler = MultiQueryHandler::new((
677 CallResultHandler::default(),
678 CallResultHandler::default(),
679 CallResultHandler::default(),
680 ));
681
682 MultiRequestBuilder::new(handler, Reference::Optimistic)
683 .add_query_builder(Self::staking_pool_reward_fee(pool.clone()))
684 .add_query_builder(Self::staking_pool_delegators(pool.clone()))
685 .add_query_builder(Self::staking_pool_total_stake(pool))
686 .map(move |(reward_fee, delegators, total_stake)| {
687 let total = near_data_to_near_token(total_stake);
688
689 StakingPoolInfo {
690 validator_id: pool_clone.clone(),
691
692 fee: Some(reward_fee.data),
693 delegators: Some(delegators.data),
694 stake: total,
695 }
696 })
697 }
698
699 /// Returns a new [`Delegation`] struct for interacting with the staking pool on behalf of the account.
700 pub const fn delegation(account_id: AccountId) -> Delegation {
701 Delegation(account_id)
702 }
703}
704
705#[derive(Clone, Debug)]
706pub struct ActiveStakingPoolQuery;
707
708#[async_trait::async_trait]
709impl RpcType for ActiveStakingPoolQuery {
710 type RpcReference = <SimpleQueryRpc as RpcType>::RpcReference;
711 type Response = <SimpleQueryRpc as RpcType>::Response;
712 type Error = <SimpleQueryRpc as RpcType>::Error;
713
714 async fn send_query(
715 &self,
716 client: &near_openapi_client::Client,
717 network: &NetworkConfig,
718 reference: &Reference,
719 ) -> RetryResponse<RpcQueryResponse, SendRequestError<RpcError>> {
720 let Some(account_id) = network.staking_pools_factory_account_id.clone() else {
721 return RetryResponse::Critical(SendRequestError::QueryCreationError(
722 QueryCreationError::StakingPoolFactoryNotDefined,
723 ));
724 };
725
726 let request = QueryRequest::ViewState {
727 account_id,
728 prefix_base64: near_api_types::StoreKey(to_base64(b"se")),
729 include_proof: Some(false),
730 };
731
732 SimpleQueryRpc { request }
733 .send_query(client, network, reference)
734 .await
735 }
736}
737
738#[derive(Clone, Debug)]
739pub struct ActiveStakingHandler;
740
741#[async_trait::async_trait]
742impl ResponseHandler for ActiveStakingHandler {
743 type Query = ActiveStakingPoolQuery;
744 type Response = std::collections::BTreeSet<AccountId>;
745
746 fn process_response(
747 &self,
748 response: Vec<RpcQueryResponse>,
749 ) -> core::result::Result<Self::Response, QueryError<RpcError>> {
750 let query_result = ViewStateHandler {}.process_response(response)?;
751
752 Ok(query_result
753 .data
754 .values
755 .into_iter()
756 .filter_map(|item| borsh::from_slice(&from_base64(&item.value).ok()?).ok())
757 .collect())
758 }
759}