diff --git a/src/backend_task/identity/mod.rs b/src/backend_task/identity/mod.rs index c9ceb3e6a..86199807f 100644 --- a/src/backend_task/identity/mod.rs +++ b/src/backend_task/identity/mod.rs @@ -146,7 +146,7 @@ impl IdentityKeys { key_map.into() } - pub fn to_public_keys_map(&self) -> BTreeMap { + pub fn to_public_keys_map(&self) -> Result, String> { let Self { master_private_key, master_private_key_type, @@ -164,7 +164,12 @@ impl IdentityKeys { .to_byte_array() .to_vec() .into(), - _ => panic!("need a ECDSA Key for now"), + other => { + return Err(format!( + "Unsupported master key type: {:?}. Only ECDSA_SECP256K1 and ECDSA_HASH160 are supported.", + other + )); + } }; let key = IdentityPublicKey::V0(IdentityPublicKeyV0 { id: 0, @@ -179,34 +184,72 @@ impl IdentityKeys { key_map.insert(0, key); } - key_map.extend(keys_input.iter().enumerate().map( - |(i, ((private_key, _), key_type, purpose, security_level, contract_bounds))| { - let id = (i + 1) as KeyID; - let data = match key_type { - KeyType::ECDSA_SECP256K1 => private_key.public_key(&secp).to_bytes().into(), - KeyType::ECDSA_HASH160 => private_key - .public_key(&secp) - .pubkey_hash() - .to_byte_array() - .to_vec() - .into(), - _ => panic!("need a ECDSA Key for now"), - }; - let identity_public_key = IdentityPublicKey::V0(IdentityPublicKeyV0 { - id, - purpose: *purpose, - security_level: *security_level, - contract_bounds: contract_bounds.clone(), - key_type: *key_type, - read_only: false, - data, - disabled_at: None, - }); - (id, identity_public_key) - }, - )); + for (i, ((private_key, _), key_type, purpose, security_level, contract_bounds)) in + keys_input.iter().enumerate() + { + let id = (i + 1) as KeyID; + + // Validate security level matches key purpose (defense-in-depth) + match purpose { + Purpose::TRANSFER => { + if *security_level != SecurityLevel::CRITICAL { + return Err(format!( + "Key {}: TRANSFER purpose requires CRITICAL security level, got {:?}", + id, security_level + )); + } + } + Purpose::ENCRYPTION | Purpose::DECRYPTION => { + if *security_level != SecurityLevel::MEDIUM { + return Err(format!( + "Key {}: {:?} purpose requires MEDIUM security level, got {:?}", + id, purpose, security_level + )); + } + } + Purpose::AUTHENTICATION => { + if *security_level != SecurityLevel::CRITICAL + && *security_level != SecurityLevel::HIGH + && *security_level != SecurityLevel::MEDIUM + { + return Err(format!( + "Key {}: AUTHENTICATION purpose requires CRITICAL, HIGH, or MEDIUM security level, got {:?}", + id, security_level + )); + } + } + _ => {} + } + + let data = match key_type { + KeyType::ECDSA_SECP256K1 => private_key.public_key(&secp).to_bytes().into(), + KeyType::ECDSA_HASH160 => private_key + .public_key(&secp) + .pubkey_hash() + .to_byte_array() + .to_vec() + .into(), + other => { + return Err(format!( + "Unsupported key type for key {}: {:?}. Only ECDSA_SECP256K1 and ECDSA_HASH160 are supported.", + id, other + )); + } + }; + let identity_public_key = IdentityPublicKey::V0(IdentityPublicKeyV0 { + id, + purpose: *purpose, + security_level: *security_level, + contract_bounds: contract_bounds.clone(), + key_type: *key_type, + read_only: false, + data, + disabled_at: None, + }); + key_map.insert(id, identity_public_key); + } - key_map + Ok(key_map) } } diff --git a/src/backend_task/identity/register_identity.rs b/src/backend_task/identity/register_identity.rs index 47ae6fa5d..ab15d026b 100644 --- a/src/backend_task/identity/register_identity.rs +++ b/src/backend_task/identity/register_identity.rs @@ -296,7 +296,7 @@ impl AppContext { .create_identifier() .expect("expected to create an identifier"); - let public_keys = keys.to_public_keys_map(); + let public_keys = keys.to_public_keys_map()?; // Debug: Log the keys being registered to verify contract bounds are set for (key_id, key) in &public_keys { @@ -651,7 +651,7 @@ impl AppContext { guard.clone() }; - let public_keys = keys.to_public_keys_map(); + let public_keys = keys.to_public_keys_map()?; // Calculate fee estimate for identity creation from platform addresses let key_count = public_keys.len(); diff --git a/src/backend_task/tokens/query_tokens.rs b/src/backend_task/tokens/query_tokens.rs index 014c57400..dc4aa70cd 100644 --- a/src/backend_task/tokens/query_tokens.rs +++ b/src/backend_task/tokens/query_tokens.rs @@ -27,7 +27,7 @@ impl AppContext { // ── 1. fetch keyword → contractId docs ──────────────────────────────── let mut kw_query = DocumentQuery::new(self.keyword_search_contract.clone(), "contractKeywords") - .expect("create query"); + .map_err(|e| format!("Failed to create document query: {}", e))?; kw_query.limit = 100; kw_query.start = cursor.clone(); kw_query = kw_query.with_where(WhereClause { @@ -69,7 +69,7 @@ impl AppContext { // build a WHERE contractId == cid query let mut desc_query = DocumentQuery::new(self.keyword_search_contract.clone(), "shortDescription") - .expect("create desc query"); + .map_err(|e| format!("Failed to create document query: {}", e))?; desc_query.limit = 1; // only one per contract (schema‑unique) desc_query = desc_query.with_where(WhereClause { field: "contractId".into(), diff --git a/src/ui/dashpay/contacts_list.rs b/src/ui/dashpay/contacts_list.rs index 0abd74e1d..8d17f7612 100644 --- a/src/ui/dashpay/contacts_list.rs +++ b/src/ui/dashpay/contacts_list.rs @@ -1044,10 +1044,13 @@ impl ScreenLike for ContactsList { // Clear all existing contacts for this identity from database first // This prevents stale contacts from persisting - let _ = self + if let Err(e) = self .app_context .db - .clear_dashpay_contacts(&owner_id, &network_str); + .clear_dashpay_contacts(&owner_id, &network_str) + { + tracing::warn!("Failed to clear dashpay contacts from database: {}", e); + } // Convert ContactData to Contact structs and save to database for contact_data in contacts_data { @@ -1073,7 +1076,7 @@ impl ScreenLike for ContactsList { self.contacts.insert(contact_data.identity_id, contact); // Save to database - let _ = self.app_context.db.save_dashpay_contact( + if let Err(e) = self.app_context.db.save_dashpay_contact( &owner_id, &contact_data.identity_id, &network_str, @@ -1082,16 +1085,23 @@ impl ScreenLike for ContactsList { contact_data.avatar_url.as_deref(), None, // public_message - not yet fetched "accepted", // Only accepted contacts are returned from load_contacts - ); + ) { + tracing::warn!("Failed to save dashpay contact to database: {}", e); + } // Save private info if present - if let Some(nickname) = &contact_data.nickname { - let _ = self.app_context.db.save_contact_private_info( + if let Some(nickname) = &contact_data.nickname + && let Err(e) = self.app_context.db.save_contact_private_info( &owner_id, &contact_data.identity_id, nickname, &contact_data.note.unwrap_or_default(), contact_data.is_hidden, + ) + { + tracing::warn!( + "Failed to save contact private info to database: {}", + e ); } } @@ -1163,7 +1173,7 @@ impl ScreenLike for ContactsList { if let Some(identity) = &self.selected_identity { let owner_id = identity.identity.id(); let network_str = self.app_context.network.to_string(); - let _ = self.app_context.db.save_dashpay_contact( + if let Err(e) = self.app_context.db.save_dashpay_contact( &owner_id, &contact_id, &network_str, @@ -1172,7 +1182,12 @@ impl ScreenLike for ContactsList { contact.avatar_url.as_deref(), public_message.as_deref(), "accepted", - ); + ) { + tracing::warn!( + "Failed to save updated contact profile to database: {}", + e + ); + } } } } diff --git a/src/ui/identities/add_new_identity_screen/mod.rs b/src/ui/identities/add_new_identity_screen/mod.rs index db7301789..847ac2c11 100644 --- a/src/ui/identities/add_new_identity_screen/mod.rs +++ b/src/ui/identities/add_new_identity_screen/mod.rs @@ -668,6 +668,7 @@ impl AddNewIdentityScreen { }); row.col(|ui| { ui.vertical(|ui| { + let prev_purpose = *purpose; ComboBox::from_id_salt(format!("purpose_combo_{}", i)) .selected_text(format!("{:?}", purpose)) .show_ui(ui, |ui| { @@ -681,7 +682,37 @@ impl AddNewIdentityScreen { Purpose::TRANSFER, "TRANSFER", ); + ui.selectable_value( + purpose, + Purpose::ENCRYPTION, + "ENCRYPTION", + ); + ui.selectable_value( + purpose, + Purpose::DECRYPTION, + "DECRYPTION", + ); }); + // Auto-set security level when purpose changes + if *purpose != prev_purpose { + match *purpose { + Purpose::TRANSFER => { + *security_level = SecurityLevel::CRITICAL; + } + Purpose::ENCRYPTION | Purpose::DECRYPTION => { + *security_level = SecurityLevel::MEDIUM; + } + Purpose::AUTHENTICATION => { + if *security_level != SecurityLevel::CRITICAL + && *security_level != SecurityLevel::HIGH + && *security_level != SecurityLevel::MEDIUM + { + *security_level = SecurityLevel::CRITICAL; + } + } + _ => {} + } + } }); }); row.col(|ui| { @@ -710,6 +741,11 @@ impl AddNewIdentityScreen { if *purpose == Purpose::TRANSFER { *security_level = SecurityLevel::CRITICAL; ui.label("Locked to CRITICAL"); + } else if *purpose == Purpose::ENCRYPTION + || *purpose == Purpose::DECRYPTION + { + *security_level = SecurityLevel::MEDIUM; + ui.label("Locked to MEDIUM"); } else { ui.selectable_value( security_level, diff --git a/src/ui/identities/identities_screen.rs b/src/ui/identities/identities_screen.rs index 6d6e07d30..7601499a2 100644 --- a/src/ui/identities/identities_screen.rs +++ b/src/ui/identities/identities_screen.rs @@ -931,25 +931,42 @@ impl IdentitiesScreen { if ui.add(yes_button).clicked() { let identity_id = identity_to_remove.identity.id(); - let mut lock = self.identities.lock().unwrap(); - lock.shift_remove(&identity_id); - self.app_context + match self + .app_context .db .delete_local_qualified_identity(&identity_id, &self.app_context) - .ok(); + { + Ok(_) => { + let mut lock = self.identities.lock().unwrap(); + lock.shift_remove(&identity_id); + } + Err(e) => { + tracing::warn!( + "Failed to delete identity from database: {}", + e + ); + self.backend_message = Some(( + format!("Failed to remove identity: {}", e), + MessageType::Error, + Utc::now(), + )); + } + } if let Some((voter_identity, _)) = &identity_to_remove.associated_voter_identity { let voter_identity_id = voter_identity.id(); - self.app_context - .db - .delete_local_qualified_identity( - &voter_identity_id, - &self.app_context, - ) - .ok(); + if let Err(e) = self.app_context.db.delete_local_qualified_identity( + &voter_identity_id, + &self.app_context, + ) { + tracing::warn!( + "Failed to delete voter identity from database: {}", + e + ); + } } self.identity_to_remove = None; diff --git a/src/ui/identities/transfer_screen.rs b/src/ui/identities/transfer_screen.rs index 9d4f35ee9..07b119b89 100644 --- a/src/ui/identities/transfer_screen.rs +++ b/src/ui/identities/transfer_screen.rs @@ -501,14 +501,21 @@ impl ScreenLike for TransferScreen { fn refresh(&mut self) { // Refresh the identity because there might be new keys - self.identity = self - .app_context - .load_local_qualified_identities() - .unwrap() - .into_iter() + let identities = match self.app_context.load_local_qualified_identities() { + Ok(list) => list, + Err(e) => { + tracing::warn!("Failed to load identities during refresh: {}", e); + Vec::new() + } + }; + if let Some(refreshed) = identities + .iter() .find(|identity| identity.identity.id() == self.identity.identity.id()) - .unwrap(); - self.max_amount = self.identity.identity.balance(); + { + self.identity = refreshed.clone(); + self.max_amount = self.identity.identity.balance(); + } + self.known_identities = identities; } /// Renders the UI components for the withdrawal screen diff --git a/src/ui/identities/withdraw_screen.rs b/src/ui/identities/withdraw_screen.rs index 1ea5aebee..01ecffda3 100644 --- a/src/ui/identities/withdraw_screen.rs +++ b/src/ui/identities/withdraw_screen.rs @@ -319,14 +319,16 @@ impl ScreenLike for WithdrawalScreen { fn refresh(&mut self) { // Refresh the identity because there might be new keys - self.identity = self + if let Some(refreshed) = self .app_context .load_local_qualified_identities() - .unwrap() + .unwrap_or_default() .into_iter() .find(|identity| identity.identity.id() == self.identity.identity.id()) - .unwrap(); - self.max_amount = self.identity.identity.balance(); + { + self.identity = refreshed; + self.max_amount = self.identity.identity.balance(); + } } /// Renders the UI components for the withdrawal screen diff --git a/src/ui/tokens/claim_tokens_screen.rs b/src/ui/tokens/claim_tokens_screen.rs index 4d95f23d2..3d3ca86fa 100644 --- a/src/ui/tokens/claim_tokens_screen.rs +++ b/src/ui/tokens/claim_tokens_screen.rs @@ -203,6 +203,16 @@ impl ClaimTokensScreen { match dialog.show(ui).inner.dialog_response { Some(ConfirmationStatus::Confirmed) => { self.confirmation_dialog = None; + + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = ClaimTokensStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") @@ -216,7 +226,7 @@ impl ClaimTokensScreen { token_position: self.identity_token_basic_info.token_position, actor_identity: self.identity.clone(), distribution_type, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: self.public_note.clone(), })), BackendTask::TokenTask(Box::new(TokenTask::QueryMyTokenBalances)), diff --git a/src/ui/tokens/destroy_frozen_funds_screen.rs b/src/ui/tokens/destroy_frozen_funds_screen.rs index 11d5f8784..a7beb05b0 100644 --- a/src/ui/tokens/destroy_frozen_funds_screen.rs +++ b/src/ui/tokens/destroy_frozen_funds_screen.rs @@ -260,19 +260,29 @@ impl DestroyFrozenFundsScreen { } fn confirmation_ok(&mut self) -> AppAction { - let maybe_frozen_id = Identifier::from_string_try_encodings( + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = DestroyFrozenFundsStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + + let frozen_id = match Identifier::from_string_try_encodings( &self.frozen_identity_id, &[ dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, dash_sdk::dpp::platform_value::string_encoding::Encoding::Hex, ], - ); - if maybe_frozen_id.is_err() { - self.error_message = Some("Invalid frozen identity format".into()); - self.status = DestroyFrozenFundsStatus::ErrorMessage("Invalid identity".into()); - return AppAction::None; - } - let frozen_id = maybe_frozen_id.unwrap(); + ) { + Ok(id) => id, + Err(_) => { + self.error_message = Some("Invalid frozen identity format".into()); + self.status = DestroyFrozenFundsStatus::ErrorMessage("Invalid identity".into()); + return AppAction::None; + } + }; let now = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -282,12 +292,12 @@ impl DestroyFrozenFundsScreen { let data_contract = Arc::new(self.identity_token_info.data_contract.contract.clone()); - let group_info = if self.group_action_id.is_some() { + let group_info = if let Some(action_id) = self.group_action_id { self.group.as_ref().map(|(pos, _)| { GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( GroupStateTransitionInfo { group_contract_position: *pos, - action_id: self.group_action_id.unwrap(), + action_id, action_is_proposer: false, }, ) @@ -303,7 +313,7 @@ impl DestroyFrozenFundsScreen { actor_identity: self.identity.clone(), data_contract, token_position: self.identity_token_info.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: if self.group_action_id.is_some() { None } else { diff --git a/src/ui/tokens/freeze_tokens_screen.rs b/src/ui/tokens/freeze_tokens_screen.rs index 35ac3d6e4..8f1d25cf3 100644 --- a/src/ui/tokens/freeze_tokens_screen.rs +++ b/src/ui/tokens/freeze_tokens_screen.rs @@ -250,20 +250,30 @@ impl FreezeTokensScreen { /// Handle confirmation OK action fn confirmation_ok(&mut self) -> AppAction { + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = FreezeTokensStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + // Validate user input - let parsed = Identifier::from_string_try_encodings( + let freeze_id = match Identifier::from_string_try_encodings( &self.freeze_identity_id, &[ dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, dash_sdk::dpp::platform_value::string_encoding::Encoding::Hex, ], - ); - if parsed.is_err() { - self.error_message = Some("Please enter a valid identity ID.".into()); - self.status = FreezeTokensStatus::ErrorMessage("Invalid identity".into()); - return AppAction::None; - } - let freeze_id = parsed.unwrap(); + ) { + Ok(id) => id, + Err(_) => { + self.error_message = Some("Please enter a valid identity ID.".into()); + self.status = FreezeTokensStatus::ErrorMessage("Invalid identity".into()); + return AppAction::None; + } + }; let now = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -274,12 +284,12 @@ impl FreezeTokensScreen { // Grab the data contract for this token from the app context let data_contract = Arc::new(self.identity_token_info.data_contract.contract.clone()); - let group_info = if self.group_action_id.is_some() { + let group_info = if let Some(action_id) = self.group_action_id { self.group.as_ref().map(|(pos, _)| { GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( GroupStateTransitionInfo { group_contract_position: *pos, - action_id: self.group_action_id.unwrap(), + action_id, action_is_proposer: false, }, ) @@ -295,7 +305,7 @@ impl FreezeTokensScreen { actor_identity: self.identity.clone(), data_contract, token_position: self.identity_token_info.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: if self.group_action_id.is_some() { None } else { diff --git a/src/ui/tokens/mint_tokens_screen.rs b/src/ui/tokens/mint_tokens_screen.rs index 0244d6797..27a07691d 100644 --- a/src/ui/tokens/mint_tokens_screen.rs +++ b/src/ui/tokens/mint_tokens_screen.rs @@ -278,27 +278,36 @@ impl MintTokensScreen { } fn confirmation_ok(&mut self) -> AppAction { + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = MintTokensStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + if self.amount.is_none() || self.amount == Some(Amount::new(0, 0)) { self.status = MintTokensStatus::ErrorMessage("Invalid amount".into()); self.error_message = Some("Invalid amount".into()); return AppAction::None; } - let parsed_receiver_id = Identifier::from_string_try_encodings( + let receiver_id = match Identifier::from_string_try_encodings( &self.recipient_identity_id, &[ dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, dash_sdk::dpp::platform_value::string_encoding::Encoding::Hex, ], - ); - - if parsed_receiver_id.is_err() { - self.status = MintTokensStatus::ErrorMessage("Invalid receiver".into()); - self.error_message = Some("Invalid receiver".into()); - return AppAction::None; - } + ) { + Ok(id) => id, + Err(_) => { + self.status = MintTokensStatus::ErrorMessage("Invalid receiver".into()); + self.error_message = Some("Invalid receiver".into()); + return AppAction::None; + } + }; - let receiver_id = parsed_receiver_id.unwrap(); let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") @@ -307,12 +316,12 @@ impl MintTokensScreen { let data_contract = Arc::new(self.identity_token_info.data_contract.contract.clone()); - let group_info = if self.group_action_id.is_some() { + let group_info = if let Some(action_id) = self.group_action_id { self.group.as_ref().map(|(pos, _)| { GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( GroupStateTransitionInfo { group_contract_position: *pos, - action_id: self.group_action_id.unwrap(), + action_id, action_is_proposer: false, }, ) @@ -327,7 +336,7 @@ impl MintTokensScreen { sending_identity: self.identity_token_info.identity.clone(), data_contract, token_position: self.identity_token_info.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: if self.group_action_id.is_some() { None } else { diff --git a/src/ui/tokens/pause_tokens_screen.rs b/src/ui/tokens/pause_tokens_screen.rs index c6624398c..4b92895c4 100644 --- a/src/ui/tokens/pause_tokens_screen.rs +++ b/src/ui/tokens/pause_tokens_screen.rs @@ -207,6 +207,16 @@ impl PauseTokensScreen { match dialog.show(ui).inner.dialog_response { Some(ConfirmationStatus::Confirmed) => { self.confirmation_dialog = None; + + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = PauseTokensStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") @@ -217,12 +227,12 @@ impl PauseTokensScreen { let data_contract = Arc::new(self.identity_token_info.data_contract.contract.clone()); - let group_info = if self.group_action_id.is_some() { + let group_info = if let Some(action_id) = self.group_action_id { self.group.as_ref().map(|(pos, _)| { GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( GroupStateTransitionInfo { group_contract_position: *pos, - action_id: self.group_action_id.unwrap(), + action_id, action_is_proposer: false, }, ) @@ -237,7 +247,7 @@ impl PauseTokensScreen { actor_identity: self.identity.clone(), data_contract, token_position: self.identity_token_info.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: if self.group_action_id.is_some() { None } else { diff --git a/src/ui/tokens/resume_tokens_screen.rs b/src/ui/tokens/resume_tokens_screen.rs index 7e20dac1e..7d9777064 100644 --- a/src/ui/tokens/resume_tokens_screen.rs +++ b/src/ui/tokens/resume_tokens_screen.rs @@ -207,6 +207,16 @@ impl ResumeTokensScreen { match dialog.show(ui).inner.dialog_response { Some(ConfirmationStatus::Confirmed) => { self.confirmation_dialog = None; + + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = ResumeTokensStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") @@ -217,12 +227,12 @@ impl ResumeTokensScreen { let data_contract = Arc::new(self.identity_token_info.data_contract.contract.clone()); - let group_info = if self.group_action_id.is_some() { + let group_info = if let Some(action_id) = self.group_action_id { self.group.as_ref().map(|(pos, _)| { GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( GroupStateTransitionInfo { group_contract_position: *pos, - action_id: self.group_action_id.unwrap(), + action_id, action_is_proposer: false, }, ) @@ -237,7 +247,7 @@ impl ResumeTokensScreen { actor_identity: self.identity.clone(), data_contract, token_position: self.identity_token_info.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: if self.group_action_id.is_some() { None } else { diff --git a/src/ui/tokens/tokens_screen/token_creator.rs b/src/ui/tokens/tokens_screen/token_creator.rs index 3b4dc2fc8..e03bc6f84 100644 --- a/src/ui/tokens/tokens_screen/token_creator.rs +++ b/src/ui/tokens/tokens_screen/token_creator.rs @@ -1484,11 +1484,23 @@ impl TokensScreen { } }; + // Validate identity and key are selected + let (identity, signing_key) = + match (&self.selected_identity, &self.selected_key) { + (Some(id), Some(key)) => (id.clone(), key.clone()), + _ => { + self.token_creator_error_message = + Some("Please select an identity and signing key.".to_string()); + self.close_token_creator_confirmation_popup(); + return AppAction::None; + } + }; + // Now create your tasks let tasks = vec![ BackendTask::TokenTask(Box::new(TokenTask::RegisterTokenContract { - identity: self.selected_identity.clone().unwrap(), - signing_key: Box::new(self.selected_key.clone().unwrap()), + identity, + signing_key: Box::new(signing_key), token_names: args.token_names, contract_keywords: args.contract_keywords, diff --git a/src/ui/tokens/transfer_tokens_screen.rs b/src/ui/tokens/transfer_tokens_screen.rs index 8fffe040b..d84b0838b 100644 --- a/src/ui/tokens/transfer_tokens_screen.rs +++ b/src/ui/tokens/transfer_tokens_screen.rs @@ -193,27 +193,36 @@ impl TransferTokensScreen { } fn confirmation_ok(&mut self) -> AppAction { + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.transfer_tokens_status = + TransferTokensStatus::ErrorMessage("No signing key selected".into()); + return AppAction::None; + } + }; + if self.amount.is_none() || self.amount == Some(Amount::new(0, 0)) { self.transfer_tokens_status = TransferTokensStatus::ErrorMessage("Invalid amount".into()); return AppAction::None; } - let parsed_receiver_id = Identifier::from_string_try_encodings( + let receiver_id = match Identifier::from_string_try_encodings( &self.receiver_identity_id, &[ dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, dash_sdk::dpp::platform_value::string_encoding::Encoding::Hex, ], - ); - - if parsed_receiver_id.is_err() { - self.transfer_tokens_status = - TransferTokensStatus::ErrorMessage("Invalid receiver".into()); - return AppAction::None; - } + ) { + Ok(id) => id, + Err(_) => { + self.transfer_tokens_status = + TransferTokensStatus::ErrorMessage("Invalid receiver".into()); + return AppAction::None; + } + }; - let receiver_id = parsed_receiver_id.unwrap(); let now = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") @@ -234,7 +243,7 @@ impl TransferTokensScreen { amount: self.amount.clone().unwrap_or(Amount::new(0, 0)).value(), data_contract, token_position: self.identity_token_balance.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: self.public_note.clone(), }, ))) diff --git a/src/ui/tokens/unfreeze_tokens_screen.rs b/src/ui/tokens/unfreeze_tokens_screen.rs index a0b97f777..98f274df3 100644 --- a/src/ui/tokens/unfreeze_tokens_screen.rs +++ b/src/ui/tokens/unfreeze_tokens_screen.rs @@ -252,20 +252,30 @@ impl UnfreezeTokensScreen { } fn confirmation_ok(&mut self) -> AppAction { + let signing_key = match self.selected_key.clone() { + Some(key) => key, + None => { + self.error_message = Some("No signing key selected".into()); + self.status = UnfreezeTokensStatus::ErrorMessage("No key selected".into()); + return AppAction::None; + } + }; + // Validate user input - let parsed = Identifier::from_string_try_encodings( + let unfreeze_id = match Identifier::from_string_try_encodings( &self.unfreeze_identity_id, &[ dash_sdk::dpp::platform_value::string_encoding::Encoding::Base58, dash_sdk::dpp::platform_value::string_encoding::Encoding::Hex, ], - ); - if parsed.is_err() { - self.error_message = Some("Please enter a valid identity ID.".into()); - self.status = UnfreezeTokensStatus::ErrorMessage("Invalid identity ID".into()); - return AppAction::None; - } - let unfreeze_id = parsed.unwrap(); + ) { + Ok(id) => id, + Err(_) => { + self.error_message = Some("Please enter a valid identity ID.".into()); + self.status = UnfreezeTokensStatus::ErrorMessage("Invalid identity ID".into()); + return AppAction::None; + } + }; let now = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -276,12 +286,12 @@ impl UnfreezeTokensScreen { // Grab the data contract for this token from the app context let data_contract = Arc::new(self.identity_token_info.data_contract.contract.clone()); - let group_info = if self.group_action_id.is_some() { + let group_info = if let Some(action_id) = self.group_action_id { self.group.as_ref().map(|(pos, _)| { GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( GroupStateTransitionInfo { group_contract_position: *pos, - action_id: self.group_action_id.unwrap(), + action_id, action_is_proposer: false, }, ) @@ -298,7 +308,7 @@ impl UnfreezeTokensScreen { actor_identity: self.identity.clone(), data_contract, token_position: self.identity_token_info.token_position, - signing_key: self.selected_key.clone().expect("No key selected"), + signing_key, public_note: if self.group_action_id.is_some() { None } else {