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    /// Prepares a new contract query (`storage_balance_of`) for fetching the storage balance (Option<[StorageBalance]>) of the account on the contract.
46    ///
47    /// ## Example
48    /// ```rust,no_run
49    /// use near_api::*;
50    ///
51    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
52    /// let balance = StorageDeposit::on_contract("contract.testnet".parse()?)
53    ///     .view_account_storage("alice.testnet".parse()?)?
54    ///     .fetch_from_testnet()
55    ///     .await?;
56    /// println!("Storage balance: {:?}", balance);
57    /// # Ok(())
58    /// # }
59    /// ```
60    #[allow(clippy::type_complexity)]
61    pub fn view_account_storage(
62        &self,
63        account_id: AccountId,
64    ) -> Result<
65        RequestBuilder<
66            PostprocessHandler<
67                Data<Option<StorageBalance>>,
68                CallResultHandler<Option<StorageBalanceInternal>>,
69            >,
70        >,
71        BuilderError,
72    > {
73        Ok(Contract(self.0.clone())
74            .call_function(
75                "storage_balance_of",
76                json!({
77                    "account_id": account_id,
78                }),
79            )?
80            .read_only()
81            .map(|storage: Data<Option<StorageBalanceInternal>>| {
82                storage.map(|option_storage| {
83                    option_storage.map(|data| StorageBalance {
84                        available: data.available,
85                        total: data.total,
86                        locked: NearToken::from_yoctonear(
87                            data.total.as_yoctonear() - data.available.as_yoctonear(),
88                        ),
89                    })
90                })
91            }))
92    }
93
94    /// Prepares a new transaction contract call (`storage_deposit`) for depositing storage on the contract.
95    ///
96    /// ## Example
97    /// ```rust,no_run
98    /// use near_api::*;
99    ///
100    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
101    /// let tx = StorageDeposit::on_contract("contract.testnet".parse()?)
102    ///     .deposit("alice.testnet".parse()?, NearToken::from_near(1))?
103    ///     .with_signer("bob.testnet".parse()?, Signer::new(Signer::from_ledger())?)
104    ///     .send_to_testnet()
105    ///     .await?;
106    /// # Ok(())
107    /// # }
108    /// ```
109    pub fn deposit(
110        &self,
111        receiver_account_id: AccountId,
112        amount: NearToken,
113    ) -> Result<ContractTransactBuilder, BuilderError> {
114        Ok(Contract(self.0.clone())
115            .call_function(
116                "storage_deposit",
117                json!({
118                    "account_id": receiver_account_id.to_string(),
119                }),
120            )?
121            .transaction()
122            .deposit(amount))
123    }
124
125    /// Prepares a new transaction contract call (`storage_withdraw`) for withdrawing storage from the contract.
126    ///
127    /// ## Example
128    /// ```rust,no_run
129    /// use near_api::*;
130    ///
131    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
132    /// let tx = StorageDeposit::on_contract("contract.testnet".parse()?)
133    ///     .withdraw("alice.testnet".parse()?, NearToken::from_near(1))?
134    ///     .with_signer(Signer::new(Signer::from_ledger())?)
135    ///     .send_to_testnet()
136    ///     .await?;
137    /// # Ok(())
138    /// # }
139    /// ```
140    pub fn withdraw(
141        &self,
142        account_id: AccountId,
143        amount: NearToken,
144    ) -> Result<ConstructTransaction, BuilderError> {
145        Ok(Contract(self.0.clone())
146            .call_function(
147                "storage_withdraw",
148                json!({
149                    "amount": amount
150                }),
151            )?
152            .transaction()
153            .deposit(NearToken::from_yoctonear(1))
154            .with_signer_account(account_id))
155    }
156}