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