near_api/storage.rs
1use near_token::NearToken;
2use serde_json::json;
3
4use crate::{
5 common::query::{CallResultHandler, PostprocessHandler, QueryBuilder},
6 contract::{Contract, ContractTransactBuilder},
7 errors::BuilderError,
8 transactions::ConstructTransaction,
9 types::storage::{StorageBalance, StorageBalanceInternal},
10 AccountId, Data,
11};
12
13///A wrapper struct that simplifies interactions with the [Storage Management](https://github.com/near/NEPs/blob/master/neps/nep-0145.md) standard
14///
15/// Contracts on NEAR Protocol often implement a [NEP-145](https://github.com/near/NEPs/blob/master/neps/nep-0145.md) for managing storage deposits,
16/// which are required for storing data on the blockchain. This struct provides convenient methods
17/// to interact with these storage-related functions on the contract.
18///
19/// # Example
20/// ```
21/// use near_api::*;
22///
23/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
24/// let storage = StorageDeposit::on_contract("contract.testnet".parse()?);
25///
26/// // Check storage balance
27/// let balance = storage.view_account_storage("alice.testnet".parse()?)?.fetch_from_testnet().await?;
28/// println!("Storage balance: {:?}", balance);
29///
30/// // Bob pays for Alice's storage on the contract contract.testnet
31/// let deposit_tx = storage.deposit("alice.testnet".parse()?, NearToken::from_near(1))?
32/// .with_signer("bob.testnet".parse()?, Signer::new(Signer::from_ledger())?)
33/// .send_to_testnet()
34/// .await
35/// .unwrap();
36/// # Ok(())
37/// # }
38/// ```
39#[derive(Clone, Debug)]
40pub struct StorageDeposit(AccountId);
41
42impl StorageDeposit {
43 pub const fn on_contract(contract_id: AccountId) -> Self {
44 Self(contract_id)
45 }
46
47 /// Prepares a new contract query (`storage_balance_of`) for fetching the storage balance (Option<[StorageBalance]>) of the account on the contract.
48 ///
49 /// ## Example
50 /// ```rust,no_run
51 /// use near_api::*;
52 ///
53 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
54 /// let balance = StorageDeposit::on_contract("contract.testnet".parse()?)
55 /// .view_account_storage("alice.testnet".parse()?)?
56 /// .fetch_from_testnet()
57 /// .await?;
58 /// println!("Storage balance: {:?}", balance);
59 /// # Ok(())
60 /// # }
61 /// ```
62 #[allow(clippy::complexity)]
63 pub fn view_account_storage(
64 &self,
65 account_id: AccountId,
66 ) -> Result<
67 QueryBuilder<
68 PostprocessHandler<
69 Data<Option<StorageBalance>>,
70 CallResultHandler<Option<StorageBalanceInternal>>,
71 >,
72 >,
73 BuilderError,
74 > {
75 Ok(Contract(self.0.clone())
76 .call_function(
77 "storage_balance_of",
78 json!({
79 "account_id": account_id,
80 }),
81 )?
82 .read_only()
83 .map(|storage: Data<Option<StorageBalanceInternal>>| {
84 storage.map(|option_storage| {
85 option_storage.map(|data| StorageBalance {
86 available: data.available,
87 total: data.total,
88 locked: NearToken::from_yoctonear(
89 data.total.as_yoctonear() - data.available.as_yoctonear(),
90 ),
91 })
92 })
93 }))
94 }
95
96 /// Prepares a new transaction contract call (`storage_deposit`) for depositing storage on the contract.
97 ///
98 /// ## Example
99 /// ```rust,no_run
100 /// use near_api::*;
101 ///
102 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
103 /// let tx: near_primitives::views::FinalExecutionOutcomeView = StorageDeposit::on_contract("contract.testnet".parse()?)
104 /// .deposit("alice.testnet".parse()?, NearToken::from_near(1))?
105 /// .with_signer("bob.testnet".parse()?, Signer::new(Signer::from_ledger())?)
106 /// .send_to_testnet()
107 /// .await?;
108 /// # Ok(())
109 /// # }
110 /// ```
111 pub fn deposit(
112 &self,
113 receiver_account_id: AccountId,
114 amount: NearToken,
115 ) -> Result<ContractTransactBuilder, BuilderError> {
116 Ok(Contract(self.0.clone())
117 .call_function(
118 "storage_deposit",
119 json!({
120 "account_id": receiver_account_id.to_string(),
121 }),
122 )?
123 .transaction()
124 .deposit(amount))
125 }
126
127 /// Prepares a new transaction contract call (`storage_withdraw`) for withdrawing storage from the contract.
128 ///
129 /// ## Example
130 /// ```rust,no_run
131 /// use near_api::*;
132 ///
133 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
134 /// let tx: near_primitives::views::FinalExecutionOutcomeView = StorageDeposit::on_contract("contract.testnet".parse()?)
135 /// .withdraw("alice.testnet".parse()?, NearToken::from_near(1))?
136 /// .with_signer(Signer::new(Signer::from_ledger())?)
137 /// .send_to_testnet()
138 /// .await?;
139 /// # Ok(())
140 /// # }
141 /// ```
142 pub fn withdraw(
143 &self,
144 account_id: AccountId,
145 amount: NearToken,
146 ) -> Result<ConstructTransaction, BuilderError> {
147 Ok(Contract(self.0.clone())
148 .call_function(
149 "storage_withdraw",
150 json!({
151 "amount": amount.as_yoctonear()
152 }),
153 )?
154 .transaction()
155 .deposit(NearToken::from_yoctonear(1))
156 .with_signer_account(account_id))
157 }
158}