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::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>, CallResultHandler<Vec<u8>>>,
341 > {
342 self.call_function("__contract_abi", ())
343 .read_only()
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 deserializes the response using Borsh instead of JSON.
803 ///
804 /// This method is useful when the contract returns Borsh-encoded data instead of JSON.
805 ///
806 /// ## Example
807 /// ```rust,no_run
808 /// use near_api::*;
809 ///
810 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
811 /// let value: Data<u64> = Contract("some_contract.testnet".parse()?)
812 /// .call_function("get_number", ())
813 /// .read_only_borsh()
814 /// .fetch_from_testnet()
815 /// .await?;
816 /// println!("Value: {:?}", value);
817 /// # Ok(())
818 /// # }
819 /// ```
820 pub fn read_only_borsh<Response: Send + Sync + BorshDeserialize>(
821 self,
822 ) -> RequestBuilder<CallResultBorshHandler<Response>> {
823 let request = QueryRequest::CallFunction {
824 account_id: self.contract,
825 method_name: self.method_name,
826 args_base64: FunctionArgs(self.args.as_deref().map(to_base64).unwrap_or_default()),
827 };
828
829 let mut builder = RequestBuilder::new(
830 SimpleQueryRpc { request },
831 Reference::Optimistic,
832 CallResultBorshHandler::<Response>::new(),
833 );
834 if let Err(err) = self.args {
835 builder = builder.with_deferred_error(err);
836 }
837 builder
838 }
839
840 /// Prepares a transaction that will call a contract function leading to a state change.
841 ///
842 /// This will require a signer to be provided and gas to be paid.
843 pub fn transaction(self) -> ContractTransactBuilder {
844 ContractTransactBuilder::new(self.contract, self.method_name, self.args)
845 }
846}
847
848#[derive(Debug, Clone)]
849pub struct ContractTransactBuilder {
850 contract: AccountId,
851 method_name: String,
852 args: Result<Vec<u8>, ArgumentValidationError>,
853 gas: Option<NearGas>,
854 deposit: Option<NearToken>,
855}
856
857impl ContractTransactBuilder {
858 const fn new(
859 contract: AccountId,
860 method_name: String,
861 args: Result<Vec<u8>, ArgumentValidationError>,
862 ) -> Self {
863 Self {
864 contract,
865 method_name,
866 args,
867 gas: None,
868 deposit: None,
869 }
870 }
871
872 /// Specify the gas limit for the transaction. By default it is set to 100 TGas.
873 pub const fn gas(mut self, gas: NearGas) -> Self {
874 self.gas = Some(gas);
875 self
876 }
877
878 /// Specify the max gas for the transaction. By default it is set to 300 TGas.
879 pub const fn max_gas(mut self) -> Self {
880 self.gas = Some(NearGas::from_tgas(300));
881 self
882 }
883
884 /// Specify the near deposit for the transaction. By default it is set to 0.
885 ///
886 /// 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.
887 /// Otherwise the transaction will fail.
888 pub const fn deposit(mut self, deposit: NearToken) -> Self {
889 self.deposit = Some(deposit);
890 self
891 }
892
893 /// Specify the signer for the transaction. This will wrap-up the process of the preparing transaction.
894 ///
895 /// This will return the [`ExecuteSignedTransaction`] that can be used to sign and send the transaction to the network.
896 pub fn with_signer(
897 self,
898 signer_id: AccountId,
899 signer: Arc<Signer>,
900 ) -> ExecuteSignedTransaction {
901 self.with_signer_account(signer_id).with_signer(signer)
902 }
903
904 // Re-used by stake.rs and tokens.rs as we do have extra signer_id context, but we don't need there a signer
905 pub(crate) fn with_signer_account(self, signer_id: AccountId) -> ConstructTransaction {
906 let gas = self.gas.unwrap_or_else(|| NearGas::from_tgas(100));
907 let deposit = self.deposit.unwrap_or_else(|| NearToken::from_yoctonear(0));
908
909 let err = self.args.as_deref().err().cloned();
910
911 let mut transaction = Transaction::construct(signer_id, self.contract).add_action(
912 Action::FunctionCall(Box::new(FunctionCallAction {
913 method_name: self.method_name.to_owned(),
914 args: self.args.unwrap_or_default(),
915 gas,
916 deposit,
917 })),
918 );
919 if let Some(err) = err {
920 transaction = transaction.with_deferred_error(err);
921 }
922 transaction
923 }
924}
925
926/// 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).
927pub struct GlobalWasmBuilder;
928
929impl GlobalWasmBuilder {
930 /// Prepares a query to fetch global contract code ([Data]<[ContractCodeView](near_api_types::ContractCodeView)>) by account ID.
931 ///
932 /// # Example
933 /// ```rust,no_run
934 /// use near_api::*;
935 ///
936 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
937 /// let code = Contract::global_wasm()
938 /// .by_account_id("nft-contract.testnet".parse()?)
939 /// .fetch_from_testnet()
940 /// .await?;
941 /// println!("Code: {}", code.data.code_base64);
942 /// # Ok(())
943 /// # }
944 /// ```
945 pub fn by_account_id(&self, account_id: AccountId) -> RequestBuilder<ViewCodeHandler> {
946 let request = QueryRequest::ViewGlobalContractCodeByAccountId { account_id };
947
948 RequestBuilder::new(
949 SimpleQueryRpc { request },
950 Reference::Optimistic,
951 ViewCodeHandler,
952 )
953 }
954
955 /// Prepares a query to fetch global contract code ([Data]<[ContractCodeView](near_api_types::ContractCodeView)>) by hash.
956 ///
957 /// # Example
958 /// ```rust,no_run
959 /// use near_api::*;
960 ///
961 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
962 /// let code = Contract::global_wasm()
963 /// .by_hash("DxfRbrjT3QPmoANMDYTR6iXPGJr7xRUyDnQhcAWjcoFF".parse()?)
964 /// .fetch_from_testnet()
965 /// .await?;
966 /// println!("Code: {}", code.data.code_base64);
967 /// # Ok(())
968 /// # }
969 /// ```
970 pub fn by_hash(&self, code_hash: CryptoHash) -> RequestBuilder<ViewCodeHandler> {
971 let request = QueryRequest::ViewGlobalContractCode { code_hash };
972
973 RequestBuilder::new(
974 SimpleQueryRpc { request },
975 Reference::Optimistic,
976 ViewCodeHandler,
977 )
978 }
979}