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 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 near deposit for the transaction. By default it is set to 0.
783 ///
784 /// 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.
785 /// Otherwise the transaction will fail.
786 pub const fn deposit(mut self, deposit: NearToken) -> Self {
787 self.deposit = Some(deposit);
788 self
789 }
790
791 /// Specify the signer for the transaction. This will wrap-up the process of the preparing transaction.
792 ///
793 /// This will return the [`ExecuteSignedTransaction`] that can be used to sign and send the transaction to the network.
794 pub fn with_signer(
795 self,
796 signer_id: AccountId,
797 signer: Arc<Signer>,
798 ) -> ExecuteSignedTransaction {
799 self.with_signer_account(signer_id).with_signer(signer)
800 }
801
802 // Re-used by stake.rs and tokens.rs as we do have extra signer_id context, but we don't need there a signer
803 pub(crate) fn with_signer_account(self, signer_id: AccountId) -> ConstructTransaction {
804 let gas = self.gas.unwrap_or_else(|| NearGas::from_tgas(100));
805 let deposit = self.deposit.unwrap_or_else(|| NearToken::from_yoctonear(0));
806
807 Transaction::construct(signer_id, self.contract).add_action(Action::FunctionCall(Box::new(
808 FunctionCallAction {
809 method_name: self.method_name.to_owned(),
810 args: self.args,
811 gas,
812 deposit,
813 },
814 )))
815 }
816}