diff --git a/contracts/assetsup/src/detokenization.rs b/contracts/assetsup/src/detokenization.rs
index c6f2078..d297ce5 100644
--- a/contracts/assetsup/src/detokenization.rs
+++ b/contracts/assetsup/src/detokenization.rs
@@ -73,7 +73,8 @@ pub fn execute_detokenization(env: &Env, asset_id: u64, proposal_id: u64) -> Res
// Get list of all token holders before clearing
let holders_list_key = TokenDataKey::TokenHoldersList(asset_id);
- let holders = store.get::<_, soroban_sdk::Vec
>(&holders_list_key)
+ let holders = store
+ .get::<_, soroban_sdk::Vec>(&holders_list_key)
.ok_or(Error::AssetNotTokenized)?;
// Remove all token holder records
diff --git a/contracts/assetsup/src/lib.rs b/contracts/assetsup/src/lib.rs
index 8639201..f65ef03 100644
--- a/contracts/assetsup/src/lib.rs
+++ b/contracts/assetsup/src/lib.rs
@@ -822,7 +822,10 @@ impl AssetUpContract {
}
/// Get a specific policy
- pub fn get_insurance_policy(env: Env, policy_id: BytesN<32>) -> Option {
+ pub fn get_insurance_policy(
+ env: Env,
+ policy_id: BytesN<32>,
+ ) -> Option {
insurance::get_policy(env, policy_id)
}
diff --git a/contracts/assetsup/src/tests/admin.rs b/contracts/assetsup/src/tests/admin.rs
new file mode 100644
index 0000000..e6cbd37
--- /dev/null
+++ b/contracts/assetsup/src/tests/admin.rs
@@ -0,0 +1,105 @@
+use crate::tests::helpers::*;
+use soroban_sdk::{testutils::Address as _, Address};
+
+#[test]
+fn test_update_admin_success() {
+ let env = create_env();
+ let (admin, new_admin, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+ client.update_admin(&new_admin);
+
+ // Verify admin was updated
+ assert_eq!(client.get_admin(), new_admin);
+
+ // Verify new admin is authorized registrar
+ assert!(client.is_authorized_registrar(&new_admin));
+
+ // Verify old admin is no longer authorized registrar
+ assert!(!client.is_authorized_registrar(&admin));
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #39)")]
+fn test_update_admin_zero_address() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let zero_address = Address::from_string(&soroban_sdk::String::from_str(
+ &env,
+ "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
+ ));
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidOwnerAddress error
+ client.update_admin(&zero_address);
+}
+
+#[test]
+fn test_pause_unpause_contract() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Initially not paused
+ assert!(!client.is_paused());
+
+ // Pause contract
+ client.pause_contract();
+ assert!(client.is_paused());
+
+ // Unpause contract
+ client.unpause_contract();
+ assert!(!client.is_paused());
+}
+
+#[test]
+fn test_add_authorized_registrar() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Initially user1 is not authorized
+ assert!(!client.is_authorized_registrar(&user1));
+
+ // Add user1 as authorized registrar
+ client.add_authorized_registrar(&user1);
+ assert!(client.is_authorized_registrar(&user1));
+}
+
+#[test]
+fn test_remove_authorized_registrar() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Add user1 as authorized registrar
+ client.add_authorized_registrar(&user1);
+ assert!(client.is_authorized_registrar(&user1));
+
+ // Remove user1 from authorized registrars
+ client.remove_authorized_registrar(&user1);
+ assert!(!client.is_authorized_registrar(&user1));
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_remove_admin_from_registrars() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Should panic with Unauthorized error - cannot remove admin
+ client.remove_authorized_registrar(&admin);
+}
diff --git a/contracts/assetsup/src/tests/asset.rs b/contracts/assetsup/src/tests/asset.rs
new file mode 100644
index 0000000..79872cb
--- /dev/null
+++ b/contracts/assetsup/src/tests/asset.rs
@@ -0,0 +1,340 @@
+use crate::tests::helpers::*;
+use crate::types::AssetStatus;
+use soroban_sdk::{testutils::Address as _, Address, String, Vec};
+
+#[test]
+fn test_register_asset_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // Verify asset was registered
+ let stored_asset = client.get_asset(&asset_id);
+ assert_eq!(stored_asset.id, asset_id);
+ assert_eq!(stored_asset.owner, user1);
+
+ // Verify total asset count increased
+ assert_eq!(client.get_total_asset_count(), 1);
+
+ // Verify asset is in owner's registry
+ let owner_assets = client.get_assets_by_owner(&user1);
+ assert_eq!(owner_assets.len(), 1);
+ assert_eq!(owner_assets.get(0).unwrap(), asset_id);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #3)")]
+fn test_register_asset_already_exists() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // Try to register same asset again - should panic with AssetAlreadyExists
+ client.register_asset(&asset, &admin);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #34)")]
+fn test_register_asset_when_paused() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Pause contract
+ client.pause_contract();
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id);
+
+ // Should panic with ContractPaused error
+ client.register_asset(&asset, &admin);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_register_asset_unauthorized() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id);
+
+ env.mock_all_auths();
+
+ // user2 is not authorized registrar - should panic with Unauthorized
+ client.register_asset(&asset, &user2);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #36)")]
+fn test_register_asset_invalid_name_too_short() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let mut asset = create_test_asset(&env, &user1, asset_id);
+ asset.name = String::from_str(&env, "AB"); // Too short (< 3 chars)
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidAssetName error
+ client.register_asset(&asset, &admin);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #37)")]
+fn test_register_asset_invalid_purchase_value() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let mut asset = create_test_asset(&env, &user1, asset_id);
+ asset.purchase_value = -100; // Negative value
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidPurchaseValue error
+ client.register_asset(&asset, &admin);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #39)")]
+fn test_register_asset_zero_owner() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let zero_address = Address::from_string(&String::from_str(
+ &env,
+ "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
+ ));
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &zero_address, asset_id);
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidOwnerAddress error
+ client.register_asset(&asset, &admin);
+}
+
+#[test]
+fn test_update_asset_metadata_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // Update metadata
+ let new_description = Some(String::from_str(&env, "Updated description"));
+ let new_uri = Some(String::from_str(&env, "ipfs://QmUpdated123"));
+
+ client.update_asset_metadata(&asset_id, &new_description, &new_uri, &None, &user1);
+
+ // Verify metadata was updated (just check it doesn't error)
+ let updated_asset = client.get_asset(&asset_id);
+ assert!(!updated_asset.description.is_empty());
+ assert!(!updated_asset.metadata_uri.is_empty());
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #4)")]
+fn test_update_asset_metadata_not_found() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 999);
+ let new_description = Some(String::from_str(&env, "Updated"));
+
+ env.mock_all_auths();
+
+ // Should panic with AssetNotFound error
+ client.update_asset_metadata(&asset_id, &new_description, &None, &None, &user1);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_update_asset_metadata_unauthorized() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ let new_description = Some(String::from_str(&env, "Hacked"));
+
+ // user2 is not owner or admin - should panic with Unauthorized
+ client.update_asset_metadata(&asset_id, &new_description, &None, &None, &user2);
+}
+
+#[test]
+fn test_transfer_asset_ownership_success() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // Transfer ownership
+ client.transfer_asset_ownership(&asset_id, &user2, &user1);
+
+ // Verify ownership was transferred
+ let transferred_asset = client.get_asset(&asset_id);
+ assert_eq!(transferred_asset.owner, user2);
+ assert_eq!(transferred_asset.status, AssetStatus::Transferred);
+
+ // Verify asset is in new owner's registry
+ let user2_assets = client.get_assets_by_owner(&user2);
+ assert_eq!(user2_assets.len(), 1);
+
+ // Verify asset is removed from old owner's registry
+ let user1_assets = client.get_assets_by_owner(&user1);
+ assert_eq!(user1_assets.len(), 0);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_transfer_asset_ownership_unauthorized() {
+ let env = create_env();
+ let (admin, user1, user2, user3) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // user3 is not owner - should panic with Unauthorized
+ client.transfer_asset_ownership(&asset_id, &user2, &user3);
+}
+
+#[test]
+fn test_retire_asset_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // Retire asset
+ client.retire_asset(&asset_id, &user1);
+
+ // Verify asset was retired
+ let retired_asset = client.get_asset(&asset_id);
+ assert_eq!(retired_asset.status, AssetStatus::Retired);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_retire_asset_unauthorized() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ // user2 is not owner or admin - should panic with Unauthorized
+ client.retire_asset(&asset_id, &user2);
+}
+
+#[test]
+fn test_check_asset_exists() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+
+ // Asset doesn't exist yet
+ assert!(!client.check_asset_exists(&asset_id));
+
+ // Register asset
+ client.register_asset(&asset, &admin);
+
+ // Asset now exists
+ assert!(client.check_asset_exists(&asset_id));
+}
+
+#[test]
+fn test_get_asset_info() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &user1, asset_id.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset, &admin);
+
+ let info = client.get_asset_info(&asset_id);
+ assert_eq!(info.id, asset_id);
+ assert_eq!(info.owner, user1);
+ assert_eq!(info.status, AssetStatus::Active);
+}
+
+#[test]
+fn test_batch_get_asset_info() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id1 = generate_asset_id(&env, 1);
+ let asset_id2 = generate_asset_id(&env, 2);
+ let asset1 = create_test_asset(&env, &user1, asset_id1.clone());
+ let asset2 = create_test_asset(&env, &user1, asset_id2.clone());
+
+ env.mock_all_auths();
+ client.register_asset(&asset1, &admin);
+ client.register_asset(&asset2, &admin);
+
+ let mut ids = Vec::new(&env);
+ ids.push_back(asset_id1.clone());
+ ids.push_back(asset_id2.clone());
+
+ let infos = client.batch_get_asset_info(&ids);
+ assert_eq!(infos.len(), 2);
+}
diff --git a/contracts/assetsup/src/tests/detokenization.rs b/contracts/assetsup/src/tests/detokenization.rs
new file mode 100644
index 0000000..0657603
--- /dev/null
+++ b/contracts/assetsup/src/tests/detokenization.rs
@@ -0,0 +1,235 @@
+use crate::tests::helpers::*;
+use crate::types::{AssetType, DetokenizationProposal};
+use soroban_sdk::String;
+
+#[test]
+fn test_propose_detokenization_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Propose detokenization
+ let proposal_id = client.propose_detokenization(&1u64, &user1);
+
+ assert_eq!(proposal_id, 1);
+
+ // Verify proposal is active
+ assert!(client.is_detokenization_active(&1u64));
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #29)")]
+fn test_propose_detokenization_already_proposed() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.propose_detokenization(&1u64, &user1);
+
+ // Try to propose again - should panic with DetokenizationAlreadyProposed
+ client.propose_detokenization(&1u64, &user1);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #11)")]
+fn test_propose_detokenization_not_tokenized() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Should panic with AssetNotTokenized error
+ client.propose_detokenization(&999u64, &user1);
+}
+
+#[test]
+fn test_execute_detokenization_success() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Transfer 60% to user2
+ client.transfer_tokens(&1u64, &user1, &user2, &600000i128);
+
+ // Propose detokenization
+ let proposal_id = client.propose_detokenization(&1u64, &user1);
+
+ // Vote with majority
+ client.cast_vote(&1u64, &proposal_id, &user2);
+
+ // Execute detokenization
+ client.execute_detokenization(&1u64, &proposal_id);
+
+ // Verify asset is no longer tokenized
+ assert!(!client.is_detokenization_active(&1u64));
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #28)")]
+fn test_execute_detokenization_not_approved() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Transfer 30% to user2 (not enough for majority)
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+
+ // Propose detokenization
+ let proposal_id = client.propose_detokenization(&1u64, &user1);
+
+ // Vote with minority
+ client.cast_vote(&1u64, &proposal_id, &user2);
+
+ // Should panic with DetokenizationNotApproved error
+ client.execute_detokenization(&1u64, &proposal_id);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #24)")]
+fn test_execute_detokenization_no_proposal() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Should panic with InvalidProposal error
+ client.execute_detokenization(&1u64, &1u64);
+}
+
+#[test]
+fn test_get_detokenization_proposal() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ let proposal_id = client.propose_detokenization(&1u64, &user1);
+
+ let proposal = client.get_detokenization_proposal(&1u64);
+
+ match proposal {
+ DetokenizationProposal::Active(active) => {
+ assert_eq!(active.proposal_id, proposal_id);
+ assert_eq!(active.proposer, user1);
+ }
+ _ => panic!("Expected Active proposal"),
+ }
+}
+
+#[test]
+fn test_detokenization_clears_all_data() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Set up some data
+ client.transfer_tokens(&1u64, &user1, &user2, &600000i128);
+ client.add_to_whitelist(&1u64, &user2);
+ client.enable_revenue_sharing(&1u64);
+
+ // Propose and execute detokenization
+ let proposal_id = client.propose_detokenization(&1u64, &user1);
+ client.cast_vote(&1u64, &proposal_id, &user2);
+ client.execute_detokenization(&1u64, &proposal_id);
+
+ // Verify whitelist is cleared
+ let whitelist = client.get_whitelist(&1u64);
+ assert_eq!(whitelist.len(), 0);
+}
diff --git a/contracts/assetsup/src/tests/detokenization_new.rs b/contracts/assetsup/src/tests/detokenization_new.rs
index ba7a0b9..cea89e1 100644
--- a/contracts/assetsup/src/tests/detokenization_new.rs
+++ b/contracts/assetsup/src/tests/detokenization_new.rs
@@ -232,7 +232,12 @@ fn test_token_elimination_on_execution() {
let holders_result = tokenization::get_token_holders(&env, asset_id);
let holders_cleared = holders_result.is_err();
- (before_exists, after_exists, balance_cleared, holders_cleared)
+ (
+ before_exists,
+ after_exists,
+ balance_cleared,
+ holders_cleared,
+ )
});
// Assert asset existed before
diff --git a/contracts/assetsup/src/tests/dividends.rs b/contracts/assetsup/src/tests/dividends.rs
new file mode 100644
index 0000000..dae3f9e
--- /dev/null
+++ b/contracts/assetsup/src/tests/dividends.rs
@@ -0,0 +1,245 @@
+use crate::tests::helpers::*;
+use crate::types::AssetType;
+use soroban_sdk::String;
+
+#[test]
+fn test_enable_revenue_sharing() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Initially disabled
+ let asset = client.get_tokenized_asset(&1u64);
+ assert!(!asset.revenue_sharing_enabled);
+
+ // Enable revenue sharing
+ client.enable_revenue_sharing(&1u64);
+
+ let asset = client.get_tokenized_asset(&1u64);
+ assert!(asset.revenue_sharing_enabled);
+}
+
+#[test]
+fn test_disable_revenue_sharing() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.enable_revenue_sharing(&1u64);
+ client.disable_revenue_sharing(&1u64);
+
+ let asset = client.get_tokenized_asset(&1u64);
+ assert!(!asset.revenue_sharing_enabled);
+}
+
+#[test]
+fn test_distribute_dividends_success() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Enable revenue sharing
+ client.enable_revenue_sharing(&1u64);
+
+ // Transfer 30% to user2
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+
+ // Distribute 10000 in dividends
+ client.distribute_dividends(&1u64, &10000i128);
+
+ // Check unclaimed dividends
+ let unclaimed1 = client.get_unclaimed_dividends(&1u64, &user1);
+ let unclaimed2 = client.get_unclaimed_dividends(&1u64, &user2);
+
+ assert_eq!(unclaimed1, 7000); // 70% of 10000
+ assert_eq!(unclaimed2, 3000); // 30% of 10000
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #27)")]
+fn test_distribute_dividends_invalid_amount() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.enable_revenue_sharing(&1u64);
+
+ // Should panic with InvalidDividendAmount error
+ client.distribute_dividends(&1u64, &0i128);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #27)")]
+fn test_distribute_dividends_not_enabled() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Revenue sharing not enabled - should panic with InvalidDividendAmount
+ client.distribute_dividends(&1u64, &10000i128);
+}
+
+#[test]
+fn test_claim_dividends_success() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.enable_revenue_sharing(&1u64);
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+ client.distribute_dividends(&1u64, &10000i128);
+
+ // Claim dividends
+ let claimed = client.claim_dividends(&1u64, &user2);
+ assert_eq!(claimed, 3000);
+
+ // After claiming, unclaimed should be 0
+ let unclaimed = client.get_unclaimed_dividends(&1u64, &user2);
+ assert_eq!(unclaimed, 0);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #26)")]
+fn test_claim_dividends_none_to_claim() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Should panic with NoDividendsToClaim error
+ client.claim_dividends(&1u64, &user1);
+}
+
+#[test]
+fn test_multiple_dividend_distributions() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.enable_revenue_sharing(&1u64);
+ client.transfer_tokens(&1u64, &user1, &user2, &500000i128);
+
+ // First distribution
+ client.distribute_dividends(&1u64, &10000i128);
+
+ // Second distribution
+ client.distribute_dividends(&1u64, &5000i128);
+
+ // Total unclaimed should be sum of both distributions
+ let unclaimed1 = client.get_unclaimed_dividends(&1u64, &user1);
+ let unclaimed2 = client.get_unclaimed_dividends(&1u64, &user2);
+
+ assert_eq!(unclaimed1, 7500); // 50% of 15000
+ assert_eq!(unclaimed2, 7500); // 50% of 15000
+}
diff --git a/contracts/assetsup/src/tests/helpers.rs b/contracts/assetsup/src/tests/helpers.rs
new file mode 100644
index 0000000..66ad205
--- /dev/null
+++ b/contracts/assetsup/src/tests/helpers.rs
@@ -0,0 +1,154 @@
+use crate::asset::Asset;
+use crate::insurance::{ClaimStatus, InsuranceClaim, InsurancePolicy, PolicyStatus, PolicyType};
+use crate::types::{AssetStatus, AssetType, CustomAttribute, TokenMetadata};
+use crate::{AssetUpContract, AssetUpContractClient};
+use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, String, Vec};
+
+/// Create a fresh test environment
+pub fn create_env() -> Env {
+ Env::default()
+}
+
+/// Create mock addresses for testing
+pub fn create_mock_addresses(env: &Env) -> (Address, Address, Address, Address) {
+ let admin = Address::generate(env);
+ let user1 = Address::generate(env);
+ let user2 = Address::generate(env);
+ let user3 = Address::generate(env);
+ (admin, user1, user2, user3)
+}
+
+/// Initialize contract with admin
+pub fn initialize_contract<'a>(env: &'a Env, admin: &Address) -> AssetUpContractClient<'a> {
+ let contract_id = env.register(AssetUpContract, ());
+ let client = AssetUpContractClient::new(env, &contract_id);
+
+ env.mock_all_auths();
+ client.initialize(admin);
+ client
+}
+
+/// Create a test asset
+pub fn create_test_asset(env: &Env, owner: &Address, id: BytesN<32>) -> Asset {
+ let timestamp = env.ledger().timestamp();
+
+ Asset {
+ id,
+ name: String::from_str(env, "Test Asset"),
+ description: String::from_str(env, "A test asset for unit testing"),
+ category: String::from_str(env, "Electronics"),
+ owner: owner.clone(),
+ registration_timestamp: timestamp,
+ last_transfer_timestamp: timestamp,
+ status: AssetStatus::Active,
+ metadata_uri: String::from_str(env, "ipfs://QmTest123456789"),
+ purchase_value: 1000,
+ custom_attributes: Vec::new(env),
+ }
+}
+
+/// Create a test asset with custom attributes
+#[allow(dead_code)]
+pub fn create_test_asset_with_attributes(
+ env: &Env,
+ owner: &Address,
+ id: BytesN<32>,
+ name: &str,
+ value: i128,
+) -> Asset {
+ let timestamp = env.ledger().timestamp();
+ let mut attributes = Vec::new(env);
+ attributes.push_back(CustomAttribute {
+ key: String::from_str(env, "serial_number"),
+ value: String::from_str(env, "SN123456"),
+ });
+
+ Asset {
+ id,
+ name: String::from_str(env, name),
+ description: String::from_str(env, "Test asset with attributes"),
+ category: String::from_str(env, "Equipment"),
+ owner: owner.clone(),
+ registration_timestamp: timestamp,
+ last_transfer_timestamp: timestamp,
+ status: AssetStatus::Active,
+ metadata_uri: String::from_str(env, "ipfs://QmTestWithAttrs"),
+ purchase_value: value,
+ custom_attributes: attributes,
+ }
+}
+
+/// Generate a unique asset ID
+pub fn generate_asset_id(env: &Env, seed: u32) -> BytesN<32> {
+ let mut bytes = [0u8; 32];
+ bytes[0] = (seed >> 24) as u8;
+ bytes[1] = (seed >> 16) as u8;
+ bytes[2] = (seed >> 8) as u8;
+ bytes[3] = seed as u8;
+ BytesN::from_array(env, &bytes)
+}
+
+/// Create token metadata for testing
+#[allow(dead_code)]
+pub fn create_test_token_metadata(env: &Env) -> TokenMetadata {
+ TokenMetadata {
+ name: String::from_str(env, "Test Token"),
+ description: String::from_str(env, "Test tokenized asset"),
+ asset_type: AssetType::Physical,
+ ipfs_uri: Some(String::from_str(env, "ipfs://QmTokenMetadata")),
+ legal_docs_hash: None,
+ valuation_report_hash: None,
+ accredited_investor_required: false,
+ geographic_restrictions: Vec::new(env),
+ }
+}
+
+/// Create a test insurance policy
+pub fn create_test_policy(
+ env: &Env,
+ policy_id: BytesN<32>,
+ holder: &Address,
+ insurer: &Address,
+ asset_id: BytesN<32>,
+) -> InsurancePolicy {
+ let current_time = env.ledger().timestamp();
+
+ InsurancePolicy {
+ policy_id,
+ holder: holder.clone(),
+ insurer: insurer.clone(),
+ asset_id,
+ policy_type: PolicyType::Property,
+ coverage_amount: 10000,
+ deductible: 500,
+ premium: 100,
+ start_date: current_time,
+ end_date: current_time + 31536000, // 1 year
+ status: PolicyStatus::Active,
+ auto_renew: false,
+ last_payment: current_time,
+ }
+}
+
+/// Create a test insurance claim
+#[allow(dead_code)]
+pub fn create_test_claim(
+ env: &Env,
+ claim_id: BytesN<32>,
+ policy_id: BytesN<32>,
+ asset_id: BytesN<32>,
+ claimant: &Address,
+) -> InsuranceClaim {
+ let current_time = env.ledger().timestamp();
+
+ InsuranceClaim {
+ claim_id,
+ policy_id,
+ asset_id,
+ claimant: claimant.clone(),
+ amount: 5000,
+ status: ClaimStatus::Submitted,
+ filed_at: current_time,
+ approved_amount: 0,
+ }
+}
diff --git a/contracts/assetsup/src/tests/initialization.rs b/contracts/assetsup/src/tests/initialization.rs
new file mode 100644
index 0000000..4430527
--- /dev/null
+++ b/contracts/assetsup/src/tests/initialization.rs
@@ -0,0 +1,80 @@
+use crate::tests::helpers::*;
+use crate::{AssetUpContract, AssetUpContractClient};
+use soroban_sdk::{testutils::Address as _, Address};
+
+#[test]
+fn test_initialize_success() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+
+ let contract_id = env.register(AssetUpContract, ());
+ let client = AssetUpContractClient::new(&env, &contract_id);
+
+ env.mock_all_auths();
+ client.initialize(&admin);
+
+ // Verify admin is set
+ let stored_admin = client.get_admin();
+ assert_eq!(stored_admin, admin);
+
+ // Verify contract is not paused
+ assert!(!client.is_paused());
+
+ // Verify total asset count is 0
+ assert_eq!(client.get_total_asset_count(), 0);
+
+ // Verify admin is authorized registrar
+ assert!(client.is_authorized_registrar(&admin));
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #1)")]
+fn test_initialize_already_initialized() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+
+ let contract_id = env.register(AssetUpContract, ());
+ let client = AssetUpContractClient::new(&env, &contract_id);
+
+ env.mock_all_auths();
+ client.initialize(&admin);
+
+ // Try to initialize again - should panic with AlreadyInitialized error
+ client.initialize(&admin);
+}
+
+#[test]
+fn test_get_contract_metadata() {
+ let env = create_env();
+ let admin = Address::generate(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let metadata = client.get_contract_metadata();
+ // Just verify metadata exists and has expected structure
+ assert!(!metadata.version.is_empty());
+ assert!(!metadata.name.is_empty());
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #35)")]
+fn test_get_contract_metadata_not_initialized() {
+ let env = create_env();
+
+ let contract_id = env.register(AssetUpContract, ());
+ let client = AssetUpContractClient::new(&env, &contract_id);
+
+ // Should panic with ContractNotInitialized error
+ client.get_contract_metadata();
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #2)")]
+fn test_get_admin_not_found() {
+ let env = create_env();
+
+ let contract_id = env.register(AssetUpContract, ());
+ let client = AssetUpContractClient::new(&env, &contract_id);
+
+ // Should panic with AdminNotFound error
+ client.get_admin();
+}
diff --git a/contracts/assetsup/src/tests/insurance.rs b/contracts/assetsup/src/tests/insurance.rs
new file mode 100644
index 0000000..677982b
--- /dev/null
+++ b/contracts/assetsup/src/tests/insurance.rs
@@ -0,0 +1,338 @@
+use crate::insurance::PolicyStatus;
+use crate::tests::helpers::*;
+use soroban_sdk::testutils::Ledger;
+
+#[test]
+fn test_create_insurance_policy_success() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Verify policy was created
+ let stored_policy = client.get_insurance_policy(&policy_id);
+ assert!(stored_policy.is_some());
+
+ let stored = stored_policy.unwrap();
+ assert_eq!(stored.policy_id, policy_id);
+ assert_eq!(stored.holder, user1);
+ assert_eq!(stored.status, PolicyStatus::Active);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #3)")]
+fn test_create_insurance_policy_already_exists() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Try to create again - should panic with AssetAlreadyExists
+ client.create_insurance_policy(&policy);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #9)")]
+fn test_create_insurance_policy_invalid_coverage() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let mut policy = create_test_policy(&env, policy_id, &user1, &insurer, asset_id);
+
+ // Invalid: deductible >= coverage_amount
+ policy.deductible = 10000;
+ policy.coverage_amount = 10000;
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidPayment error
+ client.create_insurance_policy(&policy);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #9)")]
+fn test_create_insurance_policy_invalid_dates() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let mut policy = create_test_policy(&env, policy_id, &user1, &insurer, asset_id);
+
+ // Invalid: start_date >= end_date
+ policy.start_date = 1000;
+ policy.end_date = 1000;
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidPayment error
+ client.create_insurance_policy(&policy);
+}
+
+#[test]
+fn test_cancel_insurance_policy_by_holder() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Cancel by holder
+ client.cancel_insurance_policy(&policy_id, &user1);
+
+ // Verify policy was cancelled
+ let stored_policy = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored_policy.status, PolicyStatus::Cancelled);
+}
+
+#[test]
+fn test_cancel_insurance_policy_by_insurer() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Cancel by insurer
+ client.cancel_insurance_policy(&policy_id, &insurer);
+
+ // Verify policy was cancelled
+ let stored_policy = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored_policy.status, PolicyStatus::Cancelled);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_cancel_insurance_policy_unauthorized() {
+ let env = create_env();
+ let (admin, user1, insurer, user3) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // user3 is neither holder nor insurer - should panic with Unauthorized
+ client.cancel_insurance_policy(&policy_id, &user3);
+}
+
+#[test]
+fn test_suspend_insurance_policy() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Suspend policy
+ client.suspend_insurance_policy(&policy_id, &insurer);
+
+ // Verify policy was suspended
+ let stored_policy = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored_policy.status, PolicyStatus::Suspended);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_suspend_insurance_policy_unauthorized() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Holder cannot suspend - should panic with Unauthorized
+ client.suspend_insurance_policy(&policy_id, &user1);
+}
+
+#[test]
+fn test_expire_insurance_policy() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let mut policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ // Set current time to 5000
+ env.ledger().with_mut(|li| li.timestamp = 5000);
+
+ // Set dates: start now, end in 1000 seconds
+ policy.start_date = 5000;
+ policy.end_date = 6000;
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Advance time past end_date
+ env.ledger().with_mut(|li| li.timestamp = 7000);
+
+ // Expire policy (permissionless)
+ client.expire_insurance_policy(&policy_id);
+
+ // Verify policy was expired
+ let stored_policy = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored_policy.status, PolicyStatus::Expired);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_expire_insurance_policy_not_yet_expired() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Should panic with Unauthorized error - end date hasn't passed
+ client.expire_insurance_policy(&policy_id);
+}
+
+#[test]
+fn test_renew_insurance_policy() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ // Renew policy
+ let new_end_date = env.ledger().timestamp() + 63072000; // 2 years
+ let new_premium = 150i128;
+
+ client.renew_insurance_policy(&policy_id, &new_end_date, &new_premium, &insurer);
+
+ // Verify policy was renewed
+ let stored_policy = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored_policy.end_date, new_end_date);
+ assert_eq!(stored_policy.premium, new_premium);
+ assert_eq!(stored_policy.status, PolicyStatus::Active);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_renew_insurance_policy_unauthorized() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy);
+
+ let new_end_date = env.ledger().timestamp() + 63072000;
+ let new_premium = 150i128;
+
+ // Holder cannot renew - should panic with Unauthorized
+ client.renew_insurance_policy(&policy_id, &new_end_date, &new_premium, &user1);
+}
+
+#[test]
+fn test_get_asset_insurance_policies() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let asset_id = generate_asset_id(&env, 100);
+ let policy_id1 = generate_asset_id(&env, 1);
+ let policy_id2 = generate_asset_id(&env, 2);
+
+ let policy1 = create_test_policy(&env, policy_id1.clone(), &user1, &insurer, asset_id.clone());
+ let policy2 = create_test_policy(&env, policy_id2.clone(), &user1, &insurer, asset_id.clone());
+
+ env.mock_all_auths();
+ client.create_insurance_policy(&policy1);
+ client.create_insurance_policy(&policy2);
+
+ // Get all policies for asset
+ let policies = client.get_asset_insurance_policies(&asset_id);
+ assert_eq!(policies.len(), 2);
+}
+
+#[test]
+fn test_policy_lifecycle_full() {
+ let env = create_env();
+ let (admin, user1, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ let policy_id = generate_asset_id(&env, 1);
+ let asset_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &user1, &insurer, asset_id);
+
+ env.mock_all_auths();
+
+ // Create
+ client.create_insurance_policy(&policy);
+ let stored = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored.status, PolicyStatus::Active);
+
+ // Renew while active
+ let new_end_date = env.ledger().timestamp() + 63072000;
+ client.renew_insurance_policy(&policy_id, &new_end_date, &150i128, &insurer);
+ let stored = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored.status, PolicyStatus::Active);
+ assert_eq!(stored.premium, 150);
+
+ // Suspend
+ client.suspend_insurance_policy(&policy_id, &insurer);
+ let stored = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored.status, PolicyStatus::Suspended);
+
+ // Cancel from suspended state
+ client.cancel_insurance_policy(&policy_id, &user1);
+ let stored = client.get_insurance_policy(&policy_id).unwrap();
+ assert_eq!(stored.status, PolicyStatus::Cancelled);
+}
diff --git a/contracts/assetsup/src/tests/insurance_new.rs b/contracts/assetsup/src/tests/insurance_new.rs
index 6f98605..d2f3b9a 100644
--- a/contracts/assetsup/src/tests/insurance_new.rs
+++ b/contracts/assetsup/src/tests/insurance_new.rs
@@ -328,7 +328,8 @@ fn test_renew_expired_policy() {
li.timestamp = 1000;
});
- let mut policy = create_test_policy(&env, policy_id.clone(), holder, insurer.clone(), asset_id);
+ let mut policy =
+ create_test_policy(&env, policy_id.clone(), holder, insurer.clone(), asset_id);
// Set policy to expire at timestamp 2000
policy.start_date = 1000;
policy.end_date = 2000;
@@ -343,14 +344,9 @@ fn test_renew_expired_policy() {
insurance::expire_policy(env.clone(), policy_id.clone()).unwrap();
// Now renew it to timestamp 3500
- let renew_result = insurance::renew_policy(
- env.clone(),
- policy_id.clone(),
- 3500,
- 1500,
- insurer.clone(),
- )
- .is_ok();
+ let renew_result =
+ insurance::renew_policy(env.clone(), policy_id.clone(), 3500, 1500, insurer.clone())
+ .is_ok();
let final_policy = insurance::get_policy(env.clone(), policy_id.clone()).unwrap();
@@ -409,8 +405,13 @@ fn test_status_transition_validation() {
let (suspend_ok, cancel_after_suspend_ok, suspend_cancelled_err) =
env.as_contract(&contract_id, || {
- let policy =
- create_test_policy(&env, policy_id.clone(), holder.clone(), insurer.clone(), asset_id);
+ let policy = create_test_policy(
+ &env,
+ policy_id.clone(),
+ holder.clone(),
+ insurer.clone(),
+ asset_id,
+ );
insurance::create_policy(env.clone(), policy).unwrap();
// Active -> Suspended (should work)
diff --git a/contracts/assetsup/src/tests/integration_full.rs b/contracts/assetsup/src/tests/integration_full.rs
new file mode 100644
index 0000000..a3527da
--- /dev/null
+++ b/contracts/assetsup/src/tests/integration_full.rs
@@ -0,0 +1,255 @@
+use crate::tests::helpers::*;
+use crate::types::AssetType;
+use soroban_sdk::String;
+
+#[test]
+fn test_full_asset_tokenization_workflow() {
+ let env = create_env();
+ let (admin, owner, investor1, investor2) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Step 1: Register asset
+ let asset_id_bytes = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &owner, asset_id_bytes.clone());
+ client.register_asset(&asset, &admin);
+
+ // Step 2: Tokenize asset
+ let asset_id = 1u64;
+ client.tokenize_asset(
+ &asset_id,
+ &String::from_str(&env, "PROP"),
+ &1000000i128,
+ &6u32,
+ &1000i128,
+ &owner,
+ &String::from_str(&env, "Property Token"),
+ &String::from_str(&env, "Tokenized real estate"),
+ &AssetType::Physical,
+ );
+
+ // Step 3: Distribute tokens to investors
+ client.transfer_tokens(&asset_id, &owner, &investor1, &400000i128);
+ client.transfer_tokens(&asset_id, &owner, &investor2, &300000i128);
+
+ // Verify ownership distribution
+ assert_eq!(client.get_token_balance(&asset_id, &owner), 300000);
+ assert_eq!(client.get_token_balance(&asset_id, &investor1), 400000);
+ assert_eq!(client.get_token_balance(&asset_id, &investor2), 300000);
+
+ // Step 4: Enable revenue sharing and distribute dividends
+ client.enable_revenue_sharing(&asset_id);
+ client.distribute_dividends(&asset_id, &10000i128);
+
+ // Verify dividend distribution
+ assert_eq!(client.get_unclaimed_dividends(&asset_id, &owner), 3000);
+ assert_eq!(client.get_unclaimed_dividends(&asset_id, &investor1), 4000);
+ assert_eq!(client.get_unclaimed_dividends(&asset_id, &investor2), 3000);
+
+ // Step 5: Claim dividends
+ let claimed = client.claim_dividends(&asset_id, &investor1);
+ assert_eq!(claimed, 4000);
+ assert_eq!(client.get_unclaimed_dividends(&asset_id, &investor1), 0);
+}
+
+#[test]
+fn test_governance_and_detokenization_workflow() {
+ let env = create_env();
+ let (admin, owner, investor1, investor2) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Setup: Tokenize asset
+ let asset_id = 1u64;
+ client.tokenize_asset(
+ &asset_id,
+ &String::from_str(&env, "GOV"),
+ &1000000i128,
+ &6u32,
+ &1000i128,
+ &owner,
+ &String::from_str(&env, "Governance Token"),
+ &String::from_str(&env, "Token with voting"),
+ &AssetType::Physical,
+ );
+
+ // Distribute tokens
+ client.transfer_tokens(&asset_id, &owner, &investor1, &600000i128);
+ client.transfer_tokens(&asset_id, &owner, &investor2, &200000i128);
+
+ // Propose detokenization
+ let proposal_id = client.propose_detokenization(&asset_id, &owner);
+
+ // Vote on proposal
+ client.cast_vote(&asset_id, &proposal_id, &investor1);
+
+ // Check if proposal passed
+ assert!(client.proposal_passed(&asset_id, &proposal_id));
+
+ // Execute detokenization
+ client.execute_detokenization(&asset_id, &proposal_id);
+
+ // Verify asset is detokenized
+ assert!(!client.is_detokenization_active(&asset_id));
+}
+
+#[test]
+fn test_transfer_restrictions_workflow() {
+ let env = create_env();
+ let (admin, owner, investor1, investor2) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Setup: Tokenize asset
+ let asset_id = 1u64;
+ client.tokenize_asset(
+ &asset_id,
+ &String::from_str(&env, "REST"),
+ &1000000i128,
+ &6u32,
+ &1000i128,
+ &owner,
+ &String::from_str(&env, "Restricted Token"),
+ &String::from_str(&env, "Token with restrictions"),
+ &AssetType::Physical,
+ );
+
+ // Set transfer restrictions
+ client.set_transfer_restriction(&asset_id, &true);
+
+ // Add investor1 to whitelist
+ client.add_to_whitelist(&asset_id, &investor1);
+
+ // Transfer to whitelisted address should succeed
+ client.transfer_tokens(&asset_id, &owner, &investor1, &100000i128);
+ assert_eq!(client.get_token_balance(&asset_id, &investor1), 100000);
+
+ // Verify whitelist
+ assert!(client.is_whitelisted(&asset_id, &investor1));
+ assert!(!client.is_whitelisted(&asset_id, &investor2));
+}
+
+#[test]
+fn test_multi_asset_management() {
+ let env = create_env();
+ let (admin, owner, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Register multiple assets
+ for i in 1..=5 {
+ let asset_id = generate_asset_id(&env, i);
+ let asset = create_test_asset(&env, &owner, asset_id);
+ client.register_asset(&asset, &admin);
+ }
+
+ // Verify total count
+ assert_eq!(client.get_total_asset_count(), 5);
+
+ // Verify owner has all assets
+ let owner_assets = client.get_assets_by_owner(&owner);
+ assert_eq!(owner_assets.len(), 5);
+}
+
+#[test]
+fn test_token_locking_workflow() {
+ let env = create_env();
+ let (admin, owner, investor, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Setup: Tokenize and transfer
+ let asset_id = 1u64;
+ client.tokenize_asset(
+ &asset_id,
+ &String::from_str(&env, "LOCK"),
+ &1000000i128,
+ &6u32,
+ &1000i128,
+ &owner,
+ &String::from_str(&env, "Lockable Token"),
+ &String::from_str(&env, "Token with locking"),
+ &AssetType::Physical,
+ );
+
+ client.transfer_tokens(&asset_id, &owner, &investor, &500000i128);
+
+ // Lock investor's tokens
+ let lock_until = env.ledger().timestamp() + 1000;
+ client.lock_tokens(&asset_id, &investor, &lock_until, &owner);
+
+ // Verify tokens are locked
+ assert!(client.is_tokens_locked(&asset_id, &investor));
+
+ // Unlock tokens
+ client.unlock_tokens(&asset_id, &investor);
+
+ // Verify tokens are unlocked
+ assert!(!client.is_tokens_locked(&asset_id, &investor));
+
+ // Transfer should now succeed
+ client.transfer_tokens(&asset_id, &investor, &owner, &100000i128);
+}
+
+#[test]
+fn test_insurance_and_asset_integration() {
+ let env = create_env();
+ let (admin, owner, insurer, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Register asset
+ let asset_id = generate_asset_id(&env, 1);
+ let asset = create_test_asset(&env, &owner, asset_id.clone());
+ client.register_asset(&asset, &admin);
+
+ // Create insurance policy for asset
+ let policy_id = generate_asset_id(&env, 100);
+ let policy = create_test_policy(&env, policy_id.clone(), &owner, &insurer, asset_id.clone());
+ client.create_insurance_policy(&policy);
+
+ // Verify policy exists
+ let stored_policy = client.get_insurance_policy(&policy_id);
+ assert!(stored_policy.is_some());
+
+ // Verify asset has policy
+ let asset_policies = client.get_asset_insurance_policies(&asset_id);
+ assert_eq!(asset_policies.len(), 1);
+}
+
+#[test]
+fn test_admin_operations_workflow() {
+ let env = create_env();
+ let (admin, new_admin, registrar, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Add authorized registrar
+ client.add_authorized_registrar(®istrar);
+ assert!(client.is_authorized_registrar(®istrar));
+
+ // Pause contract
+ client.pause_contract();
+ assert!(client.is_paused());
+
+ // Unpause contract
+ client.unpause_contract();
+ assert!(!client.is_paused());
+
+ // Update admin
+ client.update_admin(&new_admin);
+ assert_eq!(client.get_admin(), new_admin);
+
+ // Verify old admin is no longer authorized registrar
+ assert!(!client.is_authorized_registrar(&admin));
+
+ // Verify new admin is authorized registrar
+ assert!(client.is_authorized_registrar(&new_admin));
+}
diff --git a/contracts/assetsup/src/tests/mod.rs b/contracts/assetsup/src/tests/mod.rs
index fd8a933..0cded02 100644
--- a/contracts/assetsup/src/tests/mod.rs
+++ b/contracts/assetsup/src/tests/mod.rs
@@ -1,3 +1,25 @@
+// Test helper functions and utilities
+mod helpers;
+
+// Core contract tests
+mod admin;
+mod asset;
+mod initialization;
+
+// Tokenization and ownership tests
+mod detokenization;
+mod dividends;
+mod tokenization;
+mod transfer_restrictions;
+mod voting;
+
+// Insurance tests
+mod insurance;
+
+// Integration tests
+mod integration_full;
+
+// Legacy test modules (if still needed)
mod detokenization_new;
mod dividends_new;
mod insurance_new;
@@ -5,9 +27,3 @@ mod integration;
mod tokenization_new;
mod transfer_restrictions_new;
mod voting_new;
-// mod access_control;
-// mod asset;
-// mod branch;
-// mod initialize;
-// mod transfer;
-// mod types;
diff --git a/contracts/assetsup/src/tests/tokenization.rs b/contracts/assetsup/src/tests/tokenization.rs
new file mode 100644
index 0000000..bc1a024
--- /dev/null
+++ b/contracts/assetsup/src/tests/tokenization.rs
@@ -0,0 +1,463 @@
+use crate::tests::helpers::*;
+use crate::types::AssetType;
+use soroban_sdk::String;
+
+#[test]
+fn test_tokenize_asset_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ let result = client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ assert_eq!(result.asset_id, 1);
+ assert_eq!(result.total_supply, 1000000);
+ assert_eq!(result.tokenizer, user1);
+ assert_eq!(result.tokens_in_circulation, 1000000);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #10)")]
+fn test_tokenize_asset_already_tokenized() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Try to tokenize again - should panic with AssetAlreadyTokenized
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST2"),
+ &500000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token 2"),
+ &String::from_str(&env, "Another test"),
+ &AssetType::Physical,
+ );
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #12)")]
+fn test_tokenize_asset_invalid_supply() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Should panic with InvalidTokenSupply error
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &0i128, // Invalid: zero supply
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+}
+
+#[test]
+fn test_mint_tokens_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ let result = client.mint_tokens(&1u64, &500000i128, &user1);
+
+ assert_eq!(result.total_supply, 1500000);
+ assert_eq!(result.tokens_in_circulation, 1500000);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_mint_tokens_unauthorized() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // user2 is not tokenizer - should panic with Unauthorized
+ client.mint_tokens(&1u64, &500000i128, &user2);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #11)")]
+fn test_mint_tokens_not_tokenized() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Should panic with AssetNotTokenized error
+ client.mint_tokens(&999u64, &500000i128, &user1);
+}
+
+#[test]
+fn test_burn_tokens_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ let result = client.burn_tokens(&1u64, &200000i128, &user1);
+
+ assert_eq!(result.total_supply, 800000);
+ assert_eq!(result.tokens_in_circulation, 800000);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #14)")]
+fn test_burn_tokens_insufficient_balance() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Should panic with InsufficientBalance error
+ client.burn_tokens(&1u64, &2000000i128, &user1);
+}
+
+#[test]
+fn test_transfer_tokens_success() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+
+ // Verify balances
+ let balance1 = client.get_token_balance(&1u64, &user1);
+ let balance2 = client.get_token_balance(&1u64, &user2);
+
+ assert_eq!(balance1, 700000);
+ assert_eq!(balance2, 300000);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #14)")]
+fn test_transfer_tokens_insufficient_balance() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Should panic with InsufficientBalance error
+ client.transfer_tokens(&1u64, &user1, &user2, &2000000i128);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #16)")]
+fn test_transfer_tokens_locked() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Lock tokens
+ let future_time = env.ledger().timestamp() + 1000;
+ client.lock_tokens(&1u64, &user1, &future_time, &user1);
+
+ // Should panic with TokensAreLocked error
+ client.transfer_tokens(&1u64, &user1, &user2, &100000i128);
+}
+
+#[test]
+fn test_lock_unlock_tokens() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Initially not locked
+ assert!(!client.is_tokens_locked(&1u64, &user1));
+
+ // Lock tokens
+ let future_time = env.ledger().timestamp() + 1000;
+ client.lock_tokens(&1u64, &user1, &future_time, &user1);
+
+ assert!(client.is_tokens_locked(&1u64, &user1));
+
+ // Unlock tokens
+ client.unlock_tokens(&1u64, &user1);
+
+ assert!(!client.is_tokens_locked(&1u64, &user1));
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #8)")]
+fn test_lock_tokens_unauthorized() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ let future_time = env.ledger().timestamp() + 1000;
+
+ // user2 is not tokenizer - should panic with Unauthorized
+ client.lock_tokens(&1u64, &user1, &future_time, &user2);
+}
+
+#[test]
+fn test_get_ownership_percentage() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Transfer 30% to user2
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+
+ // Check ownership percentages (in basis points)
+ let percentage1 = client.get_ownership_percentage(&1u64, &user1);
+ let percentage2 = client.get_ownership_percentage(&1u64, &user2);
+
+ assert_eq!(percentage1, 7000); // 70%
+ assert_eq!(percentage2, 3000); // 30%
+}
+
+#[test]
+fn test_get_token_holders() {
+ let env = create_env();
+ let (admin, user1, user2, user3) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Initially only user1
+ let holders = client.get_token_holders(&1u64);
+ assert_eq!(holders.len(), 1);
+
+ // Transfer to user2 and user3
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+ client.transfer_tokens(&1u64, &user1, &user3, &200000i128);
+
+ // Now should have 3 holders
+ let holders = client.get_token_holders(&1u64);
+ assert_eq!(holders.len(), 3);
+}
+
+#[test]
+fn test_update_valuation() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.update_valuation(&1u64, &2000000i128);
+
+ let asset = client.get_tokenized_asset(&1u64);
+ assert_eq!(asset.valuation, 2000000);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #30)")]
+fn test_update_valuation_invalid() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Should panic with InvalidValuation error
+ client.update_valuation(&1u64, &0i128);
+}
diff --git a/contracts/assetsup/src/tests/transfer_restrictions.rs b/contracts/assetsup/src/tests/transfer_restrictions.rs
new file mode 100644
index 0000000..8cc39c2
--- /dev/null
+++ b/contracts/assetsup/src/tests/transfer_restrictions.rs
@@ -0,0 +1,198 @@
+use crate::tests::helpers::*;
+use crate::types::AssetType;
+use soroban_sdk::String;
+
+#[test]
+fn test_add_to_whitelist() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Initially not whitelisted
+ assert!(!client.is_whitelisted(&1u64, &user2));
+
+ // Add to whitelist
+ client.add_to_whitelist(&1u64, &user2);
+
+ assert!(client.is_whitelisted(&1u64, &user2));
+}
+
+#[test]
+fn test_remove_from_whitelist() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Add to whitelist
+ client.add_to_whitelist(&1u64, &user2);
+ assert!(client.is_whitelisted(&1u64, &user2));
+
+ // Remove from whitelist
+ client.remove_from_whitelist(&1u64, &user2);
+ assert!(!client.is_whitelisted(&1u64, &user2));
+}
+
+#[test]
+fn test_get_whitelist() {
+ let env = create_env();
+ let (admin, user1, user2, user3) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Add multiple addresses to whitelist
+ client.add_to_whitelist(&1u64, &user2);
+ client.add_to_whitelist(&1u64, &user3);
+
+ let whitelist = client.get_whitelist(&1u64);
+ assert_eq!(whitelist.len(), 2);
+}
+
+#[test]
+fn test_add_duplicate_to_whitelist() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Add to whitelist twice
+ client.add_to_whitelist(&1u64, &user2);
+ client.add_to_whitelist(&1u64, &user2);
+
+ // Should still only have one entry
+ let whitelist = client.get_whitelist(&1u64);
+ assert_eq!(whitelist.len(), 1);
+}
+
+#[test]
+fn test_set_transfer_restriction() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Set transfer restriction
+ client.set_transfer_restriction(&1u64, &true);
+
+ // Restriction should be set (no error means success)
+}
+
+#[test]
+fn test_transfer_with_whitelist() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Add user2 to whitelist
+ client.add_to_whitelist(&1u64, &user2);
+
+ // Transfer should succeed
+ client.transfer_tokens(&1u64, &user1, &user2, &100000i128);
+
+ let balance = client.get_token_balance(&1u64, &user2);
+ assert_eq!(balance, 100000);
+}
+
+#[test]
+fn test_empty_whitelist() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ let whitelist = client.get_whitelist(&1u64);
+ assert_eq!(whitelist.len(), 0);
+}
diff --git a/contracts/assetsup/src/tests/voting.rs b/contracts/assetsup/src/tests/voting.rs
new file mode 100644
index 0000000..0856870
--- /dev/null
+++ b/contracts/assetsup/src/tests/voting.rs
@@ -0,0 +1,187 @@
+use crate::tests::helpers::*;
+use crate::types::AssetType;
+use soroban_sdk::String;
+
+#[test]
+fn test_cast_vote_success() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Tokenize asset
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Cast vote
+ client.cast_vote(&1u64, &1u64, &user1);
+
+ // Verify vote was recorded
+ assert!(client.has_voted(&1u64, &1u64, &user1));
+
+ // Verify vote tally
+ let tally = client.get_vote_tally(&1u64, &1u64);
+ assert_eq!(tally, 1000000); // Full balance
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #22)")]
+fn test_cast_vote_already_voted() {
+ let env = create_env();
+ let (admin, user1, _, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ client.cast_vote(&1u64, &1u64, &user1);
+
+ // Try to vote again - should panic with AlreadyVoted
+ client.cast_vote(&1u64, &1u64, &user1);
+}
+
+#[test]
+#[should_panic(expected = "Error(Contract, #21)")]
+fn test_cast_vote_insufficient_voting_power() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ // Tokenize with high voting threshold
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &500000i128, // 50% threshold
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Transfer small amount to user2
+ client.transfer_tokens(&1u64, &user1, &user2, &10000i128);
+
+ // user2 doesn't have enough tokens - should panic with InsufficientVotingPower
+ client.cast_vote(&1u64, &1u64, &user2);
+}
+
+#[test]
+fn test_proposal_passed() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Transfer 60% to user2
+ client.transfer_tokens(&1u64, &user1, &user2, &600000i128);
+
+ // user2 votes (60% of supply)
+ client.cast_vote(&1u64, &1u64, &user2);
+
+ // Proposal should pass (>50% threshold)
+ assert!(client.proposal_passed(&1u64, &1u64));
+}
+
+#[test]
+fn test_proposal_not_passed() {
+ let env = create_env();
+ let (admin, user1, user2, _) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Transfer 40% to user2
+ client.transfer_tokens(&1u64, &user1, &user2, &400000i128);
+
+ // user2 votes (40% of supply)
+ client.cast_vote(&1u64, &1u64, &user2);
+
+ // Proposal should not pass (<50% threshold)
+ assert!(!client.proposal_passed(&1u64, &1u64));
+}
+
+#[test]
+fn test_multiple_voters() {
+ let env = create_env();
+ let (admin, user1, user2, user3) = create_mock_addresses(&env);
+ let client = initialize_contract(&env, &admin);
+
+ env.mock_all_auths();
+
+ client.tokenize_asset(
+ &1u64,
+ &String::from_str(&env, "TST"),
+ &1000000i128,
+ &6u32,
+ &100i128,
+ &user1,
+ &String::from_str(&env, "Test Token"),
+ &String::from_str(&env, "A test tokenized asset"),
+ &AssetType::Physical,
+ );
+
+ // Distribute tokens
+ client.transfer_tokens(&1u64, &user1, &user2, &300000i128);
+ client.transfer_tokens(&1u64, &user1, &user3, &200000i128);
+
+ // Multiple users vote
+ client.cast_vote(&1u64, &1u64, &user1); // 500000
+ client.cast_vote(&1u64, &1u64, &user2); // 300000
+
+ // Total tally should be 800000
+ let tally = client.get_vote_tally(&1u64, &1u64);
+ assert_eq!(tally, 800000);
+
+ // Proposal should pass (80% > 50%)
+ assert!(client.proposal_passed(&1u64, &1u64));
+}