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::{RpcError, 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::{BuilderError, QueryCreationError, QueryError, SendRequestError},
24 transactions::ConstructTransaction,
25 NetworkConfig,
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 {
541 RequestBuilder::new(
542 SimpleValidatorRpc,
543 EpochReference::Latest,
544 RpcValidatorHandler,
545 )
546 .map(|validator_response| {
547 validator_response
548 .current_proposals
549 .into_iter()
550 .map(|validator_stake_view| {
551 (validator_stake_view.account_id, validator_stake_view.stake)
552 })
553 .chain(validator_response.current_validators.into_iter().map(
554 |current_epoch_validator_info| {
555 (
556 current_epoch_validator_info.account_id,
557 current_epoch_validator_info.stake,
558 )
559 },
560 ))
561 .chain(validator_response.next_validators.into_iter().map(
562 |next_epoch_validator_info| {
563 (
564 next_epoch_validator_info.account_id,
565 next_epoch_validator_info.stake,
566 )
567 },
568 ))
569 .collect::<BTreeMap<_, NearToken>>()
570 })
571 }
572
573 /// Prepares a new contract query (`get_reward_fee_fraction`) for fetching the reward fee fraction of the staking pool ([Data]<[RewardFeeFraction]>).
574 ///
575 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
576 ///
577 /// ## Example
578 /// ```rust,no_run
579 /// use near_api::*;
580 ///
581 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
582 /// let reward_fee = Staking::staking_pool_reward_fee("pool.testnet".parse()?)
583 /// .fetch_from_testnet().await?;
584 /// println!("Reward fee: {:?}", reward_fee);
585 /// # Ok(())
586 /// # }
587 /// ```
588 pub fn staking_pool_reward_fee(
589 pool: AccountId,
590 ) -> RequestBuilder<CallResultHandler<RewardFeeFraction>> {
591 Contract(pool)
592 .call_function("get_reward_fee_fraction", ())
593 .expect("arguments are not expected")
594 .read_only()
595 }
596
597 /// Prepares a new contract query (`get_number_of_accounts`) for fetching the number of delegators of the staking pool ([Data]<[u64]>).
598 ///
599 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
600 ///
601 /// ## Example
602 /// ```rust,no_run
603 /// use near_api::*;
604 ///
605 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
606 /// let delegators = Staking::staking_pool_delegators("pool.testnet".parse()?)
607 /// .fetch_from_testnet()
608 /// .await?;
609 /// println!("Delegators: {:?}", delegators);
610 /// # Ok(())
611 /// # }
612 /// ```
613 pub fn staking_pool_delegators(pool: AccountId) -> RequestBuilder<CallResultHandler<u64>> {
614 Contract(pool)
615 .call_function("get_number_of_accounts", ())
616 .expect("arguments are not expected")
617 .read_only()
618 }
619
620 /// Prepares a new contract query (`get_total_staked_balance`) for fetching the total stake of the staking pool ([NearToken]).
621 ///
622 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
623 ///
624 /// ## Example
625 /// ```rust,no_run
626 /// use near_api::*;
627 ///
628 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
629 /// let total_stake = Staking::staking_pool_total_stake("pool.testnet".parse()?)
630 /// .fetch_from_testnet()
631 /// .await?;
632 /// println!("Total stake: {:?}", total_stake);
633 /// # Ok(())
634 /// # }
635 /// ```
636 pub fn staking_pool_total_stake(
637 pool: AccountId,
638 ) -> RequestBuilder<PostprocessHandler<NearToken, CallResultHandler<u128>>> {
639 Contract(pool)
640 .call_function("get_total_staked_balance", ())
641 .expect("arguments are not expected")
642 .read_only()
643 .map(near_data_to_near_token)
644 }
645
646 /// Returns a full information about the staking pool ([StakingPoolInfo]).
647 ///
648 /// 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.
649 /// The call depends that the contract implements [`StakingPool`](https://github.com/near/core-contracts/tree/master/staking-pool)
650 ///
651 /// ## Example
652 /// ```rust,no_run
653 /// use near_api::*;
654 ///
655 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
656 /// let staking_pool_info = Staking::staking_pool_info("pool.testnet".parse()?)
657 /// .fetch_from_testnet()
658 /// .await?;
659 /// println!("Staking pool info: {:?}", staking_pool_info);
660 /// # Ok(())
661 /// # }
662 /// ```
663 #[allow(clippy::type_complexity)]
664 pub fn staking_pool_info(
665 pool: AccountId,
666 ) -> MultiRequestBuilder<
667 PostprocessHandler<
668 StakingPoolInfo,
669 MultiQueryHandler<(
670 CallResultHandler<RewardFeeFraction>,
671 CallResultHandler<u64>,
672 CallResultHandler<u128>,
673 )>,
674 >,
675 > {
676 let pool_clone = pool.clone();
677 let handler = MultiQueryHandler::new((
678 CallResultHandler::default(),
679 CallResultHandler::default(),
680 CallResultHandler::default(),
681 ));
682
683 MultiRequestBuilder::new(handler, Reference::Optimistic)
684 .add_query_builder(Self::staking_pool_reward_fee(pool.clone()))
685 .add_query_builder(Self::staking_pool_delegators(pool.clone()))
686 .add_query_builder(Self::staking_pool_total_stake(pool))
687 .map(move |(reward_fee, delegators, total_stake)| {
688 let total = near_data_to_near_token(total_stake);
689
690 StakingPoolInfo {
691 validator_id: pool_clone.clone(),
692
693 fee: Some(reward_fee.data),
694 delegators: Some(delegators.data),
695 stake: total,
696 }
697 })
698 }
699
700 /// Returns a new [`Delegation`] struct for interacting with the staking pool on behalf of the account.
701 pub const fn delegation(account_id: AccountId) -> Delegation {
702 Delegation(account_id)
703 }
704}
705
706#[derive(Clone, Debug)]
707pub struct ActiveStakingPoolQuery;
708
709#[async_trait::async_trait]
710impl RpcType for ActiveStakingPoolQuery {
711 type RpcReference = <SimpleQueryRpc as RpcType>::RpcReference;
712 type Response = <SimpleQueryRpc as RpcType>::Response;
713 type Error = <SimpleQueryRpc as RpcType>::Error;
714
715 async fn send_query(
716 &self,
717 client: &near_openapi_client::Client,
718 network: &NetworkConfig,
719 reference: &Reference,
720 ) -> RetryResponse<RpcQueryResponse, SendRequestError<RpcError>> {
721 let Some(account_id) = network.staking_pools_factory_account_id.clone() else {
722 return RetryResponse::Critical(SendRequestError::QueryCreationError(
723 QueryCreationError::StakingPoolFactoryNotDefined,
724 ));
725 };
726
727 let request = QueryRequest::ViewState {
728 account_id,
729 prefix_base64: near_api_types::StoreKey(to_base64(b"se")),
730 include_proof: Some(false),
731 };
732
733 SimpleQueryRpc { request }
734 .send_query(client, network, reference)
735 .await
736 }
737}
738
739#[derive(Clone, Debug)]
740pub struct ActiveStakingHandler;
741
742#[async_trait::async_trait]
743impl ResponseHandler for ActiveStakingHandler {
744 type Query = ActiveStakingPoolQuery;
745 type Response = std::collections::BTreeSet<AccountId>;
746
747 fn process_response(
748 &self,
749 response: Vec<RpcQueryResponse>,
750 ) -> core::result::Result<Self::Response, QueryError<RpcError>> {
751 let query_result = ViewStateHandler {}.process_response(response)?;
752
753 Ok(query_result
754 .data
755 .values
756 .into_iter()
757 .filter_map(|item| borsh::from_slice(&from_base64(&item.value).ok()?).ok())
758 .collect())
759 }
760}