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}