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