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}