From 30ffb577e1dae58360015d97c1405b83f8d7790e Mon Sep 17 00:00:00 2001 From: olawale880 Date: Wed, 25 Feb 2026 23:37:28 +0100 Subject: [PATCH] No-unwrap() / No-expect() Policy in Contract Code --- bill_payments/src/lib.rs | 3 +- data_migration/src/lib.rs | 4 ++- family_wallet/src/lib.rs | 57 +++++++++++++++++++------------------ insurance/src/lib.rs | 24 +++++++++------- remittance_split/src/lib.rs | 16 +++++++++-- reporting/src/lib.rs | 15 +++++----- savings_goals/src/lib.rs | 50 ++++++++++++++++++++++---------- 7 files changed, 104 insertions(+), 65 deletions(-) diff --git a/bill_payments/src/lib.rs b/bill_payments/src/lib.rs index e0dceb9..e5bc53c 100644 --- a/bill_payments/src/lib.rs +++ b/bill_payments/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] mod events; use events::{EventCategory, EventPriority, RemitwiseEvents}; @@ -972,7 +973,7 @@ impl BillPayments { pub fn batch_pay_bills(env: Env, caller: Address, bill_ids: Vec) -> Result { caller.require_auth(); Self::require_not_paused(&env, pause_functions::PAY_BILL)?; - if bill_ids.len() > (MAX_BATCH_SIZE as usize).try_into().unwrap() { + if bill_ids.len() > (MAX_BATCH_SIZE as usize).try_into().unwrap_or(u32::MAX) { return Err(Error::BatchTooLarge); } let bills_map: Map = env diff --git a/data_migration/src/lib.rs b/data_migration/src/lib.rs index 9bfcd46..ea99da4 100644 --- a/data_migration/src/lib.rs +++ b/data_migration/src/lib.rs @@ -3,6 +3,8 @@ //! Supports multiple formats (JSON, binary, CSV), checksum validation, //! version compatibility checks, and data integrity verification. +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] + use base64::Engine; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -104,7 +106,7 @@ impl ExportSnapshot { /// Compute SHA256 checksum of the payload (canonical JSON). pub fn compute_checksum(&self) -> String { let mut hasher = Sha256::new(); - hasher.update(serde_json::to_vec(&self.payload).expect("payload must be serializable")); + hasher.update(serde_json::to_vec(&self.payload).unwrap_or_else(|_| panic!("payload must be serializable"))); hex::encode(hasher.finalize().as_ref()) } diff --git a/family_wallet/src/lib.rs b/family_wallet/src/lib.rs index c65f8de..75432b0 100644 --- a/family_wallet/src/lib.rs +++ b/family_wallet/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, token::TokenClient, Address, Env, Map, Symbol, Vec, @@ -299,7 +300,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); if members.get(member_address.clone()).is_some() { return Err(Error::InvalidRole); @@ -339,7 +340,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); members.get(member_address) } @@ -364,7 +365,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); let mut record = members .get(member_address.clone()) @@ -445,7 +446,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); if !Self::is_owner_or_admin_in_members(&members, &caller) { panic!("Only Owner or Admin can configure multi-sig"); @@ -507,7 +508,7 @@ impl FamilyWallet { .storage() .instance() .get(&config_key) - .expect("Multi-sig config not found"); + .unwrap_or_else(|| panic!("Multi-sig config not found")); let requires_multisig = match (&tx_type, &data) { (TransactionType::RegularWithdrawal, TransactionData::Withdrawal(_, _, amount)) => { @@ -555,7 +556,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("PEND_TXS")) - .expect("Pending transactions map not initialized"); + .unwrap_or_else(|| panic!("Pending transactions map not initialized")); pending_txs.set(tx_id, pending_tx); env.storage() @@ -580,9 +581,9 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("PEND_TXS")) - .expect("Pending transactions map not initialized"); + .unwrap_or_else(|| panic!("Pending transactions map not initialized")); - let mut pending_tx = pending_txs.get(tx_id).expect("Transaction not found"); + let mut pending_tx = pending_txs.get(tx_id).unwrap_or_else(|| panic!("Transaction not found")); let current_time = env.ledger().timestamp(); if current_time > pending_tx.expires_at { @@ -599,7 +600,7 @@ impl FamilyWallet { .storage() .instance() .get(&Self::get_config_key(pending_tx.tx_type)) - .expect("Multi-sig config not found"); + .unwrap_or_else(|| panic!("Multi-sig config not found")); let mut is_authorized = false; for authorized_signer in config.signers.iter() { @@ -634,7 +635,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("EXEC_TXS")) - .expect("Executed transactions map not initialized"); + .unwrap_or_else(|| panic!("Executed transactions map not initialized")); executed_txs.set(tx_id, true); env.storage() @@ -668,7 +669,7 @@ impl FamilyWallet { .storage() .instance() .get(&Self::get_config_key(TransactionType::LargeWithdrawal)) - .expect("Multi-sig config not found"); + .unwrap_or_else(|| panic!("Multi-sig config not found")); let tx_type = if amount > config.spending_limit { TransactionType::LargeWithdrawal @@ -836,7 +837,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); let timestamp = env.ledger().timestamp(); members.set( @@ -865,7 +866,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("OWNER")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); if caller != owner { panic!("Only Owner can remove family members"); @@ -880,7 +881,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); members.remove(member.clone()); env.storage() @@ -896,7 +897,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("PEND_TXS")) - .expect("Pending transactions map not initialized"); + .unwrap_or_else(|| panic!("Pending transactions map not initialized")); pending_txs.get(tx_id) } @@ -910,7 +911,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); members.get(member) } @@ -919,7 +920,7 @@ impl FamilyWallet { env.storage() .instance() .get(&symbol_short!("OWNER")) - .expect("Wallet not initialized") + .unwrap_or_else(|| panic!("Wallet not initialized")) } pub fn get_emergency_config(env: Env) -> Option { @@ -1117,7 +1118,7 @@ impl FamilyWallet { env.storage() .instance() .get(&symbol_short!("OWNER")) - .expect("Wallet not initialized") + .unwrap_or_else(|| panic!("Wallet not initialized")) }); if admin != caller { panic!("Only pause admin can pause"); @@ -1136,7 +1137,7 @@ impl FamilyWallet { env.storage() .instance() .get(&symbol_short!("OWNER")) - .expect("Wallet not initialized") + .unwrap_or_else(|| panic!("Wallet not initialized")) }); if admin != caller { panic!("Only pause admin can unpause"); @@ -1188,7 +1189,7 @@ impl FamilyWallet { env.storage() .instance() .get(&symbol_short!("OWNER")) - .expect("Wallet not initialized") + .unwrap_or_else(|| panic!("Wallet not initialized")) }); if admin != caller { panic!("Only upgrade admin can set version"); @@ -1220,7 +1221,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); let timestamp = env.ledger().timestamp(); let mut count = 0u32; for item in members.iter() { @@ -1259,7 +1260,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("OWNER")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); if caller != owner { panic!("Only Owner can remove members"); } @@ -1272,7 +1273,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); let mut count = 0u32; for addr in addresses.iter() { if addr.clone() == owner { @@ -1328,7 +1329,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("EM_CONF")) - .expect("Emergency config not set"); + .unwrap_or_else(|| panic!("Emergency config not set")); if amount > config.max_amount { panic!("Emergency amount exceeds maximum allowed"); @@ -1406,7 +1407,7 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); + .unwrap_or_else(|| panic!("Wallet not initialized")); if let Some(mut member_data) = members.get(member.clone()) { member_data.role = *new_role; @@ -1507,8 +1508,8 @@ impl FamilyWallet { .storage() .instance() .get(&symbol_short!("MEMBERS")) - .expect("Wallet not initialized"); - let member = members.get(caller.clone()).expect("Not a family member"); + .unwrap_or_else(|| panic!("Wallet not initialized")); + let member = members.get(caller.clone()).unwrap_or_else(|| panic!("Not a family member")); if Self::role_has_expired(env, caller) { panic!("Role has expired"); } @@ -1541,7 +1542,7 @@ impl FamilyWallet { let mut v = Vec::new(env); let start = n - MAX_ACCESS_AUDIT_ENTRIES; for i in start..n { - v.push_back(entries.get(i).unwrap()); + v.push_back(entries.get(i).unwrap_or_else(|| panic!("Item not found"))); } entries = v; } diff --git a/insurance/src/lib.rs b/insurance/src/lib.rs index 72aac8a..435ce49 100644 --- a/insurance/src/lib.rs +++ b/insurance/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] use soroban_sdk::{ contract, contractimpl, contracttype, symbol_short, Address, Env, Map, String, Symbol, Vec, }; @@ -233,7 +234,7 @@ impl Insurance { } pub fn pause(env: Env, caller: Address) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).unwrap_or_else(|| panic!("No pause admin set")); if admin != caller { panic!("Unauthorized"); } @@ -245,7 +246,7 @@ impl Insurance { } pub fn unpause(env: Env, caller: Address) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).unwrap_or_else(|| panic!("No pause admin set")); if admin != caller { panic!("Unauthorized"); } @@ -264,7 +265,7 @@ impl Insurance { } pub fn pause_function(env: Env, caller: Address, func: Symbol) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).unwrap_or_else(|| panic!("No pause admin set")); if admin != caller { panic!("Unauthorized"); } @@ -280,7 +281,7 @@ impl Insurance { } pub fn unpause_function(env: Env, caller: Address, func: Symbol) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).unwrap_or_else(|| panic!("No pause admin set")); if admin != caller { panic!("Unauthorized"); } @@ -337,7 +338,10 @@ impl Insurance { } pub fn set_version(env: Env, caller: Address, new_version: u32) { caller.require_auth(); - let admin = Self::get_upgrade_admin(&env).expect("No upgrade admin set"); + let admin = match Self::get_upgrade_admin(&env) { + Some(a) => a, + None => panic!("No upgrade admin set"), + }; if admin != caller { panic!("Unauthorized"); } @@ -547,7 +551,7 @@ impl Insurance { let current_time = env.ledger().timestamp(); let mut paid_count = 0u32; for id in policy_ids.iter() { - let mut policy = policies.get(id).unwrap(); + let mut policy = policies.get(id).unwrap_or_else(|| panic!("Policy not found")); policy.next_payment_date = current_time + (30 * 86400); let event = PremiumPaidEvent { policy_id: id, @@ -713,7 +717,7 @@ impl Insurance { .get(&symbol_short!("POLICIES")) .unwrap_or_else(|| Map::new(&env)); - let mut policy = policies.get(policy_id).expect("Policy not found"); + let mut policy = policies.get(policy_id).unwrap_or_else(|| panic!("Policy not found")); if policy.owner != caller { panic!("Only the policy owner can deactivate this policy"); @@ -794,7 +798,7 @@ impl Insurance { .get(&symbol_short!("POLICIES")) .unwrap_or_else(|| Map::new(&env)); - let mut policy = policies.get(policy_id).expect("Policy not found"); + let mut policy = policies.get(policy_id).unwrap_or_else(|| panic!("Policy not found")); if policy.owner != owner { panic!("Only the policy owner can create schedules"); @@ -879,7 +883,7 @@ impl Insurance { .get(&symbol_short!("PREM_SCH")) .unwrap_or_else(|| Map::new(&env)); - let mut schedule = schedules.get(schedule_id).expect("Schedule not found"); + let mut schedule = schedules.get(schedule_id).unwrap_or_else(|| panic!("Schedule not found")); if schedule.owner != caller { panic!("Only the schedule owner can modify it"); @@ -913,7 +917,7 @@ impl Insurance { .get(&symbol_short!("PREM_SCH")) .unwrap_or_else(|| Map::new(&env)); - let mut schedule = schedules.get(schedule_id).expect("Schedule not found"); + let mut schedule = schedules.get(schedule_id).unwrap_or_else(|| panic!("Schedule not found")); if schedule.owner != caller { panic!("Only the schedule owner can cancel it"); diff --git a/remittance_split/src/lib.rs b/remittance_split/src/lib.rs index cde755b..a12681e 100644 --- a/remittance_split/src/lib.rs +++ b/remittance_split/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] mod test; use soroban_sdk::{ @@ -683,9 +684,18 @@ impl RemittanceSplit { } let split = Self::get_split(env); - let s0 = split.get(0).unwrap() as i128; - let s1 = split.get(1).unwrap() as i128; - let s2 = split.get(2).unwrap() as i128; + let s0 = match split.get(0) { + Some(v) => v as i128, + None => return Err(RemittanceSplitError::Overflow), + }; + let s1 = match split.get(1) { + Some(v) => v as i128, + None => return Err(RemittanceSplitError::Overflow), + }; + let s2 = match split.get(2) { + Some(v) => v as i128, + None => return Err(RemittanceSplitError::Overflow), + }; let spending = total_amount .checked_mul(s0) diff --git a/reporting/src/lib.rs b/reporting/src/lib.rs index fd4d262..9193de8 100644 --- a/reporting/src/lib.rs +++ b/reporting/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] use soroban_sdk::{ contract, contractclient, contractimpl, contracttype, symbol_short, Address, Env, Map, Vec, }; @@ -393,7 +394,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADDRS")) - .expect("Contract addresses not configured"); + .unwrap_or_else(|| panic!("Contract addresses not configured")); let split_client = RemittanceSplitClient::new(&env, &addresses.remittance_split); let split_percentages = split_client.get_split(); @@ -435,7 +436,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADDRS")) - .expect("Contract addresses not configured"); + .unwrap_or_else(|| panic!("Contract addresses not configured")); let savings_client = SavingsGoalsClient::new(&env, &addresses.savings_goals); let goals = savings_client.get_all_goals(&user); @@ -481,7 +482,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADDRS")) - .expect("Contract addresses not configured"); + .unwrap_or_else(|| panic!("Contract addresses not configured")); let bill_client = BillPaymentsClient::new(&env, &addresses.bill_payments); let all_bills = bill_client.get_all_bills(); @@ -552,7 +553,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADDRS")) - .expect("Contract addresses not configured"); + .unwrap_or_else(|| panic!("Contract addresses not configured")); let insurance_client = InsuranceClient::new(&env, &addresses.insurance); let policies = insurance_client.get_active_policies(&user); @@ -589,7 +590,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADDRS")) - .expect("Contract addresses not configured"); + .unwrap_or_else(|| panic!("Contract addresses not configured")); // Savings score (0-40 points) let savings_client = SavingsGoalsClient::new(&env, &addresses.savings_goals); @@ -778,7 +779,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADMIN")) - .expect("Contract not initialized"); + .unwrap_or_else(|| panic!("Contract not initialized")); if caller != admin { panic!("Only admin can archive reports"); @@ -879,7 +880,7 @@ impl ReportingContract { .storage() .instance() .get(&symbol_short!("ADMIN")) - .expect("Contract not initialized"); + .unwrap_or_else(|| panic!("Contract not initialized")); if caller != admin { panic!("Only admin can cleanup reports"); diff --git a/savings_goals/src/lib.rs b/savings_goals/src/lib.rs index b0e8992..cc693c5 100644 --- a/savings_goals/src/lib.rs +++ b/savings_goals/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))] use soroban_sdk::{ contract, contractimpl, contracttype, symbol_short, Address, Env, Map, String, Symbol, Vec, }; @@ -278,7 +279,7 @@ impl SavingsGoalContract { pub fn pause(env: Env, caller: Address) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -291,7 +292,7 @@ impl SavingsGoalContract { pub fn unpause(env: Env, caller: Address) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -311,7 +312,7 @@ impl SavingsGoalContract { pub fn pause_function(env: Env, caller: Address, func: Symbol) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -328,7 +329,7 @@ impl SavingsGoalContract { pub fn unpause_function(env: Env, caller: Address, func: Symbol) { caller.require_auth(); - let admin = Self::get_pause_admin(&env).expect("No pause admin set"); + let admin = Self::get_pause_admin(&env).ok_or(SavingsGoalsError::Unauthorized).unwrap(); if admin != caller { panic!("Unauthorized"); } @@ -377,7 +378,10 @@ impl SavingsGoalContract { pub fn set_version(env: Env, caller: Address, new_version: u32) { caller.require_auth(); - let admin = Self::get_upgrade_admin(&env).expect("No upgrade admin set"); + let admin = match Self::get_upgrade_admin(&env) { + Some(a) => a, + None => panic!("No upgrade admin set"), + }; if admin != caller { panic!("Unauthorized"); } @@ -580,7 +584,10 @@ impl SavingsGoalContract { if item.amount <= 0 { panic!("Amount must be positive"); } - let goal = goals_map.get(item.goal_id).expect("Goal not found"); + let goal = match goals_map.get(item.goal_id) { + Some(g) => g, + None => panic!("Goal not found"), + }; if goal.owner != caller { panic!("Not owner of all goals"); } @@ -593,14 +600,19 @@ impl SavingsGoalContract { .unwrap_or_else(|| Map::new(&env)); let mut count = 0u32; for item in contributions.iter() { - let mut goal = goals.get(item.goal_id).expect("Goal not found"); + let mut goal = match goals.get(item.goal_id) { + Some(g) => g, + None => panic!("Goal not found"), + }; if goal.owner != caller { panic!("Batch validation failed"); } - goal.current_amount = goal + goal.current_amount = match goal .current_amount - .checked_add(item.amount) - .expect("overflow"); + .checked_add(item.amount) { + Some(v) => v, + None => panic!("overflow"), + }; let new_total = goal.current_amount; let was_completed = new_total >= goal.target_amount; let previously_completed = (new_total - item.amount) >= goal.target_amount; @@ -1019,7 +1031,10 @@ impl SavingsGoalContract { fn increment_nonce(env: &Env, address: &Address) { let current = Self::get_nonce(env.clone(), address.clone()); - let next = current.checked_add(1).expect("nonce overflow"); + let next = match current.checked_add(1) { + Some(v) => v, + None => panic!("nonce overflow"), + }; let mut nonces: Map = env .storage() .instance() @@ -1156,7 +1171,10 @@ impl SavingsGoalContract { .get(&symbol_short!("GOALS")) .unwrap_or_else(|| Map::new(&env)); - let goal = goals.get(goal_id).expect("Goal not found"); + let goal = match goals.get(goal_id) { + Some(g) => g, + None => panic!("Goal not found"), + }; if goal.owner != owner { panic!("Only the goal owner can create schedules"); @@ -1319,10 +1337,12 @@ impl SavingsGoalContract { } if let Some(mut goal) = goals.get(schedule.goal_id) { - goal.current_amount = goal + goal.current_amount = match goal .current_amount - .checked_add(schedule.amount) - .expect("overflow"); + .checked_add(schedule.amount) { + Some(v) => v, + None => panic!("overflow"), + }; let is_completed = goal.current_amount >= goal.target_amount; goals.set(schedule.goal_id, goal.clone());