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