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