diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 83d3e609ca..8a8a6c22fd 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -41,7 +41,7 @@ async fn main() { #[allow(unused)] struct AdderInteract { interactor: Interactor, - wallet_address: Address, + wallet_address: Bech32Address, adder_code: BytesValue, state: State, } @@ -61,17 +61,17 @@ impl AdderInteract { Self { interactor, - wallet_address, + wallet_address: wallet_address.into(), adder_code, state: State::load_state(), } } async fn set_state(&mut self) { - println!("wallet address: {}", bech32::encode(&self.wallet_address)); + println!("wallet address: {}", &self.wallet_address); let scenario_raw = retrieve_account_as_scenario_set_state( Config::load_config().gateway().to_string(), - bech32::encode(&self.wallet_address), + self.wallet_address.to_bech32_string(), true, ) .await; @@ -92,16 +92,13 @@ impl AdderInteract { .typed(adder_proxy::AdderProxy) .init(0u32) .code(&self.adder_code) - .returns(ReturnsNewAddress) + .returns(ReturnsNewBech32Address) .prepare_async() .run() .await; - let new_address_bech32 = bech32::encode(&new_address); - println!("new address: {new_address_bech32}"); - - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_adder_address(&new_address_expr); + println!("new address: {new_address}"); + self.state.set_adder_address(new_address); } async fn multi_deploy(&mut self, count: &u8) { @@ -121,17 +118,15 @@ impl AdderInteract { .init(0u32) .code(&self.adder_code) .gas(NumExpr("70,000,000")) - .returns(ReturnsNewAddress) + .returns(ReturnsNewBech32Address) }); } let results = buffer.run().await; - for result in results { - let new_address_bech32 = bech32::encode(&result); - println!("new address: {new_address_bech32}"); + for new_address in results { + println!("new address: {new_address}"); - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_adder_address(&new_address_expr); + self.state.set_adder_address(new_address); } } @@ -139,7 +134,7 @@ impl AdderInteract { self.interactor .tx() .from(&self.wallet_address) - .to(self.state.adder().to_address()) + .to(self.state.current_adder_address()) .egld(NumExpr("0,050000000000000000")) .prepare_async() .run() @@ -150,7 +145,7 @@ impl AdderInteract { self.interactor .tx() .from(&self.wallet_address) - .to(self.state.adder().to_address()) + .to(self.state.current_adder_address()) .typed(adder_proxy::AdderProxy) .add(value) .prepare_async() @@ -164,7 +159,7 @@ impl AdderInteract { let sum = self .interactor .query() - .to(self.state.adder().to_address()) + .to(self.state.current_adder_address()) .typed(adder_proxy::AdderProxy) .sum() .returns(ReturnsResultConv::::new()) diff --git a/contracts/examples/adder/interact/src/basic_interact_state.rs b/contracts/examples/adder/interact/src/basic_interact_state.rs index 7fe09dc0ed..41453e36fd 100644 --- a/contracts/examples/adder/interact/src/basic_interact_state.rs +++ b/contracts/examples/adder/interact/src/basic_interact_state.rs @@ -1,4 +1,4 @@ -use crate::{ContractInfo, StaticApi}; +use multiversx_sc_snippets::imports::*; use serde::{Deserialize, Serialize}; use std::{ io::{Read, Write}, @@ -8,12 +8,10 @@ use std::{ /// State file const STATE_FILE: &str = "state.toml"; -pub type AdderContract = ContractInfo>; - /// Multisig Interact state #[derive(Debug, Default, Serialize, Deserialize)] pub struct State { - adder_address: Option, + adder_address: Option, } impl State { @@ -30,17 +28,15 @@ impl State { } /// Sets the adder address - pub fn set_adder_address(&mut self, address: &str) { - self.adder_address = Some(String::from(address)); + pub fn set_adder_address(&mut self, address: Bech32Address) { + self.adder_address = Some(address); } /// Returns the adder contract - pub fn adder(&self) -> AdderContract { - AdderContract::new( - self.adder_address - .clone() - .expect("no known adder contract, deploy first"), - ) + pub fn current_adder_address(&self) -> &Bech32Address { + self.adder_address + .as_ref() + .expect("no known adder contract, deploy first") } } diff --git a/contracts/examples/multisig/interact/src/multisig_interact.rs b/contracts/examples/multisig/interact/src/multisig_interact.rs index 9148af2e86..48cfb05a6d 100644 --- a/contracts/examples/multisig/interact/src/multisig_interact.rs +++ b/contracts/examples/multisig/interact/src/multisig_interact.rs @@ -11,7 +11,6 @@ use multisig_interact_state::State; use multiversx_sc_snippets::imports::*; -const SYSTEM_SC_BECH32: &str = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; const INTERACTOR_SCENARIO_TRACE_PATH: &str = "interactor_trace.scen.json"; #[tokio::main] @@ -76,8 +75,7 @@ async fn main() { struct MultisigInteract { interactor: Interactor, - wallet_address: Address, - system_sc_address: Address, + wallet_address: Bech32Address, collection_token_identifier: String, multisig_code: BytesValue, state: State, @@ -98,8 +96,7 @@ impl MultisigInteract { Self { interactor, - wallet_address, - system_sc_address: bech32::decode(SYSTEM_SC_BECH32), + wallet_address: wallet_address.into(), collection_token_identifier: String::new(), multisig_code, state: State::load_state(), @@ -152,16 +149,14 @@ impl MultisigInteract { .init(quorum, board) .code(&self.multisig_code) .gas(NumExpr("100,000,000")) - .returns(ReturnsNewAddress) + .returns(ReturnsNewBech32Address) .prepare_async() .run() .await; - let new_address_bech32 = bech32::encode(&new_address); - println!("new address: {new_address_bech32}"); + println!("new address: {new_address}"); - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_multisig_address(&new_address_expr); + self.state.set_multisig_address(new_address); } async fn multi_deploy(&mut self, count: &u8) { @@ -182,17 +177,14 @@ impl MultisigInteract { .init(quorum, board.clone()) .code(&self.multisig_code) .gas(NumExpr("70,000,000")) - .returns(ReturnsNewAddress) + .returns(ReturnsNewBech32Address) }); } let results = buffer.run().await; - for result in results { - let new_address_bech32 = bech32::encode(&result); - println!("new address: {new_address_bech32}"); - - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_multisig_address(&new_address_expr); + for new_address in results { + println!("new address: {new_address}"); + self.state.set_multisig_address(new_address); } } @@ -202,7 +194,7 @@ impl MultisigInteract { let eve = test_wallets::eve(); MultiValueVec::from([ - self.wallet_address.clone(), + self.wallet_address.to_address(), carol.address().to_bytes().into(), dan.address().to_bytes().into(), eve.address().to_bytes().into(), @@ -213,7 +205,7 @@ impl MultisigInteract { self.interactor .tx() .from(&self.wallet_address) - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .egld(BigUint::from(50_000_000_000_000_000u64)) // 0,05 or 5 * 10^16 .prepare_async() .run() @@ -229,7 +221,7 @@ impl MultisigInteract { self.interactor .tx() .from(&self.wallet_address) - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(gas_expr) .typed(multisig_proxy::MultisigProxy) .perform_action_endpoint(action_id) @@ -241,8 +233,6 @@ impl MultisigInteract { } async fn perform_actions(&mut self, action_ids: Vec, gas_expr: u64) { - let multisig_address = self.state.multisig().to_address(); - let mut actions_no_quorum_reached = Vec::new(); for &action_id in &action_ids { if self.quorum_reached(action_id).await { @@ -256,10 +246,11 @@ impl MultisigInteract { let from = &self.wallet_address; let mut buffer = self.interactor.homogenous_call_buffer(); + let multisig_address = self.state.current_multisig_address(); for action_id in action_ids { buffer.push_tx(|tx| { tx.from(from) - .to(&multisig_address) + .to(multisig_address) .gas(gas_expr) .typed(multisig_proxy::MultisigProxy) .perform_action_endpoint(action_id) @@ -283,7 +274,7 @@ impl MultisigInteract { async fn quorum_reached(&mut self, action_id: usize) -> bool { self.interactor .query() - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .typed(multisig_proxy::MultisigProxy) .quorum_reached(action_id) .returns(ReturnsResult) @@ -295,7 +286,7 @@ impl MultisigInteract { async fn signed(&mut self, signer: &Address, action_id: usize) -> bool { self.interactor .query() - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .typed(multisig_proxy::MultisigProxy) .signed(signer, action_id) .returns(ReturnsResult) @@ -306,7 +297,6 @@ impl MultisigInteract { async fn sign(&mut self, action_ids: &[usize]) { println!("signing actions `{action_ids:?}`..."); - let multisig_address = self.state.multisig().to_address(); let mut pending_signers = Vec::<(Address, usize)>::new(); for &action_id in action_ids { @@ -323,10 +313,11 @@ impl MultisigInteract { } let mut buffer = self.interactor.homogenous_call_buffer(); + let multisig_address = self.state.current_multisig_address(); for (signer, action_id) in pending_signers { buffer.push_tx(|tx| { tx.from(signer) - .to(&multisig_address) + .to(multisig_address) .gas(15_000_000u64) .typed(multisig_proxy::MultisigProxy) .sign(action_id) @@ -350,7 +341,7 @@ impl MultisigInteract { self.interactor .tx() .from(&self.wallet_address) - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("30,000,000")) .typed(multisig_proxy::MultisigProxy) .dns_register(dns_address, name) @@ -365,7 +356,7 @@ impl MultisigInteract { let quorum = self .interactor .query() - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .typed(multisig_proxy::MultisigProxy) .quorum() .returns(ReturnsResult) @@ -380,7 +371,7 @@ impl MultisigInteract { let board = self .interactor .query() - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .typed(multisig_proxy::MultisigProxy) .num_board_members() .returns(ReturnsResult) diff --git a/contracts/examples/multisig/interact/src/multisig_interact_nfts.rs b/contracts/examples/multisig/interact/src/multisig_interact_nfts.rs index e7fb0104af..2efda8e4d6 100644 --- a/contracts/examples/multisig/interact/src/multisig_interact_nfts.rs +++ b/contracts/examples/multisig/interact/src/multisig_interact_nfts.rs @@ -33,16 +33,15 @@ impl MultisigInteract { } pub async fn propose_issue_collection_with_all_roles(&mut self) -> usize { - let system_sc_address = bech32::decode(SYSTEM_SC_BECH32); let action_id = self .interactor .tx() .from(&self.wallet_address) - .to(self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("10,000,000")) .typed(multisig_proxy::MultisigProxy) .propose_async_call( - system_sc_address, + ESDTSystemSCAddress, ISSUE_COST, FunctionCall::new("registerAndSetAllRoles") .argument(&COLLECTION_NAME) @@ -71,7 +70,7 @@ impl MultisigInteract { .interactor .tx() .from(&self.wallet_address) - .to(&self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("80,000,000")) .typed(multisig_proxy::MultisigProxy) .perform_action_endpoint(action_id) @@ -88,16 +87,15 @@ impl MultisigInteract { } pub async fn propose_issue_collection(&mut self) -> usize { - let system_sc_address = bech32::decode(SYSTEM_SC_BECH32); let action_id = self .interactor .tx() .from(&self.wallet_address) - .to(&self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("10,000,000")) .typed(multisig_proxy::MultisigProxy) .propose_async_call( - system_sc_address, + ESDTSystemSCAddress, ISSUE_COST, FunctionCall::new("issueNonFungible") .argument(&COLLECTION_NAME) @@ -124,7 +122,7 @@ impl MultisigInteract { .interactor .tx() .from(&self.wallet_address) - .to(&self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("80,000,000")) .typed(multisig_proxy::MultisigProxy) .perform_action_endpoint(action_id) @@ -141,20 +139,20 @@ impl MultisigInteract { } pub async fn propose_set_special_role(&mut self) -> usize { - let multisig_address = self.state.multisig().to_address(); + let multisig_address = self.state.current_multisig_address(); let action_id = self .interactor .tx() .from(&self.wallet_address) - .to(&self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("10,000,000")) .typed(multisig_proxy::MultisigProxy) .propose_async_call( - &self.system_sc_address, + ESDTSystemSCAddress, 0u64, FunctionCall::new("setSpecialRole") .argument(&self.collection_token_identifier) - .argument(&multisig_address) + .argument(multisig_address) .argument(&"ESDTRoleNFTCreate"), ) .returns(ReturnsResult) @@ -177,9 +175,8 @@ impl MultisigInteract { pub async fn create_items(&mut self) { println!("creating items..."); - let multisig_address = self.state.multisig().to_address(); - let mut buffer = self.interactor.homogenous_call_buffer(); + let multisig_address = self.state.current_multisig_address(); for item_index in 0..NUM_ITEMS { let item_name = format!("Test collection item #{item_index}"); let image_cid = format!( @@ -188,11 +185,11 @@ impl MultisigInteract { buffer.push_tx(|tx| { tx.from(&self.wallet_address) - .to(&multisig_address) + .to(multisig_address) .gas(10_000_000u64) .typed(multisig_proxy::MultisigProxy) .propose_async_call( - &multisig_address, + multisig_address, 0u64, FunctionCall::new("ESDTNFTCreate") .argument(&self.collection_token_identifier) diff --git a/contracts/examples/multisig/interact/src/multisig_interact_state.rs b/contracts/examples/multisig/interact/src/multisig_interact_state.rs index f3c97482df..d5ff8bb7c1 100644 --- a/contracts/examples/multisig/interact/src/multisig_interact_state.rs +++ b/contracts/examples/multisig/interact/src/multisig_interact_state.rs @@ -1,4 +1,4 @@ -use crate::{ContractInfo, StaticApi}; +use multiversx_sc_scenario::imports::Bech32Address; use serde::{Deserialize, Serialize}; use std::{ io::{Read, Write}, @@ -8,12 +8,10 @@ use std::{ /// State file const STATE_FILE: &str = "state.toml"; -pub type MultisigContract = ContractInfo>; - /// Multisig Interact state #[derive(Debug, Default, Serialize, Deserialize)] pub struct State { - multisig_address: Option, + multisig_address: Option, } impl State { @@ -30,13 +28,14 @@ impl State { } /// Sets the multisig address - pub fn set_multisig_address(&mut self, address: &str) { - self.multisig_address = Some(String::from(address)); + pub fn set_multisig_address(&mut self, address: Bech32Address) { + self.multisig_address = Some(address); } - /// Returns the multisig contract - pub fn multisig(&self) -> MultisigContract { - MultisigContract::new(self.multisig_address.clone().unwrap()) + pub fn current_multisig_address(&self) -> &Bech32Address { + self.multisig_address + .as_ref() + .expect("no known multisig contract, deploy first") } } diff --git a/contracts/examples/multisig/interact/src/multisig_interact_wegld.rs b/contracts/examples/multisig/interact/src/multisig_interact_wegld.rs index fbfdd6f4a6..c9156e991d 100644 --- a/contracts/examples/multisig/interact/src/multisig_interact_wegld.rs +++ b/contracts/examples/multisig/interact/src/multisig_interact_wegld.rs @@ -53,7 +53,7 @@ impl MultisigInteract { .interactor .tx() .from(&self.wallet_address) - .to(&self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("10,000,000")) .typed(multisig_proxy::MultisigProxy) .propose_async_call( @@ -83,7 +83,7 @@ impl MultisigInteract { .interactor .tx() .from(&self.wallet_address) - .to(&self.state.multisig().to_address()) + .to(self.state.current_multisig_address()) .gas(NumExpr("10,000,000")) .typed(multisig_proxy::MultisigProxy) .propose_async_call(to, 0u64, function_call) diff --git a/contracts/feature-tests/rust-snippets-generator-test/interact-rs/src/interactor_main.rs b/contracts/feature-tests/rust-snippets-generator-test/interact-rs/src/interactor_main.rs index 92d00a227d..389c717f2c 100644 --- a/contracts/feature-tests/rust-snippets-generator-test/interact-rs/src/interactor_main.rs +++ b/contracts/feature-tests/rust-snippets-generator-test/interact-rs/src/interactor_main.rs @@ -8,7 +8,6 @@ const GATEWAY: &str = sdk::blockchain::DEVNET_GATEWAY; const PEM: &str = "alice.pem"; const SC_ADDRESS: &str = ""; -const SYSTEM_SC_BECH32: &str = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; const DEFAULT_ADDRESS_EXPR: &str = "0x0000000000000000000000000000000000000000000000000000000000000000"; const TOKEN_ISSUE_COST: u64 = 50_000_000_000_000_000; diff --git a/framework/base/src/types/interaction/markers/esdt_system_sc_address.rs b/framework/base/src/types/interaction/markers/esdt_system_sc_address.rs index de8aadb1b6..f9d1f94ce2 100644 --- a/framework/base/src/types/interaction/markers/esdt_system_sc_address.rs +++ b/framework/base/src/types/interaction/markers/esdt_system_sc_address.rs @@ -1,4 +1,5 @@ use hex_literal::hex; +use multiversx_sc_codec::{CodecFrom, EncodeErrorHandler, TopEncode, TopEncodeOutput}; use crate::{ api::{ @@ -14,6 +15,8 @@ use crate::{ /// Address of the system smart contract that manages ESDT. const SYSTEM_SC_ADDRESS_BYTES: [u8; 32] = hex!("000000000000000000010000000000000000000000000000000000000002ffff"); +const SYSTEM_SC_ADDRESS_BECH32: &str = + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; const SYSTEM_SC_ADDRESS_ANNOTATION: &str = "bech32:erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; @@ -21,12 +24,20 @@ const SYSTEM_SC_ADDRESS_ANNOTATION: &str = pub struct ESDTSystemSCAddress; impl ESDTSystemSCAddress { - pub fn managed_address(self) -> ManagedAddress + pub fn to_managed_address(self) -> ManagedAddress where Api: ManagedTypeApi, { ManagedAddress::from(SYSTEM_SC_ADDRESS_BYTES) } + + pub fn to_bech32_str(&self) -> &str { + SYSTEM_SC_ADDRESS_BECH32 + } + + pub fn to_bech32_string(&self) -> alloc::string::String { + SYSTEM_SC_ADDRESS_BECH32.into() + } } impl AnnotatedValue, ManagedAddress> for ESDTSystemSCAddress @@ -38,9 +49,27 @@ where } fn to_value(&self, _env: &TxScEnv) -> ManagedAddress { - ESDTSystemSCAddress.managed_address() + ESDTSystemSCAddress.to_managed_address() } } impl TxTo> for ESDTSystemSCAddress where Api: CallTypeApi {} impl TxToSpecified> for ESDTSystemSCAddress where Api: CallTypeApi {} + +impl TopEncode for ESDTSystemSCAddress { + fn top_encode_or_handle_err(&self, output: O, h: H) -> Result<(), H::HandledErr> + where + O: TopEncodeOutput, + H: EncodeErrorHandler, + { + SYSTEM_SC_ADDRESS_BYTES.top_encode_or_handle_err(output, h) + } +} + +impl CodecFrom for ManagedAddress where M: ManagedTypeApi {} + +impl core::fmt::Display for ESDTSystemSCAddress { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(SYSTEM_SC_ADDRESS_BECH32) + } +} diff --git a/framework/base/src/types/interaction/system_proxy/legacy_system_sc_proxy.rs b/framework/base/src/types/interaction/system_proxy/legacy_system_sc_proxy.rs index 35486c6c04..c19fe2e4e6 100644 --- a/framework/base/src/types/interaction/system_proxy/legacy_system_sc_proxy.rs +++ b/framework/base/src/types/interaction/system_proxy/legacy_system_sc_proxy.rs @@ -471,7 +471,7 @@ where } pub fn esdt_system_sc_address(&self) -> ManagedAddress { - ESDTSystemSCAddress.managed_address() + ESDTSystemSCAddress.to_managed_address() } fn esdt_system_sc_call_no_args( diff --git a/framework/scenario/src/bech32.rs b/framework/scenario/src/bech32.rs index 4b72107bb8..aa224cee95 100644 --- a/framework/scenario/src/bech32.rs +++ b/framework/scenario/src/bech32.rs @@ -2,7 +2,8 @@ use bech32::{FromBase32, ToBase32, Variant}; use multiversx_sc::types::heap::Address; pub fn decode(bech32_address: &str) -> Address { - let (_, dest_address_bytes_u5, _) = bech32::decode(bech32_address).unwrap(); + let (_, dest_address_bytes_u5, _) = bech32::decode(bech32_address) + .unwrap_or_else(|err| panic!("bech32 decode error for {bech32_address}: {err}")); let dest_address_bytes = Vec::::from_base32(&dest_address_bytes_u5).unwrap(); if dest_address_bytes.len() != 32 { panic!("Invalid address length after decoding") diff --git a/framework/scenario/src/facade/expr.rs b/framework/scenario/src/facade/expr.rs index 37750ee973..6ea8595740 100644 --- a/framework/scenario/src/facade/expr.rs +++ b/framework/scenario/src/facade/expr.rs @@ -1,7 +1,9 @@ +mod bech32_address; mod file_expr; mod mxsc_expr; mod num_expr; +pub use bech32_address::Bech32Address; pub use file_expr::FileExpr; pub use mxsc_expr::MxscExpr; pub use num_expr::NumExpr; diff --git a/framework/scenario/src/facade/expr/bech32_address.rs b/framework/scenario/src/facade/expr/bech32_address.rs new file mode 100644 index 0000000000..a327e44a6b --- /dev/null +++ b/framework/scenario/src/facade/expr/bech32_address.rs @@ -0,0 +1,190 @@ +use std::fmt::Display; + +use crate::bech32; +use multiversx_sc::{ + api::ManagedTypeApi, + codec::*, + types::{ + Address, AnnotatedValue, ManagedAddress, ManagedBuffer, TxEnv, TxFrom, TxFromSpecified, + TxTo, TxToSpecified, + }, +}; +use serde::{Deserialize, Serialize}; + +const BECH32_PREFIX: &str = "bech32:"; + +/// Wraps and address, and presents it as a bech32 expression wherever possible. +/// +/// In order to avoid repeated conversions, it redundantly keeps the bech32 representation inside. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Bech32Address { + address: Address, + bech32: String, +} + +impl From
for Bech32Address { + fn from(value: Address) -> Self { + let bech32 = bech32::encode(&value); + Bech32Address { + address: value, + bech32, + } + } +} + +impl Bech32Address { + pub fn from_bech32_string(bech32: String) -> Self { + let address = bech32::decode(&bech32); + Bech32Address { address, bech32 } + } + + pub fn to_bech32_str(&self) -> &str { + &self.bech32 + } + + pub fn to_bech32_string(&self) -> String { + self.bech32.to_owned() + } + + pub fn as_address(&self) -> &Address { + &self.address + } + + pub fn to_address(&self) -> Address { + self.address.clone() + } + + pub fn into_address(self) -> Address { + self.address + } + + pub fn to_bech32_expr(&self) -> String { + format!("{BECH32_PREFIX}{}", &self.bech32) + } +} + +impl AnnotatedValue> for Bech32Address +where + Env: TxEnv, +{ + fn annotation(&self, _env: &Env) -> ManagedBuffer { + self.to_bech32_expr().into() + } + + fn to_value(&self, env: &Env) -> ManagedAddress { + self.address.to_value(env) + } +} + +impl TxFrom for Bech32Address +where + Env: TxEnv, +{ + fn resolve_address(&self, env: &Env) -> ManagedAddress { + self.address.resolve_address(env) + } +} +impl TxFromSpecified for Bech32Address where Env: TxEnv {} +impl TxTo for Bech32Address where Env: TxEnv {} +impl TxToSpecified for Bech32Address where Env: TxEnv {} + +impl AnnotatedValue> for &Bech32Address +where + Env: TxEnv, +{ + fn annotation(&self, _env: &Env) -> ManagedBuffer { + self.to_bech32_expr().into() + } + + fn to_value(&self, env: &Env) -> ManagedAddress { + self.address.to_value(env) + } +} + +impl TxFrom for &Bech32Address +where + Env: TxEnv, +{ + fn resolve_address(&self, env: &Env) -> ManagedAddress { + self.address.resolve_address(env) + } +} +impl TxFromSpecified for &Bech32Address where Env: TxEnv {} +impl TxTo for &Bech32Address where Env: TxEnv {} +impl TxToSpecified for &Bech32Address where Env: TxEnv {} + +impl Display for Bech32Address { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.bech32) + } +} + +impl NestedEncode for Bech32Address { + fn dep_encode_or_handle_err(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr> + where + O: NestedEncodeOutput, + H: EncodeErrorHandler, + { + self.address.dep_encode_or_handle_err(dest, h) + } +} + +impl TopEncode for Bech32Address { + fn top_encode_or_handle_err(&self, output: O, h: H) -> Result<(), H::HandledErr> + where + O: TopEncodeOutput, + H: EncodeErrorHandler, + { + self.address.top_encode_or_handle_err(output, h) + } +} + +impl NestedDecode for Bech32Address { + fn dep_decode_or_handle_err(input: &mut I, h: H) -> Result + where + I: NestedDecodeInput, + H: DecodeErrorHandler, + { + Ok(Bech32Address::from(Address::dep_decode_or_handle_err( + input, h, + )?)) + } +} + +impl TopDecode for Bech32Address { + fn top_decode_or_handle_err(input: I, h: H) -> Result + where + I: TopDecodeInput, + H: DecodeErrorHandler, + { + Ok(Bech32Address::from(Address::top_decode_or_handle_err( + input, h, + )?)) + } +} + +impl CodecFrom for ManagedAddress where M: ManagedTypeApi {} +impl CodecFrom<&Bech32Address> for ManagedAddress where M: ManagedTypeApi {} + +impl Serialize for Bech32Address { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.bech32.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Bech32Address { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + // some old interactors have it serialized like this + let mut bech32 = String::deserialize(deserializer)?; + if let Some(stripped) = bech32.strip_prefix("bech32:") { + bech32 = stripped.to_owned(); + } + Ok(Bech32Address::from_bech32_string(bech32)) + } +} diff --git a/framework/scenario/src/facade/result_handlers.rs b/framework/scenario/src/facade/result_handlers.rs index f8b43a5ebd..30823bf6f5 100644 --- a/framework/scenario/src/facade/result_handlers.rs +++ b/framework/scenario/src/facade/result_handlers.rs @@ -1,6 +1,7 @@ mod expect_message; mod expect_status; mod returns_message; +mod returns_new_bech32_address; mod returns_new_token_identifier; mod returns_status; mod with_tx_raw_response; @@ -8,6 +9,7 @@ mod with_tx_raw_response; pub use expect_message::ExpectMessage; pub use expect_status::ExpectStatus; pub use returns_message::ReturnsMessage; +pub use returns_new_bech32_address::ReturnsNewBech32Address; pub use returns_new_token_identifier::ReturnsNewTokenIdentifier; pub use returns_status::ReturnsStatus; pub use with_tx_raw_response::WithRawTxResponse; diff --git a/framework/scenario/src/facade/result_handlers/returns_new_bech32_address.rs b/framework/scenario/src/facade/result_handlers/returns_new_bech32_address.rs new file mode 100644 index 0000000000..6e898c6088 --- /dev/null +++ b/framework/scenario/src/facade/result_handlers/returns_new_bech32_address.rs @@ -0,0 +1,27 @@ +use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; + +use crate::{facade::expr::Bech32Address, scenario_model::TxResponse}; + +/// Indicates that the newly deployed address will be returned after a deploy. +pub struct ReturnsNewBech32Address; + +impl RHListItem for ReturnsNewBech32Address +where + Env: TxEnv, +{ + type Returns = Bech32Address; +} + +impl RHListItemExec for ReturnsNewBech32Address +where + Env: TxEnv, +{ + fn item_process_result(self, tx_response: &TxResponse) -> Self::Returns { + let new_address = tx_response + .new_deployed_address + .clone() + .expect("missing returned address"); + + new_address.into() + } +} diff --git a/framework/scenario/src/scenario/model/transaction/tx_response.rs b/framework/scenario/src/scenario/model/transaction/tx_response.rs index ee844865bc..d4171c6590 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_response.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_response.rs @@ -1,5 +1,5 @@ use multiversx_chain_vm::{crypto_functions::keccak256, tx_mock::TxResult}; -use multiversx_sc::types::Address; +use multiversx_sc::types::{Address, ESDTSystemSCAddress}; use multiversx_sdk::{ data::transaction::{ApiLogs, ApiSmartContractResult, Events, TransactionOnNetwork}, utils::base64_decode, @@ -12,8 +12,6 @@ use super::{ const SC_DEPLOY_PROCESSING_TYPE: &str = "SCDeployment"; const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError"; -const SYSTEM_SC_BECH32: &str = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"; - #[derive(Debug, Default, Clone)] /// The response of a transaction. pub struct TxResponse { @@ -196,7 +194,7 @@ impl TxResponse { fn process_new_issued_token_identifier(mut self) -> Self { for scr in self.api_scrs.iter() { - if scr.sender.to_string() != SYSTEM_SC_BECH32 { + if scr.sender.to_bech32_string().unwrap() != ESDTSystemSCAddress.to_bech32_string() { continue; } diff --git a/framework/scenario/src/scenario/model/value/address_key.rs b/framework/scenario/src/scenario/model/value/address_key.rs index 889cca69ae..d705439b1d 100644 --- a/framework/scenario/src/scenario/model/value/address_key.rs +++ b/framework/scenario/src/scenario/model/value/address_key.rs @@ -1,8 +1,8 @@ -use multiversx_sc::types::{AddressExpr, ScExpr}; +use multiversx_sc::types::{Address, AddressExpr, ScExpr}; use super::{value_from_slice, AddressValue}; use crate::{ - multiversx_sc::types::Address, + facade::expr::Bech32Address, scenario_format::{ interpret_trait::{InterpretableFrom, InterpreterContext, IntoRaw}, value_interpreter::interpret_string, @@ -117,6 +117,24 @@ impl From<&Address> for AddressKey { } } +impl From<&Bech32Address> for AddressKey { + fn from(from: &Bech32Address) -> Self { + AddressKey { + value: from.to_address().clone(), + original: from.to_bech32_expr(), + } + } +} + +impl From for AddressKey { + fn from(from: Bech32Address) -> Self { + AddressKey { + original: from.to_bech32_expr(), + value: from.into_address(), + } + } +} + impl From for AddressKey { fn from(from: AddressExpr) -> Self { AddressKey { diff --git a/framework/scenario/src/scenario/model/value/address_value.rs b/framework/scenario/src/scenario/model/value/address_value.rs index 85886e4cf8..640451a63c 100644 --- a/framework/scenario/src/scenario/model/value/address_value.rs +++ b/framework/scenario/src/scenario/model/value/address_value.rs @@ -2,10 +2,13 @@ use std::fmt; use crate::multiversx_sc::types::Address; -use crate::scenario_format::{ - interpret_trait::{InterpretableFrom, InterpreterContext, IntoRaw}, - serde_raw::ValueSubTree, - value_interpreter::{interpret_string, interpret_subtree}, +use crate::{ + facade::expr::Bech32Address, + scenario_format::{ + interpret_trait::{InterpretableFrom, InterpreterContext, IntoRaw}, + serde_raw::ValueSubTree, + value_interpreter::{interpret_string, interpret_subtree}, + }, }; use super::AddressKey; @@ -107,6 +110,24 @@ impl From<&Address> for AddressValue { } } +impl From<&Bech32Address> for AddressValue { + fn from(from: &Bech32Address) -> Self { + AddressValue { + value: from.to_address().clone(), + original: ValueSubTree::Str(from.to_bech32_expr()), + } + } +} + +impl From for AddressValue { + fn from(from: Bech32Address) -> Self { + AddressValue { + original: ValueSubTree::Str(from.to_bech32_expr()), + value: from.into_address(), + } + } +} + impl From<&str> for AddressValue { fn from(from: &str) -> Self { AddressValue::interpret_from(from, &InterpreterContext::default()) diff --git a/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs b/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs index 63e8ae80db..87d17dff7f 100644 --- a/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs +++ b/framework/snippets/src/interactor_scenario/interactor_sc_deploy.rs @@ -1,7 +1,7 @@ use crate::{mandos_to_erdrs_address, Interactor}; use log::info; use multiversx_sc_scenario::{ - bech32, + imports::Bech32Address, mandos_system::ScenarioRunner, scenario_model::{ScDeployStep, SetStateStep, TxResponse}, }; @@ -64,14 +64,11 @@ impl Interactor { .new_deployed_address .clone() .unwrap(); + let deploy_address_bech32 = Bech32Address::from(deploy_address); - let set_state_step = SetStateStep::new().new_address( - addr, - nonce, - format!("0x{}", hex::encode(&deploy_address)).as_str(), - ); + let set_state_step = SetStateStep::new().new_address(addr, nonce, &deploy_address_bech32); - println!("deploy address: {}", bech32::encode(&deploy_address)); + println!("deploy address: {deploy_address_bech32}"); self.pre_runners.run_set_state_step(&set_state_step); self.post_runners.run_set_state_step(&set_state_step); diff --git a/tools/rust-debugger/format-tests/src/format_tests.rs b/tools/rust-debugger/format-tests/src/format_tests.rs index 913851dbfa..5df781b92b 100644 --- a/tools/rust-debugger/format-tests/src/format_tests.rs +++ b/tools/rust-debugger/format-tests/src/format_tests.rs @@ -65,7 +65,7 @@ fn main() { let token_identifier: TokenIdentifier = TokenIdentifier::from("MYTOK-123456"); push!(to_check, token_identifier, "\"MYTOK-123456\""); - let managed_address = ESDTSystemSCAddress.managed_address::(); + let managed_address = ESDTSystemSCAddress.to_managed_address::(); push!( to_check, managed_address,