near_api/
contract.rs

1use std::sync::Arc;
2
3use borsh::BorshDeserialize;
4use near_api_types::{
5    contract::ContractSourceMetadata,
6    transaction::actions::{
7        DeployContractAction, DeployGlobalContractAction, FunctionCallAction,
8        GlobalContractDeployMode, GlobalContractIdentifier, UseGlobalContractAction,
9    },
10    AccountId, Action, CryptoHash, Data, FunctionArgs, NearGas, NearToken, Reference, StoreKey,
11};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13
14use crate::{
15    advanced::{query_request::QueryRequest, query_rpc::SimpleQueryRpc},
16    common::{
17        query::{
18            CallResultBorshHandler, CallResultHandler, PostprocessHandler, RequestBuilder,
19            ViewCodeHandler, ViewStateHandler,
20        },
21        send::ExecuteSignedTransaction,
22        utils::to_base64,
23    },
24    errors::BuilderError,
25    signer::Signer,
26    transactions::{ConstructTransaction, SelfActionBuilder, Transaction},
27};
28
29/// Contract-related interactions with the NEAR Protocol
30///
31/// The [`Contract`] struct provides methods to interact with NEAR contracts, including calling functions, querying storage, and deploying contracts.
32///
33/// # Examples
34///
35/// ```rust,no_run
36/// use near_api::*;
37///
38/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
39/// let abi = Contract("some_contract.testnet".parse()?).abi().fetch_from_testnet().await?;
40/// println!("ABI: {:?}", abi);
41/// # Ok(())
42/// # }
43/// ```
44#[derive(Clone, Debug)]
45pub struct Contract(pub AccountId);
46
47impl Contract {
48    /// Returns the underlying account ID for this contract.
49    ///
50    /// # Example
51    /// ```rust,no_run
52    /// use near_api::*;
53    ///
54    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
55    /// let contract = Contract("contract.testnet".parse()?);
56    /// let account_id = contract.account_id();
57    /// println!("Contract account ID: {}", account_id);
58    /// # Ok(())
59    /// # }
60    /// ```
61    pub const fn account_id(&self) -> &AccountId {
62        &self.0
63    }
64
65    /// Converts this contract to an Account for account-related operations.
66    ///
67    /// # Example
68    /// ```rust,no_run
69    /// use near_api::*;
70    ///
71    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
72    /// let contract = Contract("contract.testnet".parse()?);
73    /// let account = contract.as_account();
74    /// let account_info = account.view().fetch_from_testnet().await?;
75    /// println!("Account balance: {}", account_info.data.amount);
76    /// # Ok(())
77    /// # }
78    /// ```
79    pub fn as_account(&self) -> crate::account::Account {
80        crate::account::Account(self.0.clone())
81    }
82
83    /// Creates a StorageDeposit wrapper for storage management operations on this contract.
84    ///
85    /// This is useful for contracts that implement the [NEP-145](https://github.com/near/NEPs/blob/master/neps/nep-0145.md) storage management standard.
86    ///
87    /// # Example
88    /// ```rust,no_run
89    /// use near_api::*;
90    ///
91    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
92    /// let contract = Contract("usdt.tether-token.near".parse()?);
93    /// let storage = contract.storage_deposit();
94    ///
95    /// // Check storage balance for an account
96    /// let balance = storage.view_account_storage("alice.near".parse()?)?.fetch_from_mainnet().await?;
97    /// println!("Storage balance: {:?}", balance);
98    /// # Ok(())
99    /// # }
100    /// ```
101    pub fn storage_deposit(&self) -> crate::StorageDeposit {
102        crate::StorageDeposit::on_contract(self.0.clone())
103    }
104
105    /// Prepares a call to a contract function with JSON-serialized arguments.
106    ///
107    /// This is the default and most common way to call contract functions, using JSON serialization
108    /// for the input arguments. This will return a builder that can be used to prepare a query or a transaction.
109    ///
110    /// For alternative serialization formats, see:
111    /// - [`call_function_borsh`](Contract::call_function_borsh) for Borsh serialization
112    /// - [`call_function_raw`](Contract::call_function_raw) for pre-serialized raw bytes
113    ///
114    /// ## Calling view function `get_number`
115    /// ```rust,no_run
116    /// use near_api::*;
117    ///
118    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
119    /// let number: Data<u64> = Contract("some_contract.testnet".parse()?)
120    ///     .call_function("get_number", ())?
121    ///     .read_only()
122    ///     .fetch_from_testnet()
123    ///     .await?;
124    /// println!("Number: {:?}", number);
125    /// # Ok(())
126    /// # }
127    /// ```
128    ///
129    /// ## Calling a state changing function `set_number`
130    /// ```rust,no_run
131    /// use near_api::*;
132    /// use serde_json::json;
133    ///
134    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
135    /// let signer = Signer::new(Signer::from_ledger())?;
136    /// let result = Contract("some_contract.testnet".parse()?)
137    ///     .call_function("set_number", json!({ "number": 100 }))?
138    ///     .transaction()
139    ///      // Optional
140    ///     .gas(NearGas::from_tgas(200))
141    ///     .with_signer("alice.testnet".parse()?, signer)
142    ///     .send_to_testnet()
143    ///     .await?;
144    /// # Ok(())
145    /// # }
146    /// ```
147    pub fn call_function<Args>(
148        &self,
149        method_name: &str,
150        args: Args,
151    ) -> Result<CallFunctionBuilder, BuilderError>
152    where
153        Args: serde::Serialize,
154    {
155        let args = serde_json::to_vec(&args)?;
156
157        Ok(CallFunctionBuilder {
158            contract: self.0.clone(),
159            method_name: method_name.to_string(),
160            args,
161        })
162    }
163
164    /// Prepares a call to a contract function with Borsh-serialized arguments.
165    ///
166    /// This method is useful when the contract expects Borsh-encoded input arguments instead of JSON.
167    /// This is less common but can be more efficient for certain use cases.
168    ///
169    /// ## Example
170    /// ```rust,no_run
171    /// use near_api::*;
172    /// use borsh::BorshSerialize;
173    ///
174    /// #[derive(BorshSerialize)]
175    /// struct MyArgs {
176    ///     value: u64,
177    /// }
178    ///
179    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
180    /// let signer = Signer::new(Signer::from_ledger())?;
181    /// let args = MyArgs { value: 42 };
182    /// let result = Contract("some_contract.testnet".parse()?)
183    ///     .call_function_borsh("set_value", args)?
184    ///     .transaction()
185    ///     .with_signer("alice.testnet".parse()?, signer)
186    ///     .send_to_testnet()
187    ///     .await?;
188    /// # Ok(())
189    /// # }
190    /// ```
191    pub fn call_function_borsh<Args>(
192        &self,
193        method_name: &str,
194        args: Args,
195    ) -> Result<CallFunctionBuilder, std::io::Error>
196    where
197        Args: borsh::BorshSerialize,
198    {
199        let args = borsh::to_vec(&args)?;
200
201        Ok(CallFunctionBuilder {
202            contract: self.0.clone(),
203            method_name: method_name.to_string(),
204            args,
205        })
206    }
207
208    /// Prepares a call to a contract function with pre-serialized raw bytes.
209    ///
210    /// This method is useful when you already have serialized arguments or need complete control
211    /// over the serialization format. The bytes are passed directly to the contract without any
212    /// additional processing.
213    ///
214    /// ## Example
215    /// ```rust,no_run
216    /// use near_api::*;
217    ///
218    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
219    /// let signer = Signer::new(Signer::from_ledger())?;
220    /// // Pre-serialized or custom-encoded data
221    /// let raw_args = vec![1, 2, 3, 4];
222    /// let result = Contract("some_contract.testnet".parse()?)
223    ///     .call_function_raw("custom_method", raw_args)
224    ///     .transaction()
225    ///     .with_signer("alice.testnet".parse()?, signer)
226    ///     .send_to_testnet()
227    ///     .await?;
228    /// # Ok(())
229    /// # }
230    /// ```
231    pub fn call_function_raw(&self, method_name: &str, args: Vec<u8>) -> CallFunctionBuilder {
232        CallFunctionBuilder {
233            contract: self.0.clone(),
234            method_name: method_name.to_string(),
235            args,
236        }
237    }
238
239    /// Prepares a transaction to deploy a contract to the provided account.
240    ///
241    /// The code is the wasm bytecode of the contract. For more information on how to compile your contract,
242    /// please refer to the [NEAR documentation](https://docs.near.org/build/smart-contracts/quickstart).
243    ///
244    /// ## Deploying the contract
245    /// ```rust,no_run
246    /// use near_api::*;
247    ///
248    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
249    /// let code = std::fs::read("path/to/your/contract.wasm")?;
250    /// let signer = Signer::new(Signer::from_ledger())?;
251    /// let result = Contract::deploy("contract.testnet".parse()?)
252    ///     .use_code(code)
253    ///     .without_init_call()
254    ///     .with_signer(signer)
255    ///     .send_to_testnet()
256    ///     .await?;
257    /// # Ok(())
258    /// # }
259    /// ```
260    ///
261    /// ## Deploying the contract with an init call
262    /// ```rust,no_run
263    /// use near_api::*;
264    /// use serde_json::json;
265    ///
266    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
267    /// let code = std::fs::read("path/to/your/contract.wasm")?;
268    /// let signer = Signer::new(Signer::from_ledger())?;
269    /// let result = Contract::deploy("contract.testnet".parse()?)
270    ///     .use_code(code)
271    ///     .with_init_call("init", json!({ "number": 100 }))?
272    ///     // Optional
273    ///     .gas(NearGas::from_tgas(200))
274    ///     .with_signer(signer)
275    ///     .send_to_testnet()
276    ///     .await?;
277    /// # Ok(())
278    /// # }
279    /// ```
280    pub const fn deploy(contract: AccountId) -> DeployBuilder {
281        DeployBuilder::new(contract)
282    }
283
284    /// Prepares a transaction to deploy a code to the global contract code storage.
285    ///
286    /// This will allow other users to reference given code as hash or account-id and reduce
287    /// the gas cost for deployment.
288    ///
289    /// Please be aware that the deploy costs 10x more compared to the regular costs and the tokens are burnt
290    /// with no way to get it back.
291    ///
292    /// ## Example deploying a contract to the global contract code storage as hash
293    /// ```rust,no_run
294    /// use near_api::*;
295    ///
296    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
297    /// let code = std::fs::read("path/to/your/contract.wasm")?;
298    /// let signer = Signer::new(Signer::from_ledger())?;
299    /// let result = Contract::deploy_global_contract_code(code)
300    ///     .as_hash()
301    ///     .with_signer("some-account.testnet".parse()?, signer)
302    ///     .send_to_testnet()
303    ///     .await?;
304    /// # Ok(())
305    /// # }
306    /// ```
307    ///
308    /// ## Example deploying a contract to the global contract code storage as account-id
309    ///
310    /// The difference between the hash and account-id version is that the account-id version
311    /// upgradable and can be changed.
312    ///
313    /// ```rust,no_run
314    /// use near_api::*;
315    ///
316    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
317    /// let code = std::fs::read("path/to/your/contract.wasm")?;
318    /// let signer = Signer::new(Signer::from_ledger())?;
319    /// let result = Contract::deploy_global_contract_code(code)
320    ///     .as_account_id("nft-contract.testnet".parse()?)
321    ///     .with_signer(signer)
322    ///     .send_to_testnet()
323    ///     .await?;
324    /// # Ok(())
325    /// # }
326    /// ```
327    pub const fn deploy_global_contract_code(code: Vec<u8>) -> GlobalDeployBuilder {
328        GlobalDeployBuilder::new(code)
329    }
330
331    /// Prepares a query to fetch the [ABI](near_api_types::abi::AbiRoot) of the contract using the following [standard](https://github.com/near/near-abi-rs).
332    ///
333    /// Please be aware that not all the contracts provide the ABI.
334    ///
335    /// # Example
336    /// ```rust,no_run
337    /// use near_api::*;
338    ///
339    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
340    /// let abi = Contract("some_contract.testnet".parse()?).abi().fetch_from_testnet().await?;
341    /// println!("ABI: {:?}", abi);
342    /// # Ok(())
343    /// # }
344    /// ```
345    pub fn abi(
346        &self,
347    ) -> RequestBuilder<
348        PostprocessHandler<Option<near_api_types::abi::AbiRoot>, CallResultHandler<Vec<u8>>>,
349    > {
350        self.call_function("__contract_abi", ())
351            .expect("arguments are always serializable")
352            .read_only()
353            .map(|data: Data<Vec<u8>>| {
354                serde_json::from_slice(zstd::decode_all(data.data.as_slice()).ok()?.as_slice()).ok()
355            })
356    }
357
358    /// Prepares a query to fetch the wasm code ([Data]<[ContractCodeView](near_api_types::ContractCodeView)>) of the contract.
359    ///
360    /// # Example
361    /// ```rust,no_run
362    /// use near_api::*;
363    ///
364    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
365    /// let wasm = Contract("some_contract.testnet".parse()?).wasm().fetch_from_testnet().await?;
366    /// println!("WASM: {}", wasm.data.code_base64);
367    /// # Ok(())
368    /// # }
369    /// ```
370    pub fn wasm(&self) -> RequestBuilder<ViewCodeHandler> {
371        let request = QueryRequest::ViewCode {
372            account_id: self.0.clone(),
373        };
374
375        RequestBuilder::new(
376            SimpleQueryRpc { request },
377            Reference::Optimistic,
378            ViewCodeHandler,
379        )
380    }
381
382    /// Creates a builder to query contract code from the global contract code storage.
383    ///
384    /// The global contract code storage allows contracts to be deployed once and referenced
385    /// by multiple accounts, reducing deployment costs. This feature is defined in [NEP-591](https://github.com/near/NEPs/blob/2f6b702d55a4cd470b50d35e2f3fde6e0fb4dced/neps/nep-0591.md).
386    /// Contracts can be referenced either by a contract hash (immutable) or by an account ID (mutable).
387    ///
388    /// # Example querying by account ID
389    /// ```rust,no_run
390    /// use near_api::*;
391    ///
392    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
393    /// let code = Contract::global_wasm()
394    ///     .by_account_id("nft-contract.testnet".parse()?)
395    ///     .fetch_from_testnet()
396    ///     .await?;
397    /// println!("Global contract code: {}", code.data.code_base64);
398    /// # Ok(())
399    /// # }
400    /// ```
401    ///
402    /// # Example querying by hash
403    /// ```rust,no_run
404    /// use near_api::*;
405    ///
406    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
407    /// let code = Contract::global_wasm()
408    ///     .by_hash("DxfRbrjT3QPmoANMDYTR6iXPGJr7xRUyDnQhcAWjcoFF".parse()?)
409    ///     .fetch_from_testnet()
410    ///     .await?;
411    /// println!("Global contract code: {}", code.data.code_base64);
412    /// # Ok(())
413    /// # }
414    /// ```
415    pub const fn global_wasm() -> GlobalWasmBuilder {
416        GlobalWasmBuilder
417    }
418
419    /// Prepares a query to fetch the storage of the contract ([Data]<[ViewStateResult](near_api_types::ViewStateResult)>) using the given prefix as a filter.
420    ///
421    /// It helpful if you are aware of the storage that you are looking for.
422    ///
423    /// # Example
424    /// ```rust,no_run
425    /// use near_api::*;
426    ///
427    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
428    /// let storage = Contract("some_contract.testnet".parse()?)
429    ///     .view_storage_with_prefix(b"se")
430    ///     .fetch_from_testnet()
431    ///     .await?;
432    /// println!("Storage: {:?}", storage);
433    /// # Ok(())
434    /// # }
435    /// ```
436    pub fn view_storage_with_prefix(&self, prefix: &[u8]) -> RequestBuilder<ViewStateHandler> {
437        let request = QueryRequest::ViewState {
438            account_id: self.0.clone(),
439            prefix_base64: StoreKey(to_base64(prefix)),
440            include_proof: Some(false),
441        };
442
443        RequestBuilder::new(
444            SimpleQueryRpc { request },
445            Reference::Optimistic,
446            ViewStateHandler,
447        )
448    }
449
450    /// Prepares a query to fetch the storage of the contract ([Data]<[ViewStateResult](near_api_types::ViewStateResult)>).
451    ///
452    /// Please be aware that large storage queries might fail.
453    ///
454    /// # Example
455    /// ```rust,no_run
456    /// use near_api::*;
457    ///
458    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
459    /// let storage = Contract("some_contract.testnet".parse()?)
460    ///     .view_storage()
461    ///     .fetch_from_testnet()
462    ///     .await?;
463    /// println!("Storage: {:?}", storage);
464    /// # Ok(())
465    /// # }
466    /// ```
467    pub fn view_storage(&self) -> RequestBuilder<ViewStateHandler> {
468        self.view_storage_with_prefix(&[])
469    }
470
471    /// Prepares a query to fetch the contract source metadata([Data]<[ContractSourceMetadata]>) using [NEP-330](https://github.com/near/NEPs/blob/master/neps/nep-0330.md) standard.
472    ///
473    /// The contract source metadata is a standard interface that allows auditing and viewing source code for a deployed smart contract.
474    /// Implementation of this standard is purely optional but is recommended for developers whose contracts are open source.
475    ///
476    /// # Examples
477    ///
478    /// ```rust,no_run
479    /// use near_api::*;
480    ///
481    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
482    /// let source_metadata = Contract("some_contract.testnet".parse()?)
483    ///     .contract_source_metadata()
484    ///     .fetch_from_testnet()
485    ///     .await?;
486    /// println!("Source metadata: {:?}", source_metadata);
487    /// # Ok(())
488    /// # }
489    /// ```
490    /// A more verbose runnable example is present in `examples/contract_source_metadata.rs`:
491    /// ```rust,no_run
492    #[doc = include_str!("../examples/contract_source_metadata.rs")]
493    /// ```
494    pub fn contract_source_metadata(
495        &self,
496    ) -> RequestBuilder<CallResultHandler<ContractSourceMetadata>> {
497        self.call_function("contract_source_metadata", ())
498            .expect("arguments are always serializable")
499            .read_only()
500    }
501}
502
503#[derive(Clone, Debug)]
504pub struct DeployBuilder {
505    pub contract: AccountId,
506}
507
508impl DeployBuilder {
509    pub const fn new(contract: AccountId) -> Self {
510        Self { contract }
511    }
512
513    /// Prepares a transaction to deploy a contract to the provided account
514    ///
515    /// The code is the wasm bytecode of the contract. For more information on how to compile your contract,
516    /// please refer to the [NEAR documentation](https://docs.near.org/build/smart-contracts/quickstart).
517    ///
518    /// # Example
519    /// ```rust,no_run
520    /// use near_api::*;
521    ///
522    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
523    /// let code = std::fs::read("path/to/your/contract.wasm")?;
524    /// let signer = Signer::new(Signer::from_ledger())?;
525    /// let result = Contract::deploy("contract.testnet".parse()?)
526    ///     .use_code(code)
527    ///     .without_init_call()
528    ///     .with_signer(signer)
529    ///     .send_to_testnet()
530    ///     .await?;
531    /// # Ok(())
532    /// # }
533    pub fn use_code(self, code: Vec<u8>) -> SetDeployActionBuilder {
534        SetDeployActionBuilder::new(
535            self.contract,
536            Action::DeployContract(DeployContractAction { code }),
537        )
538    }
539
540    // /// Prepares a transaction to deploy a contract to the provided account using a immutable hash reference to the code from the global contract code storage.
541    // ///
542    // /// # Example
543    // /// ```rust,no_run
544    // /// use near_api::*;
545    // ///
546    // /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
547    // /// let signer = Signer::new(Signer::from_ledger())?;
548    // /// let result = Contract::deploy("contract.testnet".parse()?)
549    // ///     .use_global_hash("DxfRbrjT3QPmoANMDYTR6iXPGJr7xRUyDnQhcAWjcoFF".parse()?)
550    // ///     .without_init_call()
551    // ///     .with_signer(signer)
552    // ///     .send_to_testnet()
553    // ///     .await?;
554    // /// # Ok(())
555    // /// # }
556    pub fn use_global_hash(self, global_hash: CryptoHash) -> SetDeployActionBuilder {
557        SetDeployActionBuilder::new(
558            self.contract,
559            Action::UseGlobalContract(Box::new(UseGlobalContractAction {
560                contract_identifier: GlobalContractIdentifier::CodeHash(global_hash),
561            })),
562        )
563    }
564
565    // /// Prepares a transaction to deploy a contract to the provided account using a mutable account-id reference to the code from the global contract code storage.
566    // ///
567    // /// Please note that you have to trust the account-id that you are providing. As the code is mutable, the owner of the referenced account can
568    // /// change the code at any time which might lead to unexpected behavior or malicious activity.
569    // ///
570    // /// # Example
571    // /// ```rust,no_run
572    // /// use near_api::*;
573    // ///
574    // /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
575    // /// let signer = Signer::new(Signer::from_ledger())?;
576    // /// let result = Contract::deploy("contract.testnet".parse()?)
577    // ///     .use_global_account_id("nft-contract.testnet".parse()?)
578    // ///     .without_init_call()
579    // ///     .with_signer(signer)
580    // ///     .send_to_testnet()
581    // ///     .await?;
582    // /// # Ok(())
583    // /// # }
584    pub fn use_global_account_id(self, global_account_id: AccountId) -> SetDeployActionBuilder {
585        SetDeployActionBuilder::new(
586            self.contract,
587            Action::UseGlobalContract(Box::new(UseGlobalContractAction {
588                contract_identifier: GlobalContractIdentifier::AccountId(global_account_id),
589            })),
590        )
591    }
592}
593
594#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
595pub struct SetDeployActionBuilder {
596    contract: AccountId,
597    deploy_action: Action,
598}
599
600impl SetDeployActionBuilder {
601    pub const fn new(contract: AccountId, deploy_action: Action) -> Self {
602        Self {
603            contract,
604            deploy_action,
605        }
606    }
607
608    /// Prepares a transaction to deploy a contract to the provided account without an init call.
609    ///
610    /// This will deploy the contract without calling any function.
611    pub fn without_init_call(self) -> ConstructTransaction {
612        Transaction::construct(self.contract.clone(), self.contract).add_action(self.deploy_action)
613    }
614
615    /// Prepares a transaction to deploy a contract to the provided account with an init call.
616    ///
617    /// This will deploy the contract and call the init function with the provided arguments as a single transaction.
618    pub fn with_init_call<Args: Serialize>(
619        self,
620        method_name: &str,
621        args: Args,
622    ) -> Result<SetDeployActionWithInitCallBuilder, BuilderError> {
623        let args = serde_json::to_vec(&args)?;
624
625        Ok(SetDeployActionWithInitCallBuilder::new(
626            self.contract.clone(),
627            method_name.to_string(),
628            args,
629            self.deploy_action,
630        ))
631    }
632}
633
634#[derive(Clone, Debug)]
635pub struct SetDeployActionWithInitCallBuilder {
636    contract: AccountId,
637    method_name: String,
638    args: Vec<u8>,
639    deploy_action: Action,
640    gas: Option<NearGas>,
641    deposit: Option<NearToken>,
642}
643
644impl SetDeployActionWithInitCallBuilder {
645    const fn new(
646        contract: AccountId,
647        method_name: String,
648        args: Vec<u8>,
649        deploy_action: Action,
650    ) -> Self {
651        Self {
652            contract,
653            method_name,
654            args,
655            deploy_action,
656            gas: None,
657            deposit: None,
658        }
659    }
660
661    /// Specify the gas limit for the transaction. By default it is set to 100 TGas.
662    pub const fn gas(mut self, gas: NearGas) -> Self {
663        self.gas = Some(gas);
664        self
665    }
666
667    /// Specify the gas limit for the transaction to the maximum allowed value.
668    pub const fn max_gas(mut self) -> Self {
669        self.gas = Some(NearGas::from_tgas(300));
670        self
671    }
672
673    /// Specify the near deposit for the transaction. By default it is set to 0.
674    ///
675    /// Please note that the method should be [`payable`](https://docs.near.org/build/smart-contracts/anatomy/functions#payable-functions) in the contract to accept the deposit.
676    /// Otherwise the transaction will fail.
677    pub const fn deposit(mut self, deposit: NearToken) -> Self {
678        self.deposit = Some(deposit);
679        self
680    }
681
682    /// Specify the signer for the transaction. This will wrap-up the process of the preparing transaction.
683    ///
684    /// This will return the [`ExecuteSignedTransaction`] that can be used to sign and send the transaction to the network.
685    pub fn with_signer(self, signer: Arc<Signer>) -> ExecuteSignedTransaction {
686        let gas = self.gas.unwrap_or_else(|| NearGas::from_tgas(100));
687        let deposit = self.deposit.unwrap_or_else(|| NearToken::from_yoctonear(0));
688
689        Transaction::construct(self.contract.clone(), self.contract)
690            .add_action(self.deploy_action)
691            .add_action(Action::FunctionCall(Box::new(FunctionCallAction {
692                method_name: self.method_name.to_owned(),
693                args: self.args,
694                gas,
695                deposit,
696            })))
697            .with_signer(signer)
698    }
699}
700
701#[derive(Clone, Debug)]
702pub struct GlobalDeployBuilder {
703    code: Vec<u8>,
704}
705
706impl GlobalDeployBuilder {
707    pub const fn new(code: Vec<u8>) -> Self {
708        Self { code }
709    }
710
711    /// Prepares a transaction to deploy a code to the global contract code storage and reference it by hash.
712    ///
713    /// The code is immutable and cannot be changed once deployed.
714    ///
715    /// # Example
716    /// ```rust,no_run
717    /// use near_api::*;
718    ///
719    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
720    /// let code = std::fs::read("path/to/your/contract.wasm")?;
721    /// let signer = Signer::new(Signer::from_ledger())?;
722    /// let result = Contract::deploy_global_contract_code(code)
723    ///     .as_hash()
724    ///     .with_signer("some-account.testnet".parse()?, signer)
725    ///     .send_to_testnet()
726    ///     .await?;
727    /// # Ok(())
728    /// # }
729    #[allow(clippy::wrong_self_convention)]
730    pub fn as_hash(self) -> SelfActionBuilder {
731        SelfActionBuilder::new().add_action(Action::DeployGlobalContract(
732            DeployGlobalContractAction {
733                code: self.code,
734                deploy_mode: GlobalContractDeployMode::CodeHash,
735            },
736        ))
737    }
738
739    /// Prepares a transaction to deploy a code to the global contract code storage and reference it by account-id.
740    ///
741    /// You would be able to change the code later on.
742    /// Please note that every subsequent upgrade will charge full deployment cost.
743    ///
744    /// # Example
745    /// ```rust,no_run
746    /// use near_api::*;
747    ///
748    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
749    /// let code = std::fs::read("path/to/your/contract.wasm")?;
750    /// let signer = Signer::new(Signer::from_ledger())?;
751    /// let result = Contract::deploy_global_contract_code(code)
752    ///     .as_account_id("some-account.testnet".parse()?)
753    ///     .with_signer(signer)
754    ///     .send_to_testnet()
755    ///     .await?;
756    /// # Ok(())
757    /// # }
758    #[allow(clippy::wrong_self_convention)]
759    pub fn as_account_id(self, signer_id: AccountId) -> ConstructTransaction {
760        Transaction::construct(signer_id.clone(), signer_id).add_action(
761            Action::DeployGlobalContract(DeployGlobalContractAction {
762                code: self.code,
763                deploy_mode: GlobalContractDeployMode::AccountId,
764            }),
765        )
766    }
767}
768
769#[derive(Clone, Debug)]
770pub struct CallFunctionBuilder {
771    contract: AccountId,
772    method_name: String,
773    args: Vec<u8>,
774}
775
776impl CallFunctionBuilder {
777    /// Prepares a read-only query that doesn't require a signing transaction.
778    ///
779    /// ## Example
780    /// ```rust,no_run
781    /// use near_api::*;
782    ///
783    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
784    /// let balance: Data<u64> = Contract("some_contract.testnet".parse()?).call_function("get_balance", ())?.read_only().fetch_from_testnet().await?;
785    /// println!("Balance: {:?}", balance);
786    ///
787    /// let balance_at_block: Data<u64> = Contract("some_contract.testnet".parse()?).call_function("get_balance", ())?.read_only().at(Reference::AtBlock(1000000)).fetch_from_testnet().await?;
788    /// println!("Balance at block 1000000: {:?}", balance_at_block);
789    /// # Ok(())
790    /// # }
791    /// ```
792    pub fn read_only<Response: Send + Sync + DeserializeOwned>(
793        self,
794    ) -> RequestBuilder<CallResultHandler<Response>> {
795        let request = QueryRequest::CallFunction {
796            account_id: self.contract,
797            method_name: self.method_name,
798            args_base64: FunctionArgs(to_base64(&self.args)),
799        };
800
801        RequestBuilder::new(
802            SimpleQueryRpc { request },
803            Reference::Optimistic,
804            CallResultHandler::<Response>::new(),
805        )
806    }
807
808    /// Prepares a read-only query that deserializes the response using Borsh instead of JSON.
809    ///
810    /// This method is useful when the contract returns Borsh-encoded data instead of JSON.
811    ///
812    /// ## Example
813    /// ```rust,no_run
814    /// use near_api::*;
815    ///
816    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
817    /// let value: Data<u64> = Contract("some_contract.testnet".parse()?)
818    ///     .call_function("get_number", ())?
819    ///     .read_only_borsh()
820    ///     .fetch_from_testnet()
821    ///     .await?;
822    /// println!("Value: {:?}", value);
823    /// # Ok(())
824    /// # }
825    /// ```
826    pub fn read_only_borsh<Response: Send + Sync + BorshDeserialize>(
827        self,
828    ) -> RequestBuilder<CallResultBorshHandler<Response>> {
829        let request = QueryRequest::CallFunction {
830            account_id: self.contract,
831            method_name: self.method_name,
832            args_base64: FunctionArgs(to_base64(&self.args)),
833        };
834
835        RequestBuilder::new(
836            SimpleQueryRpc { request },
837            Reference::Optimistic,
838            CallResultBorshHandler::<Response>::new(),
839        )
840    }
841
842    /// Prepares a transaction that will call a contract function leading to a state change.
843    ///
844    /// This will require a signer to be provided and gas to be paid.
845    pub fn transaction(self) -> ContractTransactBuilder {
846        ContractTransactBuilder::new(self.contract, self.method_name, self.args)
847    }
848}
849
850#[derive(Clone, Debug)]
851pub struct ContractTransactBuilder {
852    contract: AccountId,
853    method_name: String,
854    args: Vec<u8>,
855    gas: Option<NearGas>,
856    deposit: Option<NearToken>,
857}
858
859impl ContractTransactBuilder {
860    const fn new(contract: AccountId, method_name: String, args: Vec<u8>) -> Self {
861        Self {
862            contract,
863            method_name,
864            args,
865            gas: None,
866            deposit: None,
867        }
868    }
869
870    /// Specify the gas limit for the transaction. By default it is set to 100 TGas.
871    pub const fn gas(mut self, gas: NearGas) -> Self {
872        self.gas = Some(gas);
873        self
874    }
875
876    /// Specify the max gas for the transaction. By default it is set to 300 TGas.
877    pub const fn max_gas(mut self) -> Self {
878        self.gas = Some(NearGas::from_tgas(300));
879        self
880    }
881
882    /// Specify the near deposit for the transaction. By default it is set to 0.
883    ///
884    /// Please note that the method should be [`payable`](https://docs.near.org/build/smart-contracts/anatomy/functions#payable-functions) in the contract to accept the deposit.
885    /// Otherwise the transaction will fail.
886    pub const fn deposit(mut self, deposit: NearToken) -> Self {
887        self.deposit = Some(deposit);
888        self
889    }
890
891    /// Specify the signer for the transaction. This will wrap-up the process of the preparing transaction.
892    ///
893    /// This will return the [`ExecuteSignedTransaction`] that can be used to sign and send the transaction to the network.
894    pub fn with_signer(
895        self,
896        signer_id: AccountId,
897        signer: Arc<Signer>,
898    ) -> ExecuteSignedTransaction {
899        self.with_signer_account(signer_id).with_signer(signer)
900    }
901
902    // Re-used by stake.rs and tokens.rs as we do have extra signer_id context, but we don't need there a signer
903    pub(crate) fn with_signer_account(self, signer_id: AccountId) -> ConstructTransaction {
904        let gas = self.gas.unwrap_or_else(|| NearGas::from_tgas(100));
905        let deposit = self.deposit.unwrap_or_else(|| NearToken::from_yoctonear(0));
906
907        Transaction::construct(signer_id, self.contract).add_action(Action::FunctionCall(Box::new(
908            FunctionCallAction {
909                method_name: self.method_name.to_owned(),
910                args: self.args,
911                gas,
912                deposit,
913            },
914        )))
915    }
916}
917
918/// Builder for querying contract code from the global contract code storage defined in [NEP-591](https://github.com/near/NEPs/blob/2f6b702d55a4cd470b50d35e2f3fde6e0fb4dced/neps/nep-0591.md).
919pub struct GlobalWasmBuilder;
920
921impl GlobalWasmBuilder {
922    /// Prepares a query to fetch global contract code ([Data]<[ContractCodeView](near_api_types::ContractCodeView)>) by account ID.
923    ///
924    /// # Example
925    /// ```rust,no_run
926    /// use near_api::*;
927    ///
928    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
929    /// let code = Contract::global_wasm()
930    ///     .by_account_id("nft-contract.testnet".parse()?)
931    ///     .fetch_from_testnet()
932    ///     .await?;
933    /// println!("Code: {}", code.data.code_base64);
934    /// # Ok(())
935    /// # }
936    /// ```
937    pub fn by_account_id(&self, account_id: AccountId) -> RequestBuilder<ViewCodeHandler> {
938        let request = QueryRequest::ViewGlobalContractCodeByAccountId { account_id };
939
940        RequestBuilder::new(
941            SimpleQueryRpc { request },
942            Reference::Optimistic,
943            ViewCodeHandler,
944        )
945    }
946
947    /// Prepares a query to fetch global contract code ([Data]<[ContractCodeView](near_api_types::ContractCodeView)>) by hash.
948    ///
949    /// # Example
950    /// ```rust,no_run
951    /// use near_api::*;
952    ///
953    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
954    /// let code = Contract::global_wasm()
955    ///     .by_hash("DxfRbrjT3QPmoANMDYTR6iXPGJr7xRUyDnQhcAWjcoFF".parse()?)
956    ///     .fetch_from_testnet()
957    ///     .await?;
958    /// println!("Code: {}", code.data.code_base64);
959    /// # Ok(())
960    /// # }
961    /// ```
962    pub fn by_hash(&self, code_hash: CryptoHash) -> RequestBuilder<ViewCodeHandler> {
963        let request = QueryRequest::ViewGlobalContractCode { code_hash };
964
965        RequestBuilder::new(
966            SimpleQueryRpc { request },
967            Reference::Optimistic,
968            ViewCodeHandler,
969        )
970    }
971}