near_api/
contract.rs

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