-
Notifications
You must be signed in to change notification settings - Fork 42
feat: implemented Add metadata_uri and verification_status to Collateral struct #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+202
−16
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
6594bb9
feat(collateral-registry): Add metadata_uri and is_verified to Collat…
d4aab2a
feat(escrow-manager): Add TTL for refund and dispute storage entries
d68cced
Merge branch 'master' into master
Ugasutun 6355e54
Merge branch 'master' into master
anonfedora File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |
| #![no_std] | ||
|
|
||
| use soroban_sdk::{ | ||
| contract, contractimpl, contracttype, symbol_short, Address, BytesN, Env, Symbol, | ||
| contract, contractimpl, contracttype, symbol_short, Address, BytesN, Env, Symbol, String, | ||
| }; | ||
|
|
||
| /// Contract errors | ||
|
|
@@ -44,6 +44,8 @@ pub struct Collateral { | |
| pub realized_value: i128, | ||
| pub expiry_ts: u64, | ||
| pub metadata_hash: BytesN<32>, | ||
| pub metadata_uri: String, | ||
| pub is_verified: bool, | ||
| pub registered_at: u64, | ||
| pub last_valuation_ts: u64, | ||
| pub locked: bool, | ||
|
|
@@ -87,6 +89,7 @@ impl CollateralRegistry { | |
| /// * `face_value` - Face value of the collateral (must be > 0) | ||
| /// * `expiry_ts` - Expiry timestamp (must be in future) | ||
| /// * `metadata_hash` - SHA-256 hash of off-chain metadata | ||
| /// * `metadata_uri` - URI pointing to off-chain metadata (IPFS/S3) | ||
| /// | ||
| /// # Returns | ||
| /// The sequential collateral ID | ||
|
|
@@ -99,6 +102,7 @@ impl CollateralRegistry { | |
| face_value: i128, | ||
| expiry_ts: u64, | ||
| metadata_hash: BytesN<32>, | ||
| metadata_uri: String, | ||
| ) -> Result<u64, ContractError> { | ||
| owner.require_auth(); | ||
|
|
||
|
|
@@ -137,6 +141,8 @@ impl CollateralRegistry { | |
| realized_value: face_value, | ||
| expiry_ts, | ||
| metadata_hash: metadata_hash.clone(), | ||
| metadata_uri: metadata_uri.clone(), | ||
| is_verified: false, | ||
| registered_at: current_ts, | ||
| last_valuation_ts: current_ts, | ||
| locked: false, | ||
|
|
@@ -355,6 +361,36 @@ impl CollateralRegistry { | |
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Verify collateral (admin only) | ||
| /// | ||
| /// # Arguments | ||
| /// * `id` - Collateral ID to verify | ||
| /// | ||
| /// # Events | ||
| /// Emits `CollateralVerified` event | ||
| pub fn verify_collateral(env: Env, id: u64) -> Result<(), ContractError> { | ||
| let admin: Address = env | ||
| .storage() | ||
| .instance() | ||
| .get(&symbol_short!("admin")) | ||
| .unwrap(); | ||
|
|
||
| admin.require_auth(); | ||
|
|
||
| let mut collateral: Collateral = env | ||
| .storage() | ||
| .persistent() | ||
| .get(&id) | ||
| .ok_or(ContractError::CollateralNotFound)?; | ||
|
|
||
| collateral.is_verified = true; | ||
| env.storage().persistent().set(&id, &collateral); | ||
|
|
||
| env.events().publish((symbol_short!("coll_verified"),), (id,)); | ||
|
Comment on lines
+440
to
+443
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 387 always forces Possible fix (toggle behavior)- collateral.is_verified = true;
+ collateral.is_verified = !collateral.is_verified;
env.storage().persistent().set(&id, &collateral);
- env.events().publish((symbol_short!("coll_verified"),), (id,));
+ env.events()
+ .publish((symbol_short!("coll_verified"),), (id, collateral.is_verified));🤖 Prompt for AI Agents |
||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
|
|
@@ -393,13 +429,15 @@ mod test { | |
| // Register collateral | ||
| let future_ts = env.ledger().timestamp() + 86400; // 1 day from now | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmTest123"); | ||
|
|
||
| let result = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner.clone(), | ||
| 1000, | ||
| future_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ); | ||
|
|
||
| assert!(result.is_ok()); | ||
|
|
@@ -413,6 +451,7 @@ mod test { | |
| assert_eq!(collateral.face_value, 1000); | ||
| assert_eq!(collateral.realized_value, 1000); | ||
| assert_eq!(collateral.locked, false); | ||
| assert_eq!(collateral.is_verified, false); | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -433,12 +472,14 @@ mod test { | |
| // Register collateral | ||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmTest456"); | ||
| let collateral_id = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner, | ||
| 1000, | ||
| future_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
|
|
@@ -468,13 +509,15 @@ mod test { | |
|
|
||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmTest789"); | ||
|
|
||
| let result = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner, | ||
| 0, // Invalid amount | ||
| future_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ); | ||
|
|
||
| assert_eq!(result, Err(ContractError::InvalidAmount)); | ||
|
|
@@ -497,13 +540,15 @@ mod test { | |
|
|
||
| let past_ts = env.ledger().timestamp() - 1; // Already expired | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmTestExp"); | ||
|
|
||
| let result = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner, | ||
| 1000, | ||
| past_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ); | ||
|
|
||
| assert_eq!(result, Err(ContractError::CollateralExpired)); | ||
|
|
@@ -524,6 +569,8 @@ mod test { | |
|
|
||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri1 = String::from_slice(&env, "ipfs://QmTestDup1"); | ||
| let metadata_uri2 = String::from_slice(&env, "ipfs://QmTestDup2"); | ||
|
|
||
| // Register first collateral | ||
| CollateralRegistry::register_collateral( | ||
|
|
@@ -532,6 +579,7 @@ mod test { | |
| 1000, | ||
| future_ts, | ||
| metadata_hash.clone(), | ||
| metadata_uri1, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
|
|
@@ -542,6 +590,7 @@ mod test { | |
| 2000, | ||
| future_ts, | ||
| metadata_hash, // Same hash | ||
| metadata_uri2, | ||
| ); | ||
|
|
||
| assert_eq!(result, Err(ContractError::DuplicateMetadata)); | ||
|
|
@@ -563,7 +612,8 @@ mod test { | |
|
|
||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let collateral_id = client.register_collateral(&owner, &1000, &future_ts, &metadata_hash); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmLockTest"); | ||
| let collateral_id = client.register_collateral(&owner, &1000, &future_ts, &metadata_hash, &metadata_uri); | ||
|
|
||
| client.lock_collateral(&collateral_id); | ||
| assert!(client.is_locked(&collateral_id)); | ||
|
|
@@ -604,12 +654,14 @@ mod test { | |
| // Register collateral | ||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmUnauthorized"); | ||
| let collateral_id = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner, | ||
| 1000, | ||
| future_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
|
|
@@ -618,4 +670,89 @@ mod test { | |
| assert_eq!(result, Err(ContractError::Unauthorized)); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_verify_collateral_success() { | ||
| let env = Env::default(); | ||
| env.mock_all_auths(); | ||
| let admin = Address::generate(&env); | ||
| let owner = Address::generate(&env); | ||
| let contract_id = env.register_contract(None, CollateralRegistry); | ||
|
|
||
| env.as_contract(&contract_id, || { | ||
| // Initialize | ||
| CollateralRegistry::initialize(env.clone(), admin.clone()).unwrap(); | ||
|
|
||
| // Register collateral | ||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmVerifyTest"); | ||
| let collateral_id = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner, | ||
| 1000, | ||
| future_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| // Verify collateral as admin | ||
| let verify_result = CollateralRegistry::verify_collateral(env.clone(), collateral_id); | ||
| assert!(verify_result.is_ok()); | ||
|
|
||
| // Verify is_verified is true | ||
| let collateral = | ||
| CollateralRegistry::get_collateral(env.clone(), collateral_id).unwrap(); | ||
| assert_eq!(collateral.is_verified, true); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_verify_collateral_unauthorized() { | ||
| let env = Env::default(); | ||
| env.mock_all_auths(); | ||
| let admin = Address::generate(&env); | ||
| let unauthorized = Address::generate(&env); | ||
| let owner = Address::generate(&env); | ||
| let contract_id = env.register_contract(None, CollateralRegistry); | ||
|
|
||
| env.as_contract(&contract_id, || { | ||
| CollateralRegistry::initialize(env.clone(), admin).unwrap(); | ||
|
|
||
| // Register collateral | ||
| let future_ts = env.ledger().timestamp() + 86400; | ||
| let metadata_hash = BytesN::from_array(&env, &[1; 32]); | ||
| let metadata_uri = String::from_slice(&env, "ipfs://QmUnauthorizedVerify"); | ||
| let collateral_id = CollateralRegistry::register_collateral( | ||
| env.clone(), | ||
| owner, | ||
| 1000, | ||
| future_ts, | ||
| metadata_hash, | ||
| metadata_uri, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| // Try to verify with non-admin (should fail due to auth) | ||
| let result = CollateralRegistry::verify_collateral(env.clone(), collateral_id); | ||
| assert_eq!(result, Err(ContractError::Unauthorized)); | ||
| }); | ||
Ugasutun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| #[test] | ||
| fn test_verify_collateral_not_found() { | ||
| let env = Env::default(); | ||
| env.mock_all_auths(); | ||
| let admin = Address::generate(&env); | ||
| let contract_id = env.register_contract(None, CollateralRegistry); | ||
|
|
||
| env.as_contract(&contract_id, || { | ||
| CollateralRegistry::initialize(env.clone(), admin).unwrap(); | ||
|
|
||
| // Try to verify non-existent collateral | ||
| let result = CollateralRegistry::verify_collateral(env.clone(), 999); | ||
| assert_eq!(result, Err(ContractError::CollateralNotFound)); | ||
| }); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cross-contract
Collateralschema is now inconsistent and can break integration.After adding
metadata_uriandis_verifiedhere,contracts/risk-assessment/src/lib.rs(Lines 274-286 in provided context) still uses the oldCollateralshape. This can break cross-contract decoding at runtime.Suggested alignment patch (risk-assessment side)
// contracts/risk-assessment/src/lib.rs #[contracttype] #[derive(Clone, Debug)] pub struct Collateral { pub id: u64, pub owner: Address, pub face_value: i128, pub realized_value: i128, pub expiry_ts: u64, + pub metadata_uri: String, + pub is_verified: bool, pub registered_at: u64, pub last_valuation_ts: u64, pub locked: bool, }🤖 Prompt for AI Agents