diff --git a/Cargo.lock b/Cargo.lock index a7bc64efd5..ec245b66b9 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,9 +47,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -182,9 +182,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -1286,9 +1286,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "globset" @@ -1449,9 +1449,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -2054,6 +2054,7 @@ dependencies = [ "multiversx-chain-scenario-format", "multiversx-sc-scenario", "multiversx-sdk", + "serde_json", "tokio", ] @@ -2075,6 +2076,7 @@ dependencies = [ "hex", "hmac", "itertools", + "log", "pbkdf2", "pem", "rand 0.8.5", @@ -2232,9 +2234,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] @@ -3885,9 +3887,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -3944,9 +3946,9 @@ dependencies = [ [[package]] name = "zip" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2568cd0f20e86cd9a7349fe05178f7bd22f22724678448ae5a9bac266df2689" +checksum = "1dd56a4d5921bc2f99947ac5b3abe5f510b1be7376fdc5e9fce4a23c6a93e87c" dependencies = [ "arbitrary", "crc32fast", diff --git a/framework/scenario/src/scenario/model/transaction.rs b/framework/scenario/src/scenario/model/transaction.rs index c1a6d6a271..3ffa8a542d 100644 --- a/framework/scenario/src/scenario/model/transaction.rs +++ b/framework/scenario/src/scenario/model/transaction.rs @@ -9,7 +9,6 @@ mod tx_interpret_util; mod tx_query; mod tx_response; mod tx_response_status; -mod tx_response_utils; mod tx_transfer; mod tx_validator_reward; mod typed_response; @@ -24,7 +23,6 @@ pub use tx_expect::*; pub use tx_query::*; pub use tx_response::TxResponse; pub use tx_response_status::TxResponseStatus; -pub use tx_response_utils::*; pub use tx_transfer::*; pub use tx_validator_reward::*; pub use typed_response::TypedResponse; diff --git a/framework/scenario/src/scenario/model/transaction/tx_response.rs b/framework/scenario/src/scenario/model/transaction/tx_response.rs index 1465885476..46806b1183 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_response.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_response.rs @@ -1,16 +1,8 @@ -use multiversx_chain_vm::{crypto_functions::keccak256, tx_mock::TxResult}; -use multiversx_sc::types::{Address, ESDTSystemSCAddress}; -use multiversx_sdk::{ - data::transaction::{ApiLogs, ApiSmartContractResult, Events, TransactionOnNetwork}, - utils::base64_decode, -}; +use multiversx_chain_vm::tx_mock::TxResult; +use multiversx_sc::types::Address; +use multiversx_sdk::data::transaction::{ApiLogs, ApiSmartContractResult}; -use super::{ - decode_scr_data_or_panic, is_out_scr, process_topics_error, Log, TxExpect, TxResponseStatus, -}; - -const SC_DEPLOY_PROCESSING_TYPE: &str = "SCDeployment"; -const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError"; +use super::{Log, TxExpect, TxResponseStatus}; #[derive(Debug, Default, Clone)] /// The response of a transaction. @@ -48,26 +40,6 @@ impl TxResponse { } } - /// Creates a [`TxResponse`] from a [`TransactionOnNetwork`]. - pub fn from_network_tx(tx: TransactionOnNetwork) -> Self { - let mut response = Self { - api_scrs: tx.smart_contract_results.unwrap_or_default(), - api_logs: tx.logs, - ..Default::default() - }; - - response.tx_error = response.process_signal_error(); - if !response.tx_error.is_success() { - return response; - } - - response.process( - tx.sender.to_bytes(), - tx.nonce, - tx.processing_type_on_destination, - ) - } - /// Creates a [`TxResponse`] from raw results. pub fn from_raw_results(raw_results: Vec>) -> Self { TxResponse { @@ -103,154 +75,4 @@ impl TxResponse { pub fn is_success(&self) -> bool { self.tx_error.is_success() } - - fn process_signal_error(&self) -> TxResponseStatus { - if let Some(event) = self.find_log(LOG_IDENTIFIER_SIGNAL_ERROR) { - let topics = event.topics.as_ref(); - if let Some(error) = process_topics_error(topics) { - return TxResponseStatus::signal_error(&error); - } - - let error_raw = base64_decode(topics.unwrap().get(1).unwrap()); - let error = String::from_utf8(error_raw).unwrap(); - return TxResponseStatus::signal_error(&error); - } - - TxResponseStatus::default() - } - - fn process( - self, - sender_address: [u8; 32], - nonce: u64, - processing_type_on_destination: String, - ) -> Self { - self.process_out() - .process_new_deployed_address(sender_address, nonce, processing_type_on_destination) - .process_new_issued_token_identifier() - } - - fn process_out(mut self) -> Self { - let out_scr = self.api_scrs.iter().find(is_out_scr); - - if let Some(out_scr) = out_scr { - self.out = decode_scr_data_or_panic(&out_scr.data); - } else if let Some(data) = self.process_out_from_log() { - self.out = data - } - - self - } - - fn process_out_from_log(&self) -> Option>> { - if let Some(logs) = &self.api_logs { - logs.events.iter().rev().find_map(|event| { - if event.identifier == "writeLog" { - if let Some(data) = &event.data { - let decoded_data = String::from_utf8(base64_decode(data)).unwrap(); - - if decoded_data.starts_with('@') { - let out = decode_scr_data_or_panic(decoded_data.as_str()); - return Some(out); - } - } - } - - None - }) - } else { - None - } - } - - fn process_new_deployed_address( - mut self, - sender_address_bytes: [u8; 32], - nonce: u64, - processing_type_on_destination: String, - ) -> Self { - if processing_type_on_destination != SC_DEPLOY_PROCESSING_TYPE { - return self; - } - - let sender_nonce_bytes = nonce.to_le_bytes(); - let mut bytes_to_hash: Vec = Vec::new(); - bytes_to_hash.extend_from_slice(&sender_address_bytes); - bytes_to_hash.extend_from_slice(&sender_nonce_bytes); - - let address_keccak = keccak256(&bytes_to_hash); - - let mut address = [0u8; 32]; - - address[0..8].copy_from_slice(&[0u8; 8]); - address[8..10].copy_from_slice(&[5, 0]); - address[10..30].copy_from_slice(&address_keccak[10..30]); - address[30..32].copy_from_slice(&sender_address_bytes[30..32]); - - self.new_deployed_address = Some(Address::from(address)); - - self - } - - fn process_new_issued_token_identifier(mut self) -> Self { - for scr in self.api_scrs.iter() { - if scr.sender.to_bech32_string().unwrap() != ESDTSystemSCAddress.to_bech32_string() { - continue; - } - - let Some(prev_tx) = self.api_scrs.iter().find(|e| e.hash == scr.prev_tx_hash) else { - continue; - }; - - let is_issue_fungible = prev_tx.data.starts_with("issue@"); - let is_issue_semi_fungible = prev_tx.data.starts_with("issueSemiFungible@"); - let is_issue_non_fungible = prev_tx.data.starts_with("issueNonFungible@"); - let is_register_meta_esdt = prev_tx.data.starts_with("registerMetaESDT@"); - let is_register_and_set_all_roles_esdt = - prev_tx.data.starts_with("registerAndSetAllRoles@"); - - if !is_issue_fungible - && !is_issue_semi_fungible - && !is_issue_non_fungible - && !is_register_meta_esdt - && !is_register_and_set_all_roles_esdt - { - continue; - } - - if scr.data.starts_with("ESDTTransfer@") { - let encoded_tid = scr.data.split('@').nth(1); - if encoded_tid.is_none() { - return self; - } - - self.new_issued_token_identifier = - Some(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap()); - - break; - } else if scr.data.starts_with("@00@") { - let encoded_tid = scr.data.split('@').nth(2); - if encoded_tid.is_none() { - return self; - } - - self.new_issued_token_identifier = - Some(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap()); - - break; - } - } - - self - } - - fn find_log(&self, log_identifier: &str) -> Option<&Events> { - if let Some(logs) = &self.api_logs { - logs.events - .iter() - .find(|event| event.identifier == log_identifier) - } else { - None - } - } } diff --git a/framework/scenario/src/scenario/model/transaction/tx_response_status.rs b/framework/scenario/src/scenario/model/transaction/tx_response_status.rs index 5c9d057615..e7902047c6 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_response_status.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_response_status.rs @@ -9,7 +9,7 @@ pub struct TxResponseStatus { impl TxResponseStatus { /// Creates a [`TxResponseStatus`] - pub(crate) fn new(status: u64, message: &str) -> Self { + pub fn new(status: u64, message: &str) -> Self { Self { status, message: message.to_string(), @@ -17,7 +17,7 @@ impl TxResponseStatus { } /// Creates a [`TxResponseStatus`] that signals an error. - pub(crate) fn signal_error(message: &str) -> Self { + pub fn signal_error(message: &str) -> Self { Self::new(4, message) } diff --git a/framework/scenario/src/scenario/model/transaction/tx_response_utils.rs b/framework/scenario/src/scenario/model/transaction/tx_response_utils.rs deleted file mode 100644 index 36c86e920a..0000000000 --- a/framework/scenario/src/scenario/model/transaction/tx_response_utils.rs +++ /dev/null @@ -1,35 +0,0 @@ -use multiversx_sdk::data::transaction::ApiSmartContractResult; - -/// Checks for invalid topics. -pub fn process_topics_error(topics: Option<&Vec>) -> Option { - if topics.is_none() { - return Some("missing topics".to_string()); - } - - let topics = topics.unwrap(); - if topics.len() != 2 { - Some(format!( - "expected to have 2 topics, found {} instead", - topics.len() - )) - } else { - None - } -} - -/// Decodes the data of a smart contract result. -pub fn decode_scr_data_or_panic(data: &str) -> Vec> { - let mut split = data.split('@'); - let _ = split.next().expect("SCR data should start with '@'"); - let result_code = split.next().expect("missing result code"); - assert_eq!(result_code, "6f6b", "result code is not 'ok'"); - - split - .map(|encoded_arg| hex::decode(encoded_arg).expect("error hex-decoding result")) - .collect() -} - -/// Checks if the given smart contract result is an out smart contract result. -pub fn is_out_scr(scr: &&ApiSmartContractResult) -> bool { - scr.nonce != 0 && scr.data.starts_with('@') -} diff --git a/framework/scenario/tests/contract_without_macros.rs b/framework/scenario/tests/contract_without_macros.rs index 91ca8b2eba..1121e88fdd 100644 --- a/framework/scenario/tests/contract_without_macros.rs +++ b/framework/scenario/tests/contract_without_macros.rs @@ -7,7 +7,6 @@ // and maintenance. #![allow(unused)] -#![allow(deprecated)] // TODO: unified syntax use multiversx_sc::{ contract_base::ProxyObjNew, @@ -103,11 +102,116 @@ mod module_1 { } pub trait ProxyTrait: multiversx_sc::contract_base::ProxyObjBase + Sized { + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] fn version( &mut self, - ) -> multiversx_sc::types::ContractCallNoPayment> { - let ___address___ = self.extract_address(); - multiversx_sc::types::ContractCallNoPayment::new(___address___, "version") + ) -> multiversx_sc::types::Tx< + multiversx_sc::types::TxScEnv, + (), + Self::To, + (), + (), + multiversx_sc::types::FunctionCall, + multiversx_sc::types::OriginalResultMarker>, + > { + multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc() + .to(self.extract_proxy_to()) + .original_result() + .raw_call("version") + } + } +} + +mod sampler_adder_proxy { + #![allow(dead_code)] + #![allow(clippy::all)] + use multiversx_sc::proxy_imports::*; + pub struct AdderProxy; + impl TxProxyTrait for AdderProxy + where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, + { + type TxProxyMethods = AdderProxyMethods; + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + AdderProxyMethods { wrapped_tx: tx } + } + } + pub struct AdderProxyMethods + where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, + { + wrapped_tx: Tx, + } + #[rustfmt::skip] + impl AdderProxyMethods + where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, + { + pub fn init>>( + self, + initial_value: Arg0, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&initial_value) + .original_result() + } + } + #[rustfmt::skip] + impl AdderProxyMethods + where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, + { + pub fn upgrade>>( + self, + initial_value: Arg0, + ) -> TxTypedUpgrade { + self.wrapped_tx + .payment(NotPayable) + .raw_upgrade() + .argument(&initial_value) + .original_result() + } + } + #[rustfmt::skip] + impl AdderProxyMethods + where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, + { + pub fn sum( + self, + ) -> TxTypedCall> { + self.wrapped_tx.payment(NotPayable).raw_call("getSum").original_result() + } + /// Add desired amount to the storage variable. + pub fn add>>( + self, + value: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("add") + .argument(&value) + .original_result() } } } @@ -121,21 +225,25 @@ mod sample_adder { pub trait Adder: super::module_1::VersionModule + multiversx_sc::contract_base::ContractBase + Sized { - fn init(&self, initial_value: &BigInt) { - self.set_sum(initial_value); - } - fn add(&self, value: BigInt) { - let mut sum = self.get_sum(); - sum.add_assign(value); - self.set_sum(&sum); - } - fn get_sum(&self) -> BigInt; - fn set_sum(&self, sum: &BigInt); - fn add_version(&self) { - self.add(self.version()) - } - fn callback(&self); - fn callbacks(&self) -> self::CallbackProxyObj; + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn init(&self, initial_value: multiversx_sc::types::BigUint) { + self.sum().set(initial_value); + } + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn upgrade(&self, initial_value: multiversx_sc::types::BigUint) { + self.init(initial_value); + } + /// Add desired amount to the storage variable. + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn add(&self, value: multiversx_sc::types::BigUint) { + self.sum().update(|sum| *sum += value); + } + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn sum(&self) -> SingleValueMapper>; } ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -149,17 +257,14 @@ mod sample_adder { where C: AutoImpl + super::module_1::AutoImpl, { - fn get_sum(&self) -> BigInt { - let mut ___key___ = multiversx_sc::storage::StorageKey::::new(&b"sum"[..]); - multiversx_sc::storage_get(multiversx_sc::types::ManagedRef::new(&___key___)) - } - fn set_sum(&self, sum: &BigInt) { + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn sum(&self) -> SingleValueMapper> { let mut ___key___ = multiversx_sc::storage::StorageKey::::new(&b"sum"[..]); - multiversx_sc::storage_set(multiversx_sc::types::ManagedRef::new(&___key___), &sum); - } - fn callback(&self) {} - fn callbacks(&self) -> self::CallbackProxyObj { - as multiversx_sc::contract_base::CallbackProxyObjBase>::new_cb_proxy_obj() + , + > as multiversx_sc::storage::mappers::StorageMapper>::new(___key___) } } @@ -172,11 +277,11 @@ mod sample_adder { Adder + multiversx_sc::contract_base::ContractBase + super::module_1::EndpointWrappers { #[inline] - fn call_get_sum(&self) { + fn call_sum(&self) { ::init_static(); multiversx_sc::io::call_value_init::not_payable::(); let () = multiversx_sc::io::load_endpoint_args::(()); - let result = self.get_sum(); + let result = self.sum(); multiversx_sc::io::finish_multi::(&result); } #[inline] @@ -185,9 +290,19 @@ mod sample_adder { multiversx_sc::io::call_value_init::not_payable::(); let (initial_value, ()) = multiversx_sc::io::load_endpoint_args::< Self::Api, - (multiversx_sc::types::BigInt, ()), + (multiversx_sc::types::BigUint, ()), >(("initial_value", ())); - self.init(&initial_value); + self.init(initial_value); + } + #[inline] + fn call_upgrade(&self) { + ::init_static(); + multiversx_sc::io::call_value_init::not_payable::(); + let (initial_value, ()) = multiversx_sc::io::load_endpoint_args::< + Self::Api, + (multiversx_sc::types::BigUint, ()), + >(("initial_value", ())); + self.upgrade(initial_value); } #[inline] fn call_add(&self) { @@ -195,38 +310,55 @@ mod sample_adder { multiversx_sc::io::call_value_init::not_payable::(); let (value, ()) = multiversx_sc::io::load_endpoint_args::< Self::Api, - (multiversx_sc::types::BigInt, ()), + (multiversx_sc::types::BigUint, ()), >(("value", ())); self.add(value); } - fn call(&self, fn_name: &str) -> bool { if match fn_name { "callBack" => { - Adder::callback(self); + self::EndpointWrappers::callback(self); + return true; + }, + "init" + if ::external_view_init_override() => + { + multiversx_sc::external_view_contract::external_view_contract_constructor::< + Self::Api, + >(); return true; }, "getSum" => { - self.call_get_sum(); + self.call_sum(); true }, - "init" => { + "init" + if !::external_view_init_override() => + { self.call_init(); true }, + "upgrade" => { + self.call_upgrade(); + true + }, "add" => { self.call_add(); true }, - _other => false, + other => false, } { return true; } - if super::module_1::EndpointWrappers::call(self, fn_name) { - return true; - } false } + fn callback_selector( + &self, + mut ___cb_closure___: multiversx_sc::types::CallbackClosureForDeser, + ) -> multiversx_sc::types::CallbackSelectorResult { + multiversx_sc::types::CallbackSelectorResult::NotProcessed(___cb_closure___) + } + fn callback(&self) {} } impl EndpointWrappers for multiversx_sc::contract_base::UniversalContractObj where @@ -234,24 +366,224 @@ mod sample_adder { { } + pub struct AbiProvider {} + impl multiversx_sc::contract_base::ContractAbiProvider for AbiProvider { + type Api = multiversx_sc::api::uncallable::UncallableApi; + fn abi() -> multiversx_sc::abi::ContractAbi { + let mut contract_abi = multiversx_sc::abi::ContractAbi::new( + multiversx_sc::abi::BuildInfoAbi { + contract_crate: multiversx_sc::abi::ContractCrateBuildAbi { + name: "adder", + version: "0.0.0", + git_version: "", + }, + framework: multiversx_sc::abi::FrameworkBuildAbi::create(), + }, + &[ + "One of the simplest smart contracts possible,", + "it holds a single variable in storage, which anyone can increment.", + ], + "Adder", + false, + ); + let mut endpoint_abi = multiversx_sc::abi::EndpointAbi::new( + &[], + "getSum", + "sum", + false, + false, + multiversx_sc::abi::EndpointMutabilityAbi::Readonly, + multiversx_sc::abi::EndpointTypeAbi::Endpoint, + &[], + &[], + false, + ); + endpoint_abi + .add_output::< + SingleValueMapper>, + >(&[]); + contract_abi + .add_type_descriptions::< + SingleValueMapper>, + >(); + contract_abi.endpoints.push(endpoint_abi); + let mut endpoint_abi = multiversx_sc::abi::EndpointAbi::new( + &[], + "init", + "init", + false, + false, + multiversx_sc::abi::EndpointMutabilityAbi::Mutable, + multiversx_sc::abi::EndpointTypeAbi::Init, + &[], + &[], + false, + ); + endpoint_abi.add_input::>("initial_value"); + contract_abi.add_type_descriptions::>(); + contract_abi.constructors.push(endpoint_abi); + let mut endpoint_abi = multiversx_sc::abi::EndpointAbi::new( + &[], + "upgrade", + "upgrade", + false, + false, + multiversx_sc::abi::EndpointMutabilityAbi::Mutable, + multiversx_sc::abi::EndpointTypeAbi::Upgrade, + &[], + &[], + false, + ); + endpoint_abi.add_input::>("initial_value"); + contract_abi.add_type_descriptions::>(); + contract_abi.upgrade_constructors.push(endpoint_abi); + let mut endpoint_abi = multiversx_sc::abi::EndpointAbi::new( + &["Add desired amount to the storage variable."], + "add", + "add", + false, + false, + multiversx_sc::abi::EndpointMutabilityAbi::Mutable, + multiversx_sc::abi::EndpointTypeAbi::Endpoint, + &[], + &[], + false, + ); + endpoint_abi.add_input::>("value"); + contract_abi.add_type_descriptions::>(); + contract_abi.endpoints.push(endpoint_abi); + contract_abi + } + } + + #[allow(non_snake_case)] + pub mod endpoints { + use super::EndpointWrappers; + pub fn sum() + where + A: multiversx_sc::api::VMApi, + { + super::EndpointWrappers::call_sum( + &multiversx_sc::contract_base::UniversalContractObj::::new(), + ); + } + pub fn init() + where + A: multiversx_sc::api::VMApi, + { + super::EndpointWrappers::call_init( + &multiversx_sc::contract_base::UniversalContractObj::::new(), + ); + } + pub fn upgrade() + where + A: multiversx_sc::api::VMApi, + { + super::EndpointWrappers::call_upgrade( + &multiversx_sc::contract_base::UniversalContractObj::::new(), + ); + } + pub fn add() + where + A: multiversx_sc::api::VMApi, + { + super::EndpointWrappers::call_add( + &multiversx_sc::contract_base::UniversalContractObj::::new(), + ); + } + pub fn callBack() + where + A: multiversx_sc::api::VMApi, + { + super::EndpointWrappers::callback( + &multiversx_sc::contract_base::UniversalContractObj::::new(), + ); + } + } pub trait ProxyTrait: multiversx_sc::contract_base::ProxyObjBase + super::module_1::ProxyTrait { - fn get_sum( + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn sum( &mut self, - ) -> multiversx_sc::types::ContractCallNoPayment> { - let ___address___ = self.extract_address(); - multiversx_sc::types::ContractCallNoPayment::new(___address___, "get_sum") - } - fn add( + ) -> multiversx_sc::types::Tx< + multiversx_sc::types::TxScEnv, + (), + Self::To, + (), + (), + multiversx_sc::types::FunctionCall, + multiversx_sc::types::OriginalResultMarker< + SingleValueMapper>, + >, + > { + multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc() + .to(self.extract_proxy_to()) + .original_result() + .raw_call("getSum") + } + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn init>>( &mut self, - amount: &BigInt, - ) -> multiversx_sc::types::ContractCallNoPayment { - let ___address___ = self.extract_address(); - let mut ___contract_call___ = - multiversx_sc::types::ContractCallNoPayment::new(___address___, "add"); - multiversx_sc::types::ContractCall::proxy_arg(&mut ___contract_call___, amount); - ___contract_call___ + initial_value: Arg0, + ) -> multiversx_sc::types::Tx< + multiversx_sc::types::TxScEnv, + (), + Self::To, + (), + (), + multiversx_sc::types::DeployCall, ()>, + multiversx_sc::types::OriginalResultMarker<()>, + > { + multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc() + .raw_deploy() + .argument(&initial_value) + .original_result() + .to(self.extract_proxy_to()) + } + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn upgrade< + Arg0: multiversx_sc::types::ProxyArg>, + >( + &mut self, + initial_value: Arg0, + ) -> multiversx_sc::types::Tx< + multiversx_sc::types::TxScEnv, + (), + Self::To, + (), + (), + multiversx_sc::types::FunctionCall, + multiversx_sc::types::OriginalResultMarker<()>, + > { + multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc() + .to(self.extract_proxy_to()) + .original_result() + .raw_call("upgrade") + .argument(&initial_value) + } + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn add>>( + &mut self, + value: Arg0, + ) -> multiversx_sc::types::Tx< + multiversx_sc::types::TxScEnv, + (), + Self::To, + (), + (), + multiversx_sc::types::FunctionCall, + multiversx_sc::types::OriginalResultMarker<()>, + > { + multiversx_sc::types::TxBaseWithEnv::new_tx_from_sc() + .to(self.extract_proxy_to()) + .original_result() + .raw_call("add") + .argument(&value) } } @@ -288,17 +620,21 @@ mod sample_adder { A: multiversx_sc::api::VMApi, { fn call(&self, fn_name: &str) -> bool { - EndpointWrappers::call( - &multiversx_sc::contract_base::UniversalContractObj::::new(), - fn_name, - ) + EndpointWrappers::call(self, fn_name) } } + pub fn contract_obj() -> ContractObj + where + A: multiversx_sc::api::VMApi, + { + ContractObj { + _phantom: core::marker::PhantomData, + } + } pub struct ContractBuilder; - - impl multiversx_sc::contract_base::CallableContractBuilder for ContractBuilder { - fn new_contract_obj( + impl multiversx_sc::contract_base::CallableContractBuilder for self::ContractBuilder { + fn new_contract_obj( &self, ) -> multiversx_sc::types::heap::Box { @@ -308,25 +644,6 @@ mod sample_adder { } } - pub struct AbiProvider {} - - impl multiversx_sc::contract_base::ContractAbiProvider for AbiProvider { - type Api = multiversx_sc::api::uncallable::UncallableApi; - - fn abi() -> multiversx_sc::abi::ContractAbi { - multiversx_sc::abi::ContractAbi::default() - } - } - - pub fn contract_obj() -> ContractObj - where - A: multiversx_sc::api::VMApi, - { - ContractObj { - _phantom: core::marker::PhantomData, - } - } - pub struct Proxy where A: multiversx_sc::api::VMApi + 'static, @@ -465,27 +782,24 @@ fn contract_without_macros_basic() { let adder = sample_adder::contract_obj::(); - adder.init(&BigInt::from(5)); - assert_eq!(BigInt::from(5), adder.get_sum()); + adder.init(multiversx_sc::types::BigUint::from(5u32)); + assert_eq!(multiversx_sc::types::BigUint::from(5u32), adder.sum().get()); - adder.add(BigInt::from(7)); - assert_eq!(BigInt::from(12), adder.get_sum()); - - adder.add(BigInt::from(-1)); - assert_eq!(BigInt::from(11), adder.get_sum()); + adder.add(multiversx_sc::types::BigUint::from(7u32)); + assert_eq!( + multiversx_sc::types::BigUint::from(12u32), + adder.sum().get() + ); assert_eq!(BigInt::from(100), adder.version()); - adder.add_version(); - assert_eq!(BigInt::from(111), adder.get_sum()); - assert!(!adder.call("invalid_endpoint")); - assert!(adder.call("version")); + assert!(adder.call("getSum")); let mut own_proxy = sample_adder::Proxy::::new_proxy_obj().contract(ManagedAddress::zero()); - let _ = own_proxy.get_sum(); + let _ = own_proxy.sum(); let _ = multiversx_sc_meta_lib::abi_json::contract_abi::(); } diff --git a/framework/snippets/Cargo.toml b/framework/snippets/Cargo.toml index 495e98425e..4d4b3d4917 100644 --- a/framework/snippets/Cargo.toml +++ b/framework/snippets/Cargo.toml @@ -32,3 +32,6 @@ path = "../../sdk/scenario-format" [dependencies.multiversx-sdk] version = "=0.4.1" path = "../../sdk/core" + +[dev-dependencies] +serde_json = "1.0" diff --git a/framework/snippets/src/account_tool.rs b/framework/snippets/src/account_tool.rs index db1f14b4af..b10baf4a80 100644 --- a/framework/snippets/src/account_tool.rs +++ b/framework/snippets/src/account_tool.rs @@ -4,20 +4,20 @@ use multiversx_sc_scenario::{ scenario_model::{Account, BytesKey, BytesValue, Scenario, SetStateStep, Step}, }; use multiversx_sdk::{ - blockchain::CommunicationProxy, data::{address::Address, esdt::EsdtBalance}, + gateway::GatewayProxy, }; use std::collections::{BTreeMap, HashMap}; /// Called directly from CLI, from `sc-meta`. -/// +/// /// Retrieves an account data via the API, /// then formats it as a scenario set state step. pub async fn print_account_as_scenario_set_state( api_string: String, address_bech32_string: String, ) { - let api = CommunicationProxy::new(api_string); + let api = GatewayProxy::new(api_string); let address = Bech32Address::from_bech32_string(address_bech32_string); let set_state = retrieve_account_as_scenario_set_state(&api, &address).await; let scenario = build_scenario(set_state); @@ -34,7 +34,7 @@ fn build_scenario(set_state: SetStateStep) -> Scenario { } pub async fn retrieve_account_as_scenario_set_state( - api: &CommunicationProxy, + api: &GatewayProxy, address: &Bech32Address, ) -> SetStateStep { let sdk_address = Address::from_bech32_string(address.to_bech32_str()).unwrap(); diff --git a/framework/snippets/src/interactor.rs b/framework/snippets/src/interactor.rs index 82c59752f6..bb38873afe 100644 --- a/framework/snippets/src/interactor.rs +++ b/framework/snippets/src/interactor.rs @@ -5,8 +5,8 @@ use multiversx_sc_scenario::{ scenario_model::AddressValue, }; use multiversx_sdk::{ - blockchain::CommunicationProxy, data::{address::Address as ErdrsAddress, network_config::NetworkConfig}, + gateway::GatewayProxy, wallet::Wallet, }; use std::{ @@ -20,7 +20,7 @@ use crate::{account_tool::retrieve_account_as_scenario_set_state, Sender}; pub const INTERACTOR_SCENARIO_TRACE_PATH: &str = "interactor_trace.scen.json"; pub struct Interactor { - pub proxy: CommunicationProxy, + pub proxy: GatewayProxy, pub network_config: NetworkConfig, pub sender_map: HashMap, @@ -33,7 +33,7 @@ pub struct Interactor { impl Interactor { pub async fn new(gateway_url: &str) -> Self { - let proxy = CommunicationProxy::new(gateway_url.to_string()); + let proxy = GatewayProxy::new(gateway_url.to_string()); let network_config = proxy.get_network_config().await.unwrap(); Self { proxy, diff --git a/framework/snippets/src/interactor_scenario/interactor_sc_call.rs b/framework/snippets/src/interactor_scenario/interactor_sc_call.rs index d01e4af590..6802304ae9 100644 --- a/framework/snippets/src/interactor_scenario/interactor_sc_call.rs +++ b/framework/snippets/src/interactor_scenario/interactor_sc_call.rs @@ -1,9 +1,9 @@ -use crate::{address_h256_to_erdrs, mandos_to_erdrs_address, Interactor}; +use crate::{address_h256_to_erdrs, mandos_to_erdrs_address, network_response, Interactor}; use log::info; use multiversx_sc_scenario::{ api::StaticApi, scenario::ScenarioRunner, - scenario_model::{ScCallStep, SetStateStep, TxCall, TxResponse}, + scenario_model::{ScCallStep, SetStateStep, TxCall}, }; use multiversx_sdk::{data::transaction::Transaction, utils::base64_encode}; @@ -14,9 +14,9 @@ impl Interactor { { let sc_call_step = sc_call_step.as_mut(); let tx_hash = self.launch_sc_call(sc_call_step).await; - let tx = self.retrieve_tx_on_network(tx_hash.clone()).await; + let tx = self.proxy.retrieve_tx_on_network(tx_hash.clone()).await; - sc_call_step.save_response(TxResponse::from_network_tx(tx)); + sc_call_step.save_response(network_response::parse_tx_response(tx)); if let Some(token_identifier) = sc_call_step.response().new_issued_token_identifier.clone() { diff --git a/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs b/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs index 87d17dff7f..59de249e83 100644 --- a/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs +++ b/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs @@ -1,9 +1,9 @@ -use crate::{mandos_to_erdrs_address, Interactor}; +use crate::{mandos_to_erdrs_address, network_response, Interactor}; use log::info; use multiversx_sc_scenario::{ imports::Bech32Address, mandos_system::ScenarioRunner, - scenario_model::{ScDeployStep, SetStateStep, TxResponse}, + scenario_model::{ScDeployStep, SetStateStep}, }; use multiversx_sdk::{ data::{address::Address as ErdrsAddress, transaction::Transaction}, @@ -53,11 +53,11 @@ impl Interactor { { let sc_deploy_step = sc_deploy_step.as_mut(); let tx_hash = self.launch_sc_deploy(sc_deploy_step).await; - let tx = self.retrieve_tx_on_network(tx_hash.clone()).await; + let tx = self.proxy.retrieve_tx_on_network(tx_hash.clone()).await; let addr = sc_deploy_step.tx.from.clone(); let nonce = tx.nonce; - sc_deploy_step.save_response(TxResponse::from_network_tx(tx)); + sc_deploy_step.save_response(network_response::parse_tx_response(tx)); let deploy_address = sc_deploy_step .response() diff --git a/framework/snippets/src/interactor_scenario/interactor_transfer.rs b/framework/snippets/src/interactor_scenario/interactor_transfer.rs index 2e99372b0f..6557e4119f 100644 --- a/framework/snippets/src/interactor_scenario/interactor_transfer.rs +++ b/framework/snippets/src/interactor_scenario/interactor_transfer.rs @@ -14,7 +14,7 @@ impl Interactor { println!("transfer tx hash: {tx_hash}"); info!("transfer tx hash: {}", tx_hash); - self.retrieve_tx_on_network(tx_hash.clone()).await; + self.proxy.retrieve_tx_on_network(tx_hash.clone()).await; self.post_runners.run_transfer_step(&transfer_step); diff --git a/framework/snippets/src/lib.rs b/framework/snippets/src/lib.rs index 07c5789d8c..7d3b52c696 100644 --- a/framework/snippets/src/lib.rs +++ b/framework/snippets/src/lib.rs @@ -1,11 +1,11 @@ pub mod account_tool; mod interactor; mod interactor_dns; -mod interactor_retrieve; mod interactor_scenario; mod interactor_sender; mod interactor_tx; mod multi; +pub mod network_response; pub mod test_wallets; pub use env_logger; diff --git a/framework/snippets/src/multi/interactor_multi_sc_exec.rs b/framework/snippets/src/multi/interactor_multi_sc_exec.rs index d99df9c807..8ac6a4eba9 100644 --- a/framework/snippets/src/multi/interactor_multi_sc_exec.rs +++ b/framework/snippets/src/multi/interactor_multi_sc_exec.rs @@ -1,6 +1,5 @@ use super::interactor_multi_sc_process::{update_nonces_and_sign_tx, SenderSet, Txs}; -use crate::{Interactor, InteractorStep, StepBuffer}; -use multiversx_sc_scenario::scenario_model::TxResponse; +use crate::{network_response, Interactor, InteractorStep, StepBuffer}; use multiversx_sdk::data::transaction::Transaction; impl Interactor { @@ -16,7 +15,9 @@ impl Interactor { let results = self.process_txs(txs).await; for (i, sc_call_step) in buffer.refs.iter_mut().enumerate() { - sc_call_step.set_response(TxResponse::from_network_tx(results.get(i).unwrap().clone())); + sc_call_step.set_response(network_response::parse_tx_response( + results.get(i).unwrap().clone(), + )); } for step in buffer.refs.iter_mut() { diff --git a/framework/snippets/src/multi/interactor_multi_sc_process.rs b/framework/snippets/src/multi/interactor_multi_sc_process.rs index f19dcfb171..3364a4d5ee 100644 --- a/framework/snippets/src/multi/interactor_multi_sc_process.rs +++ b/framework/snippets/src/multi/interactor_multi_sc_process.rs @@ -31,7 +31,7 @@ impl Interactor { .expect("failed to send transaction"); println!("process tx hash: {tx_hash} with nonce: {}", tx.nonce); - futures.push(self.retrieve_tx_on_network(tx_hash.clone())); + futures.push(self.proxy.retrieve_tx_on_network(tx_hash.clone())); } join_all(futures).await diff --git a/framework/snippets/src/network_response.rs b/framework/snippets/src/network_response.rs new file mode 100644 index 0000000000..f7f0b962df --- /dev/null +++ b/framework/snippets/src/network_response.rs @@ -0,0 +1,222 @@ +use multiversx_sc_scenario::{ + imports::{Address, ESDTSystemSCAddress}, + multiversx_chain_vm::crypto_functions::keccak256, + scenario_model::{TxResponse, TxResponseStatus}, +}; +use multiversx_sdk::{ + data::transaction::{ApiSmartContractResult, Events, TransactionOnNetwork}, + utils::base64_decode, +}; + +const SC_DEPLOY_PROCESSING_TYPE: &str = "SCDeployment"; +const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError"; + +/// Creates a [`TxResponse`] from a [`TransactionOnNetwork`]. +pub fn parse_tx_response(tx: TransactionOnNetwork) -> TxResponse { + let mut response = TxResponse { + api_scrs: tx.smart_contract_results.unwrap_or_default(), + api_logs: tx.logs, + ..Default::default() + }; + + response.tx_error = process_signal_error(&response); + if !response.tx_error.is_success() { + return response; + } + + process( + &mut response, + tx.sender.to_bytes(), + tx.nonce, + tx.processing_type_on_destination, + ); + + response +} + +fn process_signal_error(tx_response: &TxResponse) -> TxResponseStatus { + if let Some(event) = find_log(tx_response, LOG_IDENTIFIER_SIGNAL_ERROR) { + let topics = event.topics.as_ref(); + if let Some(error) = process_topics_error(topics) { + return TxResponseStatus::signal_error(&error); + } + + let error_raw = base64_decode(topics.unwrap().get(1).unwrap()); + let error = String::from_utf8(error_raw).unwrap(); + return TxResponseStatus::signal_error(&error); + } + + TxResponseStatus::default() +} + +fn process( + tx_response: &mut TxResponse, + sender_address: [u8; 32], + nonce: u64, + processing_type_on_destination: String, +) { + process_out(tx_response); + process_new_deployed_address( + tx_response, + sender_address, + nonce, + processing_type_on_destination, + ); + process_new_issued_token_identifier(tx_response); +} + +fn process_out(tx_response: &mut TxResponse) { + let out_scr = tx_response.api_scrs.iter().find(is_out_scr); + + if let Some(out_scr) = out_scr { + tx_response.out = decode_scr_data_or_panic(&out_scr.data); + } else if let Some(data) = process_out_from_log(tx_response) { + tx_response.out = data + } +} + +fn process_out_from_log(tx_response: &TxResponse) -> Option>> { + if let Some(logs) = &tx_response.api_logs { + logs.events.iter().rev().find_map(|event| { + if event.identifier == "writeLog" { + if let Some(data) = &event.data { + let decoded_data = String::from_utf8(base64_decode(data)).unwrap(); + + if decoded_data.starts_with('@') { + let out = decode_scr_data_or_panic(decoded_data.as_str()); + return Some(out); + } + } + } + + None + }) + } else { + None + } +} + +fn process_new_deployed_address( + tx_response: &mut TxResponse, + sender_address_bytes: [u8; 32], + nonce: u64, + processing_type_on_destination: String, +) { + if processing_type_on_destination != SC_DEPLOY_PROCESSING_TYPE { + return; + } + + let sender_nonce_bytes = nonce.to_le_bytes(); + let mut bytes_to_hash: Vec = Vec::new(); + bytes_to_hash.extend_from_slice(&sender_address_bytes); + bytes_to_hash.extend_from_slice(&sender_nonce_bytes); + + let address_keccak = keccak256(&bytes_to_hash); + + let mut address = [0u8; 32]; + + address[0..8].copy_from_slice(&[0u8; 8]); + address[8..10].copy_from_slice(&[5, 0]); + address[10..30].copy_from_slice(&address_keccak[10..30]); + address[30..32].copy_from_slice(&sender_address_bytes[30..32]); + + tx_response.new_deployed_address = Some(Address::from(address)); +} + +fn process_new_issued_token_identifier(tx_response: &mut TxResponse) { + for scr in tx_response.api_scrs.iter() { + if scr.sender.to_bech32_string().unwrap() != ESDTSystemSCAddress.to_bech32_string() { + continue; + } + + let Some(prev_tx) = tx_response + .api_scrs + .iter() + .find(|e| e.hash == scr.prev_tx_hash) + else { + continue; + }; + + let is_issue_fungible = prev_tx.data.starts_with("issue@"); + let is_issue_semi_fungible = prev_tx.data.starts_with("issueSemiFungible@"); + let is_issue_non_fungible = prev_tx.data.starts_with("issueNonFungible@"); + let is_register_meta_esdt = prev_tx.data.starts_with("registerMetaESDT@"); + let is_register_and_set_all_roles_esdt = + prev_tx.data.starts_with("registerAndSetAllRoles@"); + + if !is_issue_fungible + && !is_issue_semi_fungible + && !is_issue_non_fungible + && !is_register_meta_esdt + && !is_register_and_set_all_roles_esdt + { + continue; + } + + if scr.data.starts_with("ESDTTransfer@") { + let encoded_tid = scr.data.split('@').nth(1); + if encoded_tid.is_none() { + return; + } + + tx_response.new_issued_token_identifier = + Some(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap()); + + break; + } else if scr.data.starts_with("@00@") { + let encoded_tid = scr.data.split('@').nth(2); + if encoded_tid.is_none() { + return; + } + + tx_response.new_issued_token_identifier = + Some(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap()); + + break; + } + } +} + +fn find_log<'a>(tx_response: &'a TxResponse, log_identifier: &str) -> Option<&'a Events> { + if let Some(logs) = &tx_response.api_logs { + logs.events + .iter() + .find(|event| event.identifier == log_identifier) + } else { + None + } +} + +/// Checks for invalid topics. +pub fn process_topics_error(topics: Option<&Vec>) -> Option { + if topics.is_none() { + return Some("missing topics".to_string()); + } + + let topics = topics.unwrap(); + if topics.len() != 2 { + Some(format!( + "expected to have 2 topics, found {} instead", + topics.len() + )) + } else { + None + } +} + +/// Decodes the data of a smart contract result. +pub fn decode_scr_data_or_panic(data: &str) -> Vec> { + let mut split = data.split('@'); + let _ = split.next().expect("SCR data should start with '@'"); + let result_code = split.next().expect("missing result code"); + assert_eq!(result_code, "6f6b", "result code is not 'ok'"); + + split + .map(|encoded_arg| hex::decode(encoded_arg).expect("error hex-decoding result")) + .collect() +} + +/// Checks if the given smart contract result is an out smart contract result. +pub fn is_out_scr(scr: &&ApiSmartContractResult) -> bool { + scr.nonce != 0 && scr.data.starts_with('@') +} diff --git a/framework/scenario/tests/test_tx_deployed_address.rs b/framework/snippets/tests/test_tx_deployed_address.rs similarity index 99% rename from framework/scenario/tests/test_tx_deployed_address.rs rename to framework/snippets/tests/test_tx_deployed_address.rs index b040ace618..377a0e779f 100644 --- a/framework/scenario/tests/test_tx_deployed_address.rs +++ b/framework/snippets/tests/test_tx_deployed_address.rs @@ -1,5 +1,5 @@ -use multiversx_sc::types::Address; -use multiversx_sc_scenario::scenario_model::TxResponse; +use multiversx_sc_scenario::imports::Address; +use multiversx_sc_snippets::network_response; use multiversx_sdk::data::transaction::{TransactionInfo, TransactionOnNetwork}; #[test] @@ -53,7 +53,7 @@ fn test_deployed_address() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let opt_address = tx_response.new_deployed_address.map(|e| { multiversx_sdk::data::address::Address::from_bytes(*e.as_array()) .to_bech32_string() @@ -124,7 +124,7 @@ fn test_deployed_address_should_be_none_if_not_a_sc_deployment_tx() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let opt_address = tx_response.new_deployed_address; let expected: Option
= None; diff --git a/framework/scenario/tests/test_tx_issued_token_identifier.rs b/framework/snippets/tests/test_tx_issued_token_identifier.rs similarity index 99% rename from framework/scenario/tests/test_tx_issued_token_identifier.rs rename to framework/snippets/tests/test_tx_issued_token_identifier.rs index 2d0ccffa84..3539c4e757 100644 --- a/framework/scenario/tests/test_tx_issued_token_identifier.rs +++ b/framework/snippets/tests/test_tx_issued_token_identifier.rs @@ -1,4 +1,4 @@ -use multiversx_sc_scenario::scenario_model::TxResponse; +use multiversx_sc_snippets::network_response; use multiversx_sdk::data::transaction::{TransactionInfo, TransactionOnNetwork}; #[test] @@ -203,7 +203,7 @@ fn test_process_issued_token_identifier_fungible() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Option = Some("EGLDMEX-95c6d5".to_string()); @@ -362,7 +362,7 @@ fn test_process_issued_token_identifier_semi_fungible() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Option = Some("DOPETEST-77200c".to_string()); @@ -618,7 +618,7 @@ fn test_process_issued_token_identifier_non_fungible() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Option = Some("GEN-868593".to_string()); @@ -919,7 +919,7 @@ fn test_process_issued_token_identifier_meta_esdt() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Option = Some("AVASH-7d8b5d".to_string()); @@ -1152,7 +1152,7 @@ fn test_set_special_roles_should_not_process_issued_token_identifier() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Option = None; @@ -1414,7 +1414,7 @@ fn test_multisig_issue_nft_and_set_all_roles() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected = Some("TESTCOLL1-5aa80c".to_string()); diff --git a/framework/scenario/tests/test_tx_multi_contract_sc_result.rs b/framework/snippets/tests/test_tx_multi_contract_sc_result.rs similarity index 98% rename from framework/scenario/tests/test_tx_multi_contract_sc_result.rs rename to framework/snippets/tests/test_tx_multi_contract_sc_result.rs index 8d45dfff0c..a9b64d8941 100644 --- a/framework/scenario/tests/test_tx_multi_contract_sc_result.rs +++ b/framework/snippets/tests/test_tx_multi_contract_sc_result.rs @@ -1,4 +1,4 @@ -use multiversx_sc_scenario::scenario_model::TxResponse; +use multiversx_sc_snippets::network_response; use multiversx_sdk::data::transaction::{TransactionInfo, TransactionOnNetwork}; #[test] @@ -79,7 +79,7 @@ fn test_with_multi_contract_same_shard_tx_that_has_no_sc_result() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Vec> = vec![ hex::decode("0a").unwrap(), @@ -211,7 +211,7 @@ fn test_with_multi_contract_cross_shard_tx_that_has_no_callback() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Vec> = vec![]; @@ -340,7 +340,7 @@ fn test_with_multi_contract_cross_shard_tx_that_has_non_returning_callback() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Vec> = vec![]; @@ -469,7 +469,7 @@ fn test_with_multi_contract_cross_shard_tx_that_has_returning_callback() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Vec> = vec![]; diff --git a/framework/scenario/tests/test_tx_multiple_sc_results.rs b/framework/snippets/tests/test_tx_multiple_sc_results.rs similarity index 98% rename from framework/scenario/tests/test_tx_multiple_sc_results.rs rename to framework/snippets/tests/test_tx_multiple_sc_results.rs index 6322b0a7bb..25e6fc477a 100644 --- a/framework/scenario/tests/test_tx_multiple_sc_results.rs +++ b/framework/snippets/tests/test_tx_multiple_sc_results.rs @@ -1,4 +1,4 @@ -use multiversx_sc_scenario::scenario_model::{is_out_scr, TxResponse}; +use multiversx_sc_snippets::network_response::{self, is_out_scr}; use multiversx_sdk::data::transaction::{TransactionInfo, TransactionOnNetwork}; #[test] @@ -281,7 +281,7 @@ fn test_transaction_multiple_sc_results() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); assert_eq!(tx_response.api_scrs.len(), 4usize); assert!(is_out_scr(&tx_response.api_scrs.get(2).unwrap())); } diff --git a/framework/scenario/tests/test_tx_sc_result.rs b/framework/snippets/tests/test_tx_sc_result.rs similarity index 98% rename from framework/scenario/tests/test_tx_sc_result.rs rename to framework/snippets/tests/test_tx_sc_result.rs index 561712300c..b3809eb2f8 100644 --- a/framework/scenario/tests/test_tx_sc_result.rs +++ b/framework/snippets/tests/test_tx_sc_result.rs @@ -1,4 +1,4 @@ -use multiversx_sc_scenario::scenario_model::TxResponse; +use multiversx_sc_snippets::network_response; use multiversx_sdk::data::transaction::{TransactionInfo, TransactionOnNetwork}; #[test] @@ -251,7 +251,7 @@ fn test_with_tx_that_has_sc_result() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Vec> = vec![ hex::decode("0000000c5745474c442d64376336626200000000000000000000000803856446ff9a304b") @@ -339,7 +339,7 @@ fn test_with_tx_that_has_no_sc_result() { .data .unwrap() .transaction; - let tx_response = TxResponse::from_network_tx(tx_on_network); + let tx_response = network_response::parse_tx_response(tx_on_network); let expected: Vec> = vec![ hex::decode("0a").unwrap(), diff --git a/sdk/core/Cargo.toml b/sdk/core/Cargo.toml index 319c81b137..c8bf3c75fb 100644 --- a/sdk/core/Cargo.toml +++ b/sdk/core/Cargo.toml @@ -34,3 +34,4 @@ zeroize = "1.4.2" bech32 = "0.9" itertools = "0.12.0" pem = "3.0.2" +log = "0.4.17" diff --git a/sdk/core/examples/account.rs b/sdk/core/examples/account.rs index 2ad1d6c4cc..5e9456f4b0 100644 --- a/sdk/core/examples/account.rs +++ b/sdk/core/examples/account.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::address::Address, + gateway::{GatewayProxy, DEVNET_GATEWAY}, }; #[tokio::main] @@ -10,7 +10,7 @@ async fn main() { ) .unwrap(); - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let account = blockchain.get_account(&addr).await.unwrap(); println!("account: {account:#?}"); diff --git a/sdk/core/examples/account_storage.rs b/sdk/core/examples/account_storage.rs index 31adc7d0f9..7693c26e08 100644 --- a/sdk/core/examples/account_storage.rs +++ b/sdk/core/examples/account_storage.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::address::Address, + gateway::{GatewayProxy, DEVNET_GATEWAY}, }; #[tokio::main] @@ -10,7 +10,7 @@ async fn main() { ) .unwrap(); - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let account_storage = blockchain.get_account_storage_keys(&addr).await.unwrap(); println!("Account Storage: {account_storage:#?}"); diff --git a/sdk/core/examples/get_esdt_tokens.rs b/sdk/core/examples/get_esdt_tokens.rs index faa4debfba..fac424da27 100644 --- a/sdk/core/examples/get_esdt_tokens.rs +++ b/sdk/core/examples/get_esdt_tokens.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::address::Address, + gateway::{GatewayProxy, DEVNET_GATEWAY}, }; #[tokio::main] @@ -10,7 +10,7 @@ async fn main() { ) .unwrap(); - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let balances = blockchain.get_account_esdt_tokens(&addr).await.unwrap(); println!("{balances:#?}"); diff --git a/sdk/core/examples/get_hyper_block_by_hash.rs b/sdk/core/examples/get_hyper_block_by_hash.rs index c67b479767..1d7f969ce1 100644 --- a/sdk/core/examples/get_hyper_block_by_hash.rs +++ b/sdk/core/examples/get_hyper_block_by_hash.rs @@ -1,8 +1,8 @@ -use multiversx_sdk::blockchain::{CommunicationProxy, DEVNET_GATEWAY}; +use multiversx_sdk::gateway::{GatewayProxy, DEVNET_GATEWAY}; #[tokio::main] async fn main() { - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let result = blockchain .get_hyper_block_by_hash("20b14ba0e68c465810c5ded091f220e51dad41629d7ccd87dab572206185e419") .await; diff --git a/sdk/core/examples/get_hyper_block_by_nonce.rs b/sdk/core/examples/get_hyper_block_by_nonce.rs index c3d270d2bc..575255d6c9 100644 --- a/sdk/core/examples/get_hyper_block_by_nonce.rs +++ b/sdk/core/examples/get_hyper_block_by_nonce.rs @@ -1,8 +1,8 @@ -use multiversx_sdk::blockchain::{CommunicationProxy, DEVNET_GATEWAY}; +use multiversx_sdk::gateway::{GatewayProxy, DEVNET_GATEWAY}; #[tokio::main] async fn main() { - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let result = blockchain.get_hyper_block_by_nonce(7468).await; println!("block by nonce result: {result:?}") diff --git a/sdk/core/examples/get_hyper_block_latest.rs b/sdk/core/examples/get_hyper_block_latest.rs index b9ad946829..934358fdbf 100644 --- a/sdk/core/examples/get_hyper_block_latest.rs +++ b/sdk/core/examples/get_hyper_block_latest.rs @@ -1,8 +1,8 @@ -use multiversx_sdk::blockchain::{CommunicationProxy, DEVNET_GATEWAY}; +use multiversx_sdk::gateway::{GatewayProxy, DEVNET_GATEWAY}; #[tokio::main] async fn main() { - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let result = blockchain.get_latest_hyper_block_nonce(false).await; println!("latest block result: {result:?}") diff --git a/sdk/core/examples/get_network_config.rs b/sdk/core/examples/get_network_config.rs index 7ca699b3c8..d53e0e7ce1 100644 --- a/sdk/core/examples/get_network_config.rs +++ b/sdk/core/examples/get_network_config.rs @@ -1,8 +1,8 @@ -use multiversx_sdk::blockchain::{CommunicationProxy, DEVNET_GATEWAY}; +use multiversx_sdk::gateway::{GatewayProxy, DEVNET_GATEWAY}; #[tokio::main] async fn main() { - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let network_config = blockchain.get_network_config().await.unwrap(); println!("network_config: {network_config:#?}") diff --git a/sdk/core/examples/get_network_economics.rs b/sdk/core/examples/get_network_economics.rs index e562f3db46..bb0410c8f0 100644 --- a/sdk/core/examples/get_network_economics.rs +++ b/sdk/core/examples/get_network_economics.rs @@ -1,8 +1,8 @@ -use multiversx_sdk::blockchain::{CommunicationProxy, DEVNET_GATEWAY}; +use multiversx_sdk::gateway::{GatewayProxy, DEVNET_GATEWAY}; #[tokio::main] async fn main() { - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let network_economics = blockchain.get_network_economics().await.unwrap(); println!("network_economics: {network_economics:#?}") diff --git a/sdk/core/examples/sign_tx.rs b/sdk/core/examples/sign_tx.rs index 1455061830..aa3f977321 100644 --- a/sdk/core/examples/sign_tx.rs +++ b/sdk/core/examples/sign_tx.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::transaction::Transaction, + gateway::{GatewayProxy, DEVNET_GATEWAY}, wallet::Wallet, }; @@ -11,7 +11,7 @@ async fn main() { ) .unwrap(); let addr = wl.address(); - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let network_config = blockchain.get_network_config().await.unwrap(); let arg = blockchain diff --git a/sdk/core/examples/sign_txs.rs b/sdk/core/examples/sign_txs.rs index 6a4a12a231..2860d528f7 100644 --- a/sdk/core/examples/sign_txs.rs +++ b/sdk/core/examples/sign_txs.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::transaction::Transaction, + gateway::{GatewayProxy, DEVNET_GATEWAY}, wallet::Wallet, }; @@ -11,7 +11,7 @@ async fn main() { ) .unwrap(); let addr = wl.address(); - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let network_config = blockchain.get_network_config().await.unwrap(); let arg = blockchain diff --git a/sdk/core/examples/tx_cost.rs b/sdk/core/examples/tx_cost.rs index b98cbd2e4b..a982b130ec 100644 --- a/sdk/core/examples/tx_cost.rs +++ b/sdk/core/examples/tx_cost.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::{address::Address, transaction::Transaction}, + gateway::{GatewayProxy, DEVNET_GATEWAY}, utils::base64_encode, }; @@ -26,7 +26,7 @@ async fn main() { signature: None, }; - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let cost = blockchain.request_transaction_cost(&tx).await.unwrap(); println!("tx cost: {cost:#?}"); diff --git a/sdk/core/examples/tx_default_args.rs b/sdk/core/examples/tx_default_args.rs index dad593eaf8..01b91aa5a6 100644 --- a/sdk/core/examples/tx_default_args.rs +++ b/sdk/core/examples/tx_default_args.rs @@ -1,11 +1,11 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::address::Address, + gateway::{GatewayProxy, DEVNET_GATEWAY}, }; #[tokio::main] async fn main() { - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let network_config = blockchain.get_network_config().await.unwrap(); let addr = Address::from_bech32_string( "erd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts", diff --git a/sdk/core/examples/tx_info.rs b/sdk/core/examples/tx_info.rs index 33c929be50..f7126daca9 100644 --- a/sdk/core/examples/tx_info.rs +++ b/sdk/core/examples/tx_info.rs @@ -1,9 +1,9 @@ -use multiversx_sdk::blockchain::{CommunicationProxy, DEVNET_GATEWAY}; +use multiversx_sdk::gateway::{GatewayProxy, DEVNET_GATEWAY}; #[tokio::main] async fn main() { let tx_hash = "49edb289892a655a0e988b360c19326c21107f9696c6197b435667c6e8c6e1a3"; - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let status = blockchain.get_transaction_status(tx_hash).await; println!("tx status: {status:?}"); diff --git a/sdk/core/examples/vm_query.rs b/sdk/core/examples/vm_query.rs index 41cdfd5dbf..d07f49b856 100644 --- a/sdk/core/examples/vm_query.rs +++ b/sdk/core/examples/vm_query.rs @@ -1,6 +1,6 @@ use multiversx_sdk::{ - blockchain::{CommunicationProxy, DEVNET_GATEWAY}, data::{address::Address, vm::VmValueRequest}, + gateway::{GatewayProxy, DEVNET_GATEWAY}, wallet::Wallet, }; @@ -11,7 +11,7 @@ async fn main() { ) .unwrap(); let addr = wl.address(); - let blockchain = CommunicationProxy::new(DEVNET_GATEWAY.to_string()); + let blockchain = GatewayProxy::new(DEVNET_GATEWAY.to_string()); let req = VmValueRequest { sc_address: Address::from_bech32_string( "erd1qqqqqqqqqqqqqpgqhn3ae8dpc957t7jadn7kywtg503dy7pnj9ts3umqxx", diff --git a/sdk/core/src/blockchain.rs b/sdk/core/src/blockchain.rs deleted file mode 100644 index 73d97ef0ea..0000000000 --- a/sdk/core/src/blockchain.rs +++ /dev/null @@ -1,405 +0,0 @@ -use std::collections::HashMap; - -use crate::data::{ - account::{Account, AccountResponse}, - account_storage::AccountStorageResponse, - address::Address, - esdt::{EsdtBalance, EsdtBalanceResponse, EsdtRolesResponse}, - hyperblock::{HyperBlock, HyperBlockResponse}, - network_config::{NetworkConfig, NetworkConfigResponse}, - network_economics::{NetworkEconomics, NetworkEconomicsResponse}, - network_status::NetworkStatusResponse, - transaction::{ - ArgCreateTransaction, ResponseTxCost, SendTransactionResponse, SendTransactionsResponse, - Transaction, TransactionInfo, TransactionOnNetwork, TransactionStatus, TxCostResponseData, - }, - vm::{ResponseVmValue, VmValueRequest, VmValuesResponseData}, -}; -use anyhow::{anyhow, Result}; -use itertools::Itertools; -use reqwest::Client; - -pub const MAINNET_GATEWAY: &str = "https://gateway.multiversx.com"; -pub const TESTNET_GATEWAY: &str = "https://testnet-gateway.multiversx.com"; -pub const DEVNET_GATEWAY: &str = "https://devnet-gateway.multiversx.com"; - -// MetachainShardId will be used to identify a shard ID as metachain -pub const METACHAIN_SHARD_ID: u32 = 0xFFFFFFFF; - -const NETWORK_CONFIG_ENDPOINT: &str = "network/config"; -const NETWORK_ECONOMICS_ENDPOINT: &str = "network/economics"; -const ACCOUNT_ENDPOINT: &str = "address/"; -const KEYS_ENDPOINT: &str = "/keys/"; -const COST_TRANSACTION_ENDPOINT: &str = "transaction/cost"; -const SEND_TRANSACTION_ENDPOINT: &str = "transaction/send"; -const SEND_MULTIPLE_TRANSACTIONS_ENDPOINT: &str = "transaction/send-multiple"; -const GET_TRANSACTION_INFO_ENDPOINT: &str = "transaction/"; -const GET_HYPER_BLOCK_BY_NONCE_ENDPOINT: &str = "hyperblock/by-nonce/"; -const GET_HYPER_BLOCK_BY_HASH_ENDPOINT: &str = "hyperblock/by-hash/"; -const GET_NETWORK_STATUS_ENDPOINT: &str = "network/status"; -const WITH_RESULTS_QUERY_PARAM: &str = "?withResults=true"; -const VM_VALUES_ENDPOINT: &str = "vm-values/query"; - -#[derive(Clone, Debug)] -pub struct CommunicationProxy { - proxy_url: String, - client: Client, -} - -impl CommunicationProxy { - pub fn new(proxy_url: String) -> Self { - Self { - proxy_url, - client: Client::new(), - } - } - - fn get_endpoint(&self, endpoint: &str) -> String { - format!("{}/{}", self.proxy_url, endpoint) - } - - // get_network_config retrieves the network configuration from the proxy - pub async fn get_network_config(&self) -> Result { - let endpoint = self.get_endpoint(NETWORK_CONFIG_ENDPOINT); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.config), - } - } - - // get_network_economics retrieves the network economics from the proxy - pub async fn get_network_economics(&self) -> Result { - let endpoint = self.get_endpoint(NETWORK_ECONOMICS_ENDPOINT); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.metrics), - } - } - - async fn get_hyper_block(&self, endpoint: &str) -> Result { - let endpoint = self.get_endpoint(endpoint); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.hyperblock), - } - } - - // get_hyper_block_by_hash retrieves a hyper block's info by hash from the network - pub async fn get_hyper_block_by_hash(&self, hash: &str) -> Result { - let endpoint = GET_HYPER_BLOCK_BY_HASH_ENDPOINT.to_string() + hash; - self.get_hyper_block(endpoint.as_str()).await - } - - // get_hyper_block_by_nonce retrieves a hyper block's info by nonce from the network - pub async fn get_hyper_block_by_nonce(&self, nonce: u64) -> Result { - let endpoint = GET_HYPER_BLOCK_BY_NONCE_ENDPOINT.to_string() + nonce.to_string().as_str(); - self.get_hyper_block(endpoint.as_str()).await - } - - // get_latest_hyper_block_nonce retrieves the latest hyper block (metachain) nonce from the network - pub async fn get_latest_hyper_block_nonce(&self, with_metachain: bool) -> Result { - let mut endpoint = GET_NETWORK_STATUS_ENDPOINT.to_string(); - - if with_metachain { - endpoint = format!("{GET_NETWORK_STATUS_ENDPOINT}/{METACHAIN_SHARD_ID}"); - } - - let endpoint = self.get_endpoint(endpoint.as_str()); - - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.status.nonce), - } - } - - // request_transaction_cost retrieves how many gas a transaction will consume - pub async fn request_transaction_cost(&self, tx: &Transaction) -> Result { - let endpoint = self.get_endpoint(COST_TRANSACTION_ENDPOINT); - let resp = self - .client - .post(endpoint) - .json(tx) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b), - } - } - - // get_account retrieves an account info from the network (nonce, balance) - pub async fn get_account(&self, address: &Address) -> Result { - if !address.is_valid() { - return Err(anyhow!("invalid address")); - } - - let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str(); - let endpoint = self.get_endpoint(endpoint.as_str()); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.account), - } - } - - // get_account_esdt_roles retrieves an all esdt roles of an account from the network - pub async fn get_account_esdt_roles( - &self, - address: &Address, - ) -> Result>> { - if !address.is_valid() { - return Err(anyhow!("invalid address")); - } - - let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str() + "/esdts/roles"; - let endpoint = self.get_endpoint(endpoint.as_str()); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.roles), - } - } - - // get_account_esdt_tokens retrieves an all esdt token of an account from the network - pub async fn get_account_esdt_tokens( - &self, - address: &Address, - ) -> Result> { - if !address.is_valid() { - return Err(anyhow!("invalid address")); - } - - let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str() + "/esdt"; - let endpoint = self.get_endpoint(endpoint.as_str()); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.esdts), - } - } - - // get_account_esdt_tokens retrieves an all esdt token of an account from the network - pub async fn get_account_storage_keys( - &self, - address: &Address, - ) -> Result> { - if !address.is_valid() { - return Err(anyhow!("invalid address")); - } - - let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str() + KEYS_ENDPOINT; - let endpoint = self.get_endpoint(endpoint.as_str()); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.pairs), - } - } - - async fn get_transaction_info_internal( - &self, - hash: &str, - with_results: bool, - ) -> Result { - let mut endpoint = GET_TRANSACTION_INFO_ENDPOINT.to_string() + hash; - - if with_results { - endpoint += WITH_RESULTS_QUERY_PARAM - } - - let endpoint = self.get_endpoint(endpoint.as_str()); - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.transaction), - } - } - - // get_transaction_info retrieves a transaction's details from the network - pub async fn get_transaction_info(&self, hash: &str) -> Result { - self.get_transaction_info_internal(hash, false).await - } - - // get_transaction_info_with_results retrieves a transaction's details from the network with events - pub async fn get_transaction_info_with_results( - &self, - hash: &str, - ) -> Result { - self.get_transaction_info_internal(hash, true).await - } - - // get_transaction_status retrieves a transaction's status from the network - pub async fn get_transaction_status(&self, hash: &str) -> Result { - let endpoint = format!("transaction/{hash}/status"); - let endpoint = self.get_endpoint(endpoint.as_str()); - - let resp = self - .client - .get(endpoint) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.status), - } - } - - // get_default_transaction_arguments will prepare the transaction creation argument by querying the account's info - pub async fn get_default_transaction_arguments( - &self, - address: &Address, - network_configs: &NetworkConfig, - ) -> Result { - let account = self.get_account(address).await?; - - Ok(ArgCreateTransaction { - nonce: account.nonce, - value: "".to_string(), - rcv_addr: address.clone(), - snd_addr: address.clone(), - gas_price: network_configs.min_gas_price, - gas_limit: network_configs.min_gas_limit, - data: None, - signature: "".to_string(), - chain_id: network_configs.chain_id.clone(), - version: network_configs.min_transaction_version, - options: 0, - available_balance: account.balance, - }) - } - - pub async fn send_transaction(&self, tx: &Transaction) -> Result { - let endpoint = self.get_endpoint(SEND_TRANSACTION_ENDPOINT); - let resp = self - .client - .post(endpoint) - .json(tx) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b.tx_hash), - } - } - - pub async fn send_transactions(&self, txs: &Vec) -> Result> { - let endpoint = self.get_endpoint(SEND_MULTIPLE_TRANSACTIONS_ENDPOINT); - let resp = self - .client - .post(endpoint) - .json(txs) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => { - let mut tx_hashs: Vec = vec![]; - for key in b.txs_hashes.keys().sorted() { - tx_hashs.push(b.txs_hashes[key].clone()); - } - - Ok(tx_hashs) - }, - } - } - - // execute_vmquery retrieves data from existing SC trie through the use of a VM - pub async fn execute_vmquery( - &self, - vm_request: &VmValueRequest, - ) -> Result { - let endpoint = self.get_endpoint(VM_VALUES_ENDPOINT); - let resp = self - .client - .post(endpoint) - .json(vm_request) - .send() - .await? - .json::() - .await?; - - match resp.data { - None => Err(anyhow!("{}", resp.error)), - Some(b) => Ok(b), - } - } -} diff --git a/sdk/core/src/gateway.rs b/sdk/core/src/gateway.rs new file mode 100644 index 0000000000..35e2668b50 --- /dev/null +++ b/sdk/core/src/gateway.rs @@ -0,0 +1,15 @@ +mod gateway_account; +mod gateway_block; +mod gateway_network; +mod gateway_proxy; +mod gateway_tx; +mod gateway_tx_retrieve; + +pub use gateway_proxy::GatewayProxy; + +pub const MAINNET_GATEWAY: &str = "https://gateway.multiversx.com"; +pub const TESTNET_GATEWAY: &str = "https://testnet-gateway.multiversx.com"; +pub const DEVNET_GATEWAY: &str = "https://devnet-gateway.multiversx.com"; + +// MetachainShardId will be used to identify a shard ID as metachain +pub const METACHAIN_SHARD_ID: u32 = 0xFFFFFFFF; diff --git a/sdk/core/src/gateway/gateway_account.rs b/sdk/core/src/gateway/gateway_account.rs new file mode 100644 index 0000000000..7d72258544 --- /dev/null +++ b/sdk/core/src/gateway/gateway_account.rs @@ -0,0 +1,112 @@ +use crate::data::{ + account::{Account, AccountResponse}, + account_storage::AccountStorageResponse, + address::Address, + esdt::{EsdtBalance, EsdtBalanceResponse, EsdtRolesResponse}, +}; +use anyhow::{anyhow, Result}; +use std::collections::HashMap; + +use super::GatewayProxy; + +const ACCOUNT_ENDPOINT: &str = "address/"; +const KEYS_ENDPOINT: &str = "/keys/"; + +impl GatewayProxy { + // get_account retrieves an account info from the network (nonce, balance) + pub async fn get_account(&self, address: &Address) -> Result { + if !address.is_valid() { + return Err(anyhow!("invalid address")); + } + + let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str(); + let endpoint = self.get_endpoint(endpoint.as_str()); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.account), + } + } + + // get_account_esdt_roles retrieves an all esdt roles of an account from the network + pub async fn get_account_esdt_roles( + &self, + address: &Address, + ) -> Result>> { + if !address.is_valid() { + return Err(anyhow!("invalid address")); + } + + let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str() + "/esdts/roles"; + let endpoint = self.get_endpoint(endpoint.as_str()); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.roles), + } + } + + // get_account_esdt_tokens retrieves an all esdt token of an account from the network + pub async fn get_account_esdt_tokens( + &self, + address: &Address, + ) -> Result> { + if !address.is_valid() { + return Err(anyhow!("invalid address")); + } + + let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str() + "/esdt"; + let endpoint = self.get_endpoint(endpoint.as_str()); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.esdts), + } + } + + // get_account_esdt_tokens retrieves an all esdt token of an account from the network + pub async fn get_account_storage_keys( + &self, + address: &Address, + ) -> Result> { + if !address.is_valid() { + return Err(anyhow!("invalid address")); + } + + let endpoint = ACCOUNT_ENDPOINT.to_string() + address.to_string().as_str() + KEYS_ENDPOINT; + let endpoint = self.get_endpoint(endpoint.as_str()); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.pairs), + } + } +} diff --git a/sdk/core/src/gateway/gateway_block.rs b/sdk/core/src/gateway/gateway_block.rs new file mode 100644 index 0000000000..b6daf57abb --- /dev/null +++ b/sdk/core/src/gateway/gateway_block.rs @@ -0,0 +1,66 @@ +use crate::data::{ + hyperblock::{HyperBlock, HyperBlockResponse}, + network_status::NetworkStatusResponse, +}; +use anyhow::{anyhow, Result}; + +use super::GatewayProxy; +use super::METACHAIN_SHARD_ID; + +const GET_HYPER_BLOCK_BY_NONCE_ENDPOINT: &str = "hyperblock/by-nonce/"; +const GET_HYPER_BLOCK_BY_HASH_ENDPOINT: &str = "hyperblock/by-hash/"; +const GET_NETWORK_STATUS_ENDPOINT: &str = "network/status"; + +impl GatewayProxy { + async fn get_hyper_block(&self, endpoint: &str) -> Result { + let endpoint = self.get_endpoint(endpoint); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.hyperblock), + } + } + + // get_hyper_block_by_hash retrieves a hyper block's info by hash from the network + pub async fn get_hyper_block_by_hash(&self, hash: &str) -> Result { + let endpoint = GET_HYPER_BLOCK_BY_HASH_ENDPOINT.to_string() + hash; + self.get_hyper_block(endpoint.as_str()).await + } + + // get_hyper_block_by_nonce retrieves a hyper block's info by nonce from the network + pub async fn get_hyper_block_by_nonce(&self, nonce: u64) -> Result { + let endpoint = GET_HYPER_BLOCK_BY_NONCE_ENDPOINT.to_string() + nonce.to_string().as_str(); + self.get_hyper_block(endpoint.as_str()).await + } + + // get_latest_hyper_block_nonce retrieves the latest hyper block (metachain) nonce from the network + pub async fn get_latest_hyper_block_nonce(&self, with_metachain: bool) -> Result { + let mut endpoint = GET_NETWORK_STATUS_ENDPOINT.to_string(); + + if with_metachain { + endpoint = format!("{GET_NETWORK_STATUS_ENDPOINT}/{METACHAIN_SHARD_ID}"); + } + + let endpoint = self.get_endpoint(endpoint.as_str()); + + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.status.nonce), + } + } +} diff --git a/sdk/core/src/gateway/gateway_network.rs b/sdk/core/src/gateway/gateway_network.rs new file mode 100644 index 0000000000..8b02a57d0d --- /dev/null +++ b/sdk/core/src/gateway/gateway_network.rs @@ -0,0 +1,46 @@ +use crate::data::{ + network_config::{NetworkConfig, NetworkConfigResponse}, + network_economics::{NetworkEconomics, NetworkEconomicsResponse}, +}; +use anyhow::{anyhow, Result}; + +use super::GatewayProxy; + +const NETWORK_CONFIG_ENDPOINT: &str = "network/config"; +const NETWORK_ECONOMICS_ENDPOINT: &str = "network/economics"; + +impl GatewayProxy { + // get_network_config retrieves the network configuration from the proxy + pub async fn get_network_config(&self) -> Result { + let endpoint = self.get_endpoint(NETWORK_CONFIG_ENDPOINT); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.config), + } + } + + // get_network_economics retrieves the network economics from the proxy + pub async fn get_network_economics(&self) -> Result { + let endpoint = self.get_endpoint(NETWORK_ECONOMICS_ENDPOINT); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.metrics), + } + } +} diff --git a/sdk/core/src/gateway/gateway_proxy.rs b/sdk/core/src/gateway/gateway_proxy.rs new file mode 100644 index 0000000000..47925cd79f --- /dev/null +++ b/sdk/core/src/gateway/gateway_proxy.rs @@ -0,0 +1,21 @@ +use reqwest::Client; + +/// Allows communication with the MultiversX gateway API. +#[derive(Clone, Debug)] +pub struct GatewayProxy { + pub(crate) proxy_url: String, + pub(crate) client: Client, +} + +impl GatewayProxy { + pub fn new(proxy_url: String) -> Self { + Self { + proxy_url, + client: Client::new(), + } + } + + pub(crate) fn get_endpoint(&self, endpoint: &str) -> String { + format!("{}/{}", self.proxy_url, endpoint) + } +} diff --git a/sdk/core/src/gateway/gateway_tx.rs b/sdk/core/src/gateway/gateway_tx.rs new file mode 100644 index 0000000000..47aa8b5a83 --- /dev/null +++ b/sdk/core/src/gateway/gateway_tx.rs @@ -0,0 +1,184 @@ +use crate::data::{ + address::Address, + network_config::NetworkConfig, + transaction::{ + ArgCreateTransaction, ResponseTxCost, SendTransactionResponse, SendTransactionsResponse, + Transaction, TransactionInfo, TransactionOnNetwork, TransactionStatus, TxCostResponseData, + }, + vm::{ResponseVmValue, VmValueRequest, VmValuesResponseData}, +}; +use anyhow::{anyhow, Result}; +use itertools::Itertools; + +use super::GatewayProxy; + +const COST_TRANSACTION_ENDPOINT: &str = "transaction/cost"; +const SEND_TRANSACTION_ENDPOINT: &str = "transaction/send"; +const SEND_MULTIPLE_TRANSACTIONS_ENDPOINT: &str = "transaction/send-multiple"; +const GET_TRANSACTION_INFO_ENDPOINT: &str = "transaction/"; +const WITH_RESULTS_QUERY_PARAM: &str = "?withResults=true"; +const VM_VALUES_ENDPOINT: &str = "vm-values/query"; + +impl GatewayProxy { + // request_transaction_cost retrieves how many gas a transaction will consume + pub async fn request_transaction_cost(&self, tx: &Transaction) -> Result { + let endpoint = self.get_endpoint(COST_TRANSACTION_ENDPOINT); + let resp = self + .client + .post(endpoint) + .json(tx) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b), + } + } + + async fn get_transaction_info_internal( + &self, + hash: &str, + with_results: bool, + ) -> Result { + let mut endpoint = GET_TRANSACTION_INFO_ENDPOINT.to_string() + hash; + + if with_results { + endpoint += WITH_RESULTS_QUERY_PARAM + } + + let endpoint = self.get_endpoint(endpoint.as_str()); + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.transaction), + } + } + + // get_transaction_info retrieves a transaction's details from the network + pub async fn get_transaction_info(&self, hash: &str) -> Result { + self.get_transaction_info_internal(hash, false).await + } + + // get_transaction_info_with_results retrieves a transaction's details from the network with events + pub async fn get_transaction_info_with_results( + &self, + hash: &str, + ) -> Result { + self.get_transaction_info_internal(hash, true).await + } + + // get_transaction_status retrieves a transaction's status from the network + pub async fn get_transaction_status(&self, hash: &str) -> Result { + let endpoint = format!("transaction/{hash}/status"); + let endpoint = self.get_endpoint(endpoint.as_str()); + + let resp = self + .client + .get(endpoint) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.status), + } + } + + // get_default_transaction_arguments will prepare the transaction creation argument by querying the account's info + pub async fn get_default_transaction_arguments( + &self, + address: &Address, + network_configs: &NetworkConfig, + ) -> Result { + let account = self.get_account(address).await?; + + Ok(ArgCreateTransaction { + nonce: account.nonce, + value: "".to_string(), + rcv_addr: address.clone(), + snd_addr: address.clone(), + gas_price: network_configs.min_gas_price, + gas_limit: network_configs.min_gas_limit, + data: None, + signature: "".to_string(), + chain_id: network_configs.chain_id.clone(), + version: network_configs.min_transaction_version, + options: 0, + available_balance: account.balance, + }) + } + + pub async fn send_transaction(&self, tx: &Transaction) -> Result { + let endpoint = self.get_endpoint(SEND_TRANSACTION_ENDPOINT); + let resp = self + .client + .post(endpoint) + .json(tx) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b.tx_hash), + } + } + + pub async fn send_transactions(&self, txs: &Vec) -> Result> { + let endpoint = self.get_endpoint(SEND_MULTIPLE_TRANSACTIONS_ENDPOINT); + let resp = self + .client + .post(endpoint) + .json(txs) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => { + let mut tx_hashs: Vec = vec![]; + for key in b.txs_hashes.keys().sorted() { + tx_hashs.push(b.txs_hashes[key].clone()); + } + + Ok(tx_hashs) + }, + } + } + + // execute_vmquery retrieves data from existing SC trie through the use of a VM + pub async fn execute_vmquery( + &self, + vm_request: &VmValueRequest, + ) -> Result { + let endpoint = self.get_endpoint(VM_VALUES_ENDPOINT); + let resp = self + .client + .post(endpoint) + .json(vm_request) + .send() + .await? + .json::() + .await?; + + match resp.data { + None => Err(anyhow!("{}", resp.error)), + Some(b) => Ok(b), + } + } +} diff --git a/framework/snippets/src/interactor_retrieve.rs b/sdk/core/src/gateway/gateway_tx_retrieve.rs similarity index 87% rename from framework/snippets/src/interactor_retrieve.rs rename to sdk/core/src/gateway/gateway_tx_retrieve.rs index cd9291ba33..821713f972 100644 --- a/framework/snippets/src/interactor_retrieve.rs +++ b/sdk/core/src/gateway/gateway_tx_retrieve.rs @@ -1,28 +1,28 @@ -use crate::Interactor; +use crate::data::transaction::TransactionOnNetwork; use log::info; -use multiversx_sdk::data::transaction::TransactionOnNetwork; use std::time::{Duration, Instant}; +use super::GatewayProxy; + const INITIAL_BACKOFF_DELAY: f32 = 1.4; const MAX_RETRIES: usize = 8; const MAX_BACKOFF_DELAY: Duration = Duration::from_secs(6); -impl Interactor { +impl GatewayProxy { /// Retrieves a transaction from the network. - pub(crate) async fn retrieve_tx_on_network(&self, tx_hash: String) -> TransactionOnNetwork { + pub async fn retrieve_tx_on_network(&self, tx_hash: String) -> TransactionOnNetwork { let mut retries = 0; let mut backoff_delay = Duration::from_secs_f32(INITIAL_BACKOFF_DELAY); let start_time = Instant::now(); loop { - match self.proxy.get_transaction_status(&tx_hash).await { + match self.get_transaction_status(&tx_hash).await { Ok(status) => { // checks if transaction status is final match status.as_str() { "success" | "fail" => { // retrieve transaction info with results let transaction_info_with_results = self - .proxy .get_transaction_info_with_results(&tx_hash) .await .unwrap(); diff --git a/sdk/core/src/lib.rs b/sdk/core/src/lib.rs index f37f2197e5..3a1db99fd7 100644 --- a/sdk/core/src/lib.rs +++ b/sdk/core/src/lib.rs @@ -1,5 +1,5 @@ -pub mod blockchain; pub mod crypto; pub mod data; +pub mod gateway; pub mod utils; pub mod wallet;