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