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