diff --git a/applications/tari_collectibles/src-tauri/src/commands/asset_wallets/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/asset_wallets/mod.rs index 0963975423..e7ae013688 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/asset_wallets/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/asset_wallets/mod.rs @@ -45,6 +45,62 @@ pub(crate) async fn asset_wallets_create( asset_public_key: String, state: tauri::State<'_, ConcurrentAppState>, app: tauri::AppHandle, +) -> Result<(), Status> { + inner_asset_wallets_create(asset_public_key, state.inner(), app).await +} + +#[tauri::command] +pub(crate) async fn asset_wallets_get_balance( + asset_public_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_asset_wallets_get_balance(asset_public_key, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn asset_wallets_get_unspent_amounts( + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_asset_wallets_get_unspent_amounts(state.inner()).await +} + +#[tauri::command] +pub(crate) async fn asset_wallets_list( + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_asset_wallets_list(state.inner()).await +} + +#[tauri::command] +pub(crate) async fn asset_wallets_create_address( + asset_public_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_asset_wallets_create_address(asset_public_key, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn asset_wallets_get_latest_address( + asset_public_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_asset_wallets_get_latest_address(asset_public_key, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn asset_wallets_send_to( + asset_public_key: String, + amount: u64, + to_address: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result<(), Status> { + inner_asset_wallets_send_to(asset_public_key, amount, to_address, state.inner()).await +} + +pub(crate) async fn inner_asset_wallets_create( + asset_public_key: String, + state: &ConcurrentAppState, + app: tauri::AppHandle, ) -> Result<(), Status> { let wallet_id = state .current_wallet_id() @@ -119,10 +175,9 @@ pub(crate) async fn asset_wallets_create( Ok(()) } -#[tauri::command] -pub(crate) async fn asset_wallets_get_balance( +pub(crate) async fn inner_asset_wallets_get_balance( asset_public_key: String, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result { debug!( target: LOG_TARGET, @@ -175,9 +230,8 @@ pub(crate) async fn asset_wallets_get_balance( Ok(total) } -#[tauri::command] -pub(crate) async fn asset_wallets_get_unspent_amounts( - state: tauri::State<'_, ConcurrentAppState>, +pub(crate) async fn inner_asset_wallets_get_unspent_amounts( + state: &ConcurrentAppState, ) -> Result, Status> { let mut client = state.create_wallet_client().await; client.connect().await?; @@ -185,9 +239,8 @@ pub(crate) async fn asset_wallets_get_unspent_amounts( Ok(result.amount) } -#[tauri::command] -pub(crate) async fn asset_wallets_list( - state: tauri::State<'_, ConcurrentAppState>, +pub(crate) async fn inner_asset_wallets_list( + state: &ConcurrentAppState, ) -> Result, Status> { let wallet_id = state .current_wallet_id() @@ -202,10 +255,9 @@ pub(crate) async fn asset_wallets_list( Ok(result) } -#[tauri::command] -pub(crate) async fn asset_wallets_create_address( +pub(crate) async fn inner_asset_wallets_create_address( asset_public_key: String, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result { let wallet_id = state .current_wallet_id() @@ -241,10 +293,9 @@ pub(crate) async fn asset_wallets_create_address( Ok(address) } -#[tauri::command] -pub(crate) async fn asset_wallets_get_latest_address( +pub(crate) async fn inner_asset_wallets_get_latest_address( asset_public_key: String, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result { let wallet_id = state .current_wallet_id() @@ -266,12 +317,11 @@ pub(crate) async fn asset_wallets_get_latest_address( ) } -#[tauri::command] -pub(crate) async fn asset_wallets_send_to( +pub(crate) async fn inner_asset_wallets_send_to( asset_public_key: String, amount: u64, to_address: String, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result<(), Status> { let wallet_id = state .current_wallet_id() diff --git a/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs index c7c000e0e8..8b0e574283 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/assets/mod.rs @@ -53,6 +53,103 @@ pub(crate) async fn assets_create( template_ids: Vec, template_parameters: Vec, state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_assets_create( + name, + description, + image, + template_ids, + template_parameters, + state.inner(), + ) + .await +} + +#[tauri::command] +pub(crate) async fn assets_list_owned( + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_assets_list_owned(state.inner()).await +} + +// TODO: remove and use better serializer +#[derive(Debug)] +struct AssetMetadata { + name: String, + description: String, + image: String, +} + +trait AssetMetadataDeserializer { + fn deserialize(&self, metadata: &[u8]) -> AssetMetadata; +} +trait AssetMetadataSerializer { + fn serialize(&self, model: &AssetMetadata) -> Vec; +} + +struct V1AssetMetadataSerializer {} + +impl AssetMetadataDeserializer for V1AssetMetadataSerializer { + fn deserialize(&self, metadata: &[u8]) -> AssetMetadata { + let m = String::from_utf8(Vec::from(metadata)).unwrap(); + let mut m = m + .as_str() + .split('|') + .map(|s| s.to_string()) + .collect::>() + .into_iter(); + let name = m.next(); + let description = m.next(); + let image = m.next(); + + AssetMetadata { + name: name.unwrap_or_else(|| "".to_string()), + description: description.unwrap_or_else(|| "".to_string()), + image: image.unwrap_or_else(|| "".to_string()), + } + } +} + +impl AssetMetadataSerializer for V1AssetMetadataSerializer { + fn serialize(&self, model: &AssetMetadata) -> Vec { + let str = format!("{}|{}|{}", model.name, model.description, model.image); + + str.into_bytes() + } +} + +#[tauri::command] +pub(crate) async fn assets_list_registered_assets( + offset: u64, + count: u64, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_assets_list_registered_assets(offset, count, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn assets_create_initial_checkpoint( + asset_pub_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result<(), Status> { + inner_assets_create_initial_checkpoint(asset_pub_key, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn assets_get_registration( + asset_pub_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_assets_get_registration(asset_pub_key, state.inner()).await +} + +pub(crate) async fn inner_assets_create( + name: String, + description: String, + image: String, + template_ids: Vec, + template_parameters: Vec, + state: &ConcurrentAppState, ) -> Result { let wallet_id = state .current_wallet_id() @@ -134,9 +231,8 @@ pub(crate) async fn assets_create( Ok(res) } -#[tauri::command] -pub(crate) async fn assets_list_owned( - state: tauri::State<'_, ConcurrentAppState>, +pub(crate) async fn inner_assets_list_owned( + state: &ConcurrentAppState, ) -> Result, Status> { let mut client = state.create_wallet_client().await; client.connect().await?; @@ -155,57 +251,10 @@ pub(crate) async fn assets_list_owned( ) } -// TODO: remove and use better serializer -#[derive(Debug)] -struct AssetMetadata { - name: String, - description: String, - image: String, -} - -trait AssetMetadataDeserializer { - fn deserialize(&self, metadata: &[u8]) -> AssetMetadata; -} -trait AssetMetadataSerializer { - fn serialize(&self, model: &AssetMetadata) -> Vec; -} - -struct V1AssetMetadataSerializer {} - -impl AssetMetadataDeserializer for V1AssetMetadataSerializer { - fn deserialize(&self, metadata: &[u8]) -> AssetMetadata { - let m = String::from_utf8(Vec::from(metadata)).unwrap(); - let mut m = m - .as_str() - .split('|') - .map(|s| s.to_string()) - .collect::>() - .into_iter(); - let name = m.next(); - let description = m.next(); - let image = m.next(); - - AssetMetadata { - name: name.unwrap_or_else(|| "".to_string()), - description: description.unwrap_or_else(|| "".to_string()), - image: image.unwrap_or_else(|| "".to_string()), - } - } -} - -impl AssetMetadataSerializer for V1AssetMetadataSerializer { - fn serialize(&self, model: &AssetMetadata) -> Vec { - let str = format!("{}|{}|{}", model.name, model.description, model.image); - - str.into_bytes() - } -} - -#[tauri::command] -pub(crate) async fn assets_list_registered_assets( +pub(crate) async fn inner_assets_list_registered_assets( offset: u64, count: u64, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result, Status> { let mut client = state.connect_base_node_client().await?; let assets = client.list_registered_assets(offset, count).await?; @@ -235,10 +284,9 @@ pub(crate) async fn assets_list_registered_assets( .collect() } -#[tauri::command] -pub(crate) async fn assets_create_initial_checkpoint( - asset_public_key: String, - state: tauri::State<'_, ConcurrentAppState>, +pub(crate) async fn inner_assets_create_initial_checkpoint( + asset_pub_key: String, + state: &ConcurrentAppState, ) -> Result<(), Status> { let mmr = MerkleMountainRange::::new(MemBackendVec::new()); @@ -251,7 +299,7 @@ pub(crate) async fn assets_create_initial_checkpoint( // create asset reg checkpoint client - .create_initial_asset_checkpoint(&asset_public_key, merkle_root) + .create_initial_asset_checkpoint(&asset_pub_key, merkle_root) .await?; Ok(()) @@ -274,10 +322,9 @@ pub(crate) async fn assets_create_committee_definition( Ok(()) } -#[tauri::command] -pub(crate) async fn assets_get_registration( +pub(crate) async fn inner_assets_get_registration( asset_pub_key: String, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result { let mut client = state.connect_base_node_client().await?; let asset_pub_key = PublicKey::from_hex(&asset_pub_key)?; diff --git a/applications/tari_collectibles/src-tauri/src/commands/keys/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/keys/mod.rs index dafe42ec5b..b6e2c2d6c8 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/keys/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/keys/mod.rs @@ -31,6 +31,12 @@ use tari_common_types::types::PublicKey; #[tauri::command] pub(crate) async fn next_asset_public_key( state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_next_asset_public_key(state.inner()).await +} + +pub(crate) async fn inner_next_asset_public_key( + state: &ConcurrentAppState, ) -> Result { let wallet_id = state .current_wallet_id() diff --git a/applications/tari_collectibles/src-tauri/src/commands/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/mod.rs index b8a3f11edc..acf370dae8 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/mod.rs @@ -31,6 +31,10 @@ pub mod wallets; #[tauri::command] pub async fn create_db(state: tauri::State<'_, ConcurrentAppState>) -> Result<(), String> { + inner_create_db(state.inner()).await +} + +pub async fn inner_create_db(state: &ConcurrentAppState) -> Result<(), String> { let _db = state .create_db() .await diff --git a/applications/tari_collectibles/src-tauri/src/commands/tip004/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/tip004/mod.rs index 4f36ed68b9..b7312e07ad 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/tip004/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/tip004/mod.rs @@ -43,6 +43,22 @@ pub(crate) async fn tip004_mint_token( asset_public_key: String, token: String, state: tauri::State<'_, ConcurrentAppState>, +) -> Result<(), Status> { + inner_tip004_mint_token(asset_public_key, token, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn tip004_list_tokens( + asset_public_key: String, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_tip004_list_tokens(asset_public_key, state.inner()).await +} + +pub(crate) async fn inner_tip004_mint_token( + asset_public_key: String, + token: String, + state: &ConcurrentAppState, ) -> Result<(), Status> { let _wallet_id = state .current_wallet_id() @@ -69,10 +85,9 @@ pub(crate) async fn tip004_mint_token( Ok(()) } -#[tauri::command] -pub(crate) async fn tip004_list_tokens( +pub(crate) async fn inner_tip004_list_tokens( asset_public_key: String, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result, Status> { let wallet_id = state .current_wallet_id() diff --git a/applications/tari_collectibles/src-tauri/src/commands/tip721/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/tip721/mod.rs index 927fa900bf..f15303faa3 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/tip721/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/tip721/mod.rs @@ -41,6 +41,24 @@ pub(crate) async fn tip721_transfer_from( send_to_address: String, from_address_id: Uuid, state: tauri::State<'_, ConcurrentAppState>, +) -> Result<(), Status> { + inner_tip721_transfer_from( + asset_public_key, + token_id, + send_to_address, + from_address_id, + state.inner(), + ) + .await +} + +#[tauri::command] +pub(crate) async fn inner_tip721_transfer_from( + asset_public_key: String, + token_id: String, + send_to_address: String, + from_address_id: Uuid, + state: &ConcurrentAppState, ) -> Result<(), Status> { let wallet_id = state .current_wallet_id() diff --git a/applications/tari_collectibles/src-tauri/src/commands/wallets/mod.rs b/applications/tari_collectibles/src-tauri/src/commands/wallets/mod.rs index e27fb3abe3..8e1712ef5e 100644 --- a/applications/tari_collectibles/src-tauri/src/commands/wallets/mod.rs +++ b/applications/tari_collectibles/src-tauri/src/commands/wallets/mod.rs @@ -33,8 +33,40 @@ use uuid::Uuid; #[tauri::command] pub(crate) async fn wallets_create( name: Option, - _passphrase: Option, + passphrase: Option, state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_wallets_create(name, passphrase, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn wallets_list( + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_wallets_list(state.inner()).await +} + +#[tauri::command] +pub(crate) async fn wallets_unlock( + id: Uuid, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result { + inner_wallets_unlock(id, state.inner()).await +} + +#[tauri::command] +pub(crate) async fn wallets_seed_words( + id: Uuid, + passphrase: Option, + state: tauri::State<'_, ConcurrentAppState>, +) -> Result, Status> { + inner_wallets_seed_words(id, passphrase, state.inner()).await +} + +pub async fn inner_wallets_create( + name: Option, + _passphrase: Option, + state: &ConcurrentAppState, ) -> Result { let new_wallet = WalletRow { id: Uuid::new_v4(), @@ -50,9 +82,8 @@ pub(crate) async fn wallets_create( Ok(new_wallet) } -#[tauri::command] -pub(crate) async fn wallets_list( - state: tauri::State<'_, ConcurrentAppState>, +pub(crate) async fn inner_wallets_list( + state: &ConcurrentAppState, ) -> Result, Status> { let db = state.create_db().await?; @@ -60,10 +91,9 @@ pub(crate) async fn wallets_list( Ok(result) } -#[tauri::command] -pub(crate) async fn wallets_unlock( +pub(crate) async fn inner_wallets_unlock( id: Uuid, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result { let db = state.create_db().await?; @@ -73,11 +103,10 @@ pub(crate) async fn wallets_unlock( Ok(result) } -#[tauri::command] -pub(crate) async fn wallets_seed_words( +pub(crate) async fn inner_wallets_seed_words( id: Uuid, passphrase: Option, - state: tauri::State<'_, ConcurrentAppState>, + state: &ConcurrentAppState, ) -> Result, Status> { let db = state.create_db().await?; diff --git a/applications/tari_collectibles/src-tauri/src/main.rs b/applications/tari_collectibles/src-tauri/src/main.rs index 09f0ea900a..9807e6713c 100644 --- a/applications/tari_collectibles/src-tauri/src/main.rs +++ b/applications/tari_collectibles/src-tauri/src/main.rs @@ -7,7 +7,11 @@ use std::error::Error; use tauri::{Menu, MenuItem, Submenu}; use tari_app_utilities::initialization::init_configuration; -use tari_common::configuration::bootstrap::ApplicationType; +use tari_common::{ + configuration::bootstrap::ApplicationType, + exit_codes::{ExitCode, ExitError}, +}; +use uuid::Uuid; use crate::app_state::ConcurrentAppState; @@ -27,6 +31,136 @@ mod schema; mod status; mod storage; +#[derive(Debug)] +pub enum Command { + MakeItRain { + asset_public_key: String, + amount_per_transaction: u64, + number_transactions: u32, + destination_address: String, + source_address: Option, + }, +} + +fn parse_make_it_rain(src: &[&str]) -> Result { + if src.len() < 4 && 5 < src.len() { + return Err(ExitError::new( + ExitCode::CommandError, + "Invalid arguments for make-it-rain", + )); + } + let asset_public_key = src[0].to_string(); + let amount_per_transaction = src[1] + .to_string() + .parse::() + .map_err(|e| ExitError::new(ExitCode::CommandError, e.to_string()))?; + let number_transactions = src[2] + .to_string() + .parse::() + .map_err(|e| ExitError::new(ExitCode::CommandError, e.to_string()))?; + let destination_address = src[3].to_string(); + let source_address = match src.len() { + 5 => Some(src[4].to_string()), + _ => None, + }; + Ok(Command::MakeItRain { + asset_public_key, + amount_per_transaction, + number_transactions, + destination_address, + source_address, + }) +} + +fn parse_command(src: String) -> Result { + let args: Vec<&str> = src.split(' ').collect(); + if args.is_empty() { + return Err(ExitError::new(ExitCode::CommandError, "Empty command")); + } + match args[0] { + "make-it-rain" => parse_make_it_rain(&args[1..]), + _ => Err(ExitError::new(ExitCode::CommandError, "Invalid command")), + } +} + +// make-it-rain +fn make_it_rain( + asset_public_key: String, + amount: u64, + number_transactions: u32, + to_address: String, + source_address: Option, + state: &ConcurrentAppState, +) -> Result<(), ExitError> { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("Failed to build a runtime!"); + let id = match runtime.block_on(commands::wallets::inner_wallets_list(state)) { + Ok(rows) => { + if rows.is_empty() { + return Err(ExitError::new( + ExitCode::CommandError, + "There is no wallet!", + )); + } + match source_address { + Some(source_address) => { + let source_uuid = Uuid::parse_str(&source_address) + .map_err(|e| ExitError::new(ExitCode::CommandError, e.to_string()))?; + if !rows.iter().any(|wallet| wallet.id == source_uuid) { + return Err(ExitError::new(ExitCode::CommandError, "Wallet not found!")); + } + source_uuid + } + None => rows[0].id, + } + } + Err(e) => { + return Err(ExitError::new(ExitCode::CommandError, e.to_string())); + } + }; + + runtime + .block_on(commands::wallets::inner_wallets_unlock(id, state)) + .map_err(|e| ExitError::new(ExitCode::CommandError, e.to_string()))?; + println!( + "Sending {} of {} to {} {} times.", + asset_public_key, amount, to_address, number_transactions + ); + for _ in 0..number_transactions { + runtime + .block_on(commands::asset_wallets::inner_asset_wallets_send_to( + asset_public_key.clone(), + amount, + to_address.clone(), + state, + )) + .map_err(|e| ExitError::new(ExitCode::CommandError, e.to_string()))?; + } + Ok(()) +} + +pub fn process_command(command: Command, state: &ConcurrentAppState) -> Result<(), ExitError> { + println!("command {:?}", command); + match command { + Command::MakeItRain { + asset_public_key, + amount_per_transaction, + number_transactions: number_transaction, + destination_address, + source_address, + } => make_it_rain( + asset_public_key, + amount_per_transaction, + number_transaction, + destination_address, + source_address, + state, + ), + } +} + fn main() -> Result<(), Box> { let (bootstrap, config, _) = init_configuration(ApplicationType::Collectibles)?; let state = ConcurrentAppState::new( @@ -34,6 +168,12 @@ fn main() -> Result<(), Box> { config.collectibles_config.unwrap_or_default(), ); + if let Some(command) = bootstrap.command { + let command = parse_command(command)?; + process_command(command, &state)?; + return Ok(()); + } + tauri::Builder::default() .menu(build_menu()) .manage(state) diff --git a/applications/tari_collectibles/src-tauri/src/status.rs b/applications/tari_collectibles/src-tauri/src/status.rs index 188e9e2582..218ace2fb8 100644 --- a/applications/tari_collectibles/src-tauri/src/status.rs +++ b/applications/tari_collectibles/src-tauri/src/status.rs @@ -20,13 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use core::fmt; + use crate::{error::CollectiblesError, storage::StorageError}; use diesel::result::Error; use prost::{DecodeError, EncodeError}; use serde::{Deserialize, Serialize}; use tari_utilities::hex::HexError; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(tag = "status")] pub enum Status { BadRequest { @@ -125,3 +127,18 @@ impl From for Status { } } } + +impl fmt::Display for Status { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self { + Status::BadRequest { code, message } => write!(f, "BadRequest {} {}", code, message), + Status::Unauthorized { code, message } => write!(f, "Unauthorized {} {}", code, message), + Status::NotFound { + code, + message, + entity, + } => write!(f, "NotFound {} {} {}", code, message, entity), + Status::Internal { code, message } => write!(f, "Internal {} {}", code, message), + } + } +} diff --git a/applications/tari_collectibles/src-tauri/src/storage/models/wallet_row.rs b/applications/tari_collectibles/src-tauri/src/storage/models/wallet_row.rs index 49b0f79707..dbbb467cdf 100644 --- a/applications/tari_collectibles/src-tauri/src/storage/models/wallet_row.rs +++ b/applications/tari_collectibles/src-tauri/src/storage/models/wallet_row.rs @@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct WalletRow { pub id: Uuid, pub name: Option,