Skip to content

Commit 66069cf

Browse files
committed
Feat: Escrow Search/Indexing
1 parent 530aad7 commit 66069cf

25 files changed

Lines changed: 8060 additions & 1 deletion

craft-nexus-contract/src/lib.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ const DEFAULT_PLATFORM_FEE_BPS: u32 = 500;
5353
/// Maximum platform fee in basis points (10000 = 100%)
5454
const MAX_PLATFORM_FEE_BPS: u32 = 1000; // 10% max
5555

56+
#[contracttype]
57+
#[derive(Clone, Debug, Eq, PartialEq)]
58+
pub enum DataKey {
59+
Escrow(u32),
60+
BuyerEscrows(Address),
61+
SellerEscrows(Address),
62+
}
63+
5664
#[contracttype]
5765
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
5866
pub enum EscrowStatus {
@@ -277,11 +285,22 @@ impl EscrowContract {
277285
metadata_hash: metadata_hash.clone(),
278286
};
279287

280-
// Store escrow by order_id
281288
env.storage()
282289
.persistent()
283290
.set(&(ESCROW, order_id), &escrow);
284291

292+
// Update buyer's escrow list for indexing
293+
let buyer_key = DataKey::BuyerEscrows(buyer.clone());
294+
let mut buyer_escrows: soroban_sdk::Vec<u64> = env.storage().persistent().get(&buyer_key).unwrap_or(soroban_sdk::Vec::new(&env));
295+
buyer_escrows.push_back(order_id as u64);
296+
env.storage().persistent().set(&buyer_key, &buyer_escrows);
297+
298+
// Update seller's escrow list for indexing
299+
let seller_key = DataKey::SellerEscrows(seller.clone());
300+
let mut seller_escrows: soroban_sdk::Vec<u64> = env.storage().persistent().get(&seller_key).unwrap_or(soroban_sdk::Vec::new(&env));
301+
seller_escrows.push_back(order_id as u64);
302+
env.storage().persistent().set(&seller_key, &seller_escrows);
303+
285304
// Transfer funds from buyer to contract
286305
let client = token::Client::new(&env, &token);
287306
client.transfer(&buyer, &env.current_contract_address(), &amount);
@@ -301,6 +320,50 @@ impl EscrowContract {
301320
escrow
302321
}
303322

323+
/// Get escrows for a specific buyer with pagination.
324+
pub fn get_escrows_by_buyer(
325+
env: Env,
326+
buyer: Address,
327+
page: u32,
328+
limit: u32,
329+
) -> Result<soroban_sdk::Vec<u64>, Error> {
330+
let key = DataKey::BuyerEscrows(buyer);
331+
let escrow_ids: soroban_sdk::Vec<u64> = env.storage().persistent().get(&key)
332+
.unwrap_or(soroban_sdk::Vec::new(&env));
333+
334+
let start = page * limit;
335+
let len = escrow_ids.len();
336+
337+
if start >= len {
338+
return Ok(soroban_sdk::Vec::new(&env));
339+
}
340+
341+
let end = (start + limit).min(len);
342+
Ok(escrow_ids.slice(start..end))
343+
}
344+
345+
/// Get escrows for a specific seller with pagination.
346+
pub fn get_escrows_by_seller(
347+
env: Env,
348+
seller: Address,
349+
page: u32,
350+
limit: u32,
351+
) -> Result<soroban_sdk::Vec<u64>, Error> {
352+
let key = DataKey::SellerEscrows(seller);
353+
let escrow_ids: soroban_sdk::Vec<u64> = env.storage().persistent().get(&key)
354+
.unwrap_or(soroban_sdk::Vec::new(&env));
355+
356+
let start = page * limit;
357+
let len = escrow_ids.len();
358+
359+
if start >= len {
360+
return Ok(soroban_sdk::Vec::new(&env));
361+
}
362+
363+
let end = (start + limit).min(len);
364+
Ok(escrow_ids.slice(start..end))
365+
}
366+
304367
/// Get platform configuration
305368
fn get_platform_config(env: &Env) -> PlatformConfig {
306369
let config = env.storage()

craft-nexus-contract/src/test.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,3 +700,70 @@ fn test_create_escrow_with_invalid_cid_fails() {
700700
&None,
701701
);
702702
}
703+
// ===== Search and Pagination Tests =====
704+
705+
#[test]
706+
fn test_escrow_search_by_buyer() {
707+
let env = Env::default();
708+
env.mock_all_auths();
709+
let (client, buyer, seller, token_id, token_admin, _) = setup_test(&env);
710+
711+
token_admin.mint(&buyer, &2000);
712+
713+
// Create 3 escrows for the same buyer
714+
client.create_escrow(&buyer, &seller, &token_id, &100, &1, &None);
715+
client.create_escrow(&buyer, &seller, &token_id, &200, &2, &None);
716+
client.create_escrow(&buyer, &seller, &token_id, &300, &3, &None);
717+
718+
// Get all (limit 10)
719+
let b1 = client.get_escrows_by_buyer(&buyer, &0, &10);
720+
assert_eq!(b1.len(), 3);
721+
assert_eq!(b1.get_unchecked(0), 1);
722+
assert_eq!(b1.get_unchecked(1), 2);
723+
assert_eq!(b1.get_unchecked(2), 3);
724+
725+
// Pagination: page 0, limit 2
726+
let b2 = client.get_escrows_by_buyer(&buyer, &0, &2);
727+
assert_eq!(b2.len(), 2);
728+
assert_eq!(b2.get_unchecked(0), 1);
729+
assert_eq!(b2.get_unchecked(1), 2);
730+
731+
// Pagination: page 1, limit 2
732+
let b3 = client.get_escrows_by_buyer(&buyer, &1, &2);
733+
assert_eq!(b3.len(), 1);
734+
assert_eq!(b3.get_unchecked(0), 3);
735+
736+
// Pagination: out of bounds
737+
let b4 = client.get_escrows_by_buyer(&buyer, &2, &2);
738+
assert_eq!(b4.len(), 0);
739+
}
740+
741+
#[test]
742+
fn test_escrow_search_by_seller() {
743+
let env = Env::default();
744+
env.mock_all_auths();
745+
let (client, buyer, seller, token_id, token_admin, _) = setup_test(&env);
746+
747+
token_admin.mint(&buyer, &2000);
748+
749+
// Create escrows for different sellers
750+
let seller2 = Address::generate(&env);
751+
client.create_escrow(&buyer, &seller, &token_id, &100, &1, &None);
752+
client.create_escrow(&buyer, &seller2, &token_id, &200, &2, &None);
753+
client.create_escrow(&buyer, &seller, &token_id, &300, &3, &None);
754+
755+
// Check seller 1
756+
let s1 = client.get_escrows_by_seller(&seller, &0, &10);
757+
assert_eq!(s1.len(), 2);
758+
assert_eq!(s1.get_unchecked(0), 1);
759+
assert_eq!(s1.get_unchecked(1), 3);
760+
761+
// Check seller 2
762+
let s2 = client.get_escrows_by_seller(&seller2, &0, &10);
763+
assert_eq!(s2.len(), 1);
764+
assert_eq!(s2.get_unchecked(0), 2);
765+
766+
// Check non-existent seller
767+
let s3 = client.get_escrows_by_seller(&Address::generate(&env), &0, &10);
768+
assert_eq!(s3.len(), 0);
769+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
Compiling craft-nexus-contract v0.1.0 (/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract)
2+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
3+
--> src/test.rs:719:59
4+
|
5+
719 | ...0).unwrap();
6+
| ^^^^^^ method not found in `Vec<u64>`
7+
|
8+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
9+
= note: consider using `--verbose` to print the full type name to the console
10+
11+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
12+
--> src/test.rs:726:58
13+
|
14+
726 | ...2).unwrap();
15+
| ^^^^^^ method not found in `Vec<u64>`
16+
|
17+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
18+
= note: consider using `--verbose` to print the full type name to the console
19+
20+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
21+
--> src/test.rs:732:58
22+
|
23+
732 | ...2).unwrap();
24+
| ^^^^^^ method not found in `Vec<u64>`
25+
|
26+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
27+
= note: consider using `--verbose` to print the full type name to the console
28+
29+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
30+
--> src/test.rs:737:58
31+
|
32+
737 | ...2).unwrap();
33+
| ^^^^^^ method not found in `Vec<u64>`
34+
|
35+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
36+
= note: consider using `--verbose` to print the full type name to the console
37+
38+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
39+
--> src/test.rs:756:61
40+
|
41+
756 | ...0).unwrap();
42+
| ^^^^^^ method not found in `Vec<u64>`
43+
|
44+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
45+
= note: consider using `--verbose` to print the full type name to the console
46+
47+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
48+
--> src/test.rs:762:62
49+
|
50+
762 | ...0).unwrap();
51+
| ^^^^^^ method not found in `Vec<u64>`
52+
|
53+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
54+
= note: consider using `--verbose` to print the full type name to the console
55+
56+
error[E0599]: no method named `unwrap` found for struct `Vec<T>` in the current scope
57+
--> src/test.rs:767:78
58+
|
59+
767 | ...0).unwrap();
60+
| ^^^^^^ method not found in `Vec<u64>`
61+
|
62+
= note: the full name for the type has been written to '/Users/atec/Desktop/Work stuff/CraftNexus/craft-nexus-contract/target/debug/deps/craft_nexus_contract-ec1ace976eaf7145.long-type-18408171063992311396.txt'
63+
= note: consider using `--verbose` to print the full type name to the console
64+
65+
warning: unused variable: `token_contract`
66+
--> src/test.rs:394:9
67+
|
68+
394 | ...et token_contract = ...
69+
| ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_token_contract`
70+
|
71+
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
72+
73+
For more information about this error, try `rustc --explain E0599`.
74+
warning: `craft-nexus-contract` (lib test) generated 1 warning
75+
error: could not compile `craft-nexus-contract` (lib test) due to 7 previous errors; 1 warning emitted

craft-nexus-contract/test_snapshots/test/test_auto_release_at_exact_window_boundary.1.json

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,55 @@
362362
4095
363363
]
364364
],
365+
[
366+
{
367+
"contract_data": {
368+
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
369+
"key": {
370+
"vec": [
371+
{
372+
"symbol": "BuyerEscrows"
373+
},
374+
{
375+
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
376+
}
377+
]
378+
},
379+
"durability": "persistent"
380+
}
381+
},
382+
[
383+
{
384+
"last_modified_ledger_seq": 0,
385+
"data": {
386+
"contract_data": {
387+
"ext": "v0",
388+
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
389+
"key": {
390+
"vec": [
391+
{
392+
"symbol": "BuyerEscrows"
393+
},
394+
{
395+
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4"
396+
}
397+
]
398+
},
399+
"durability": "persistent",
400+
"val": {
401+
"vec": [
402+
{
403+
"u64": 1
404+
}
405+
]
406+
}
407+
}
408+
},
409+
"ext": "v0"
410+
},
411+
4095
412+
]
413+
],
365414
[
366415
{
367416
"contract_data": {
@@ -487,6 +536,55 @@
487536
4095
488537
]
489538
],
539+
[
540+
{
541+
"contract_data": {
542+
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
543+
"key": {
544+
"vec": [
545+
{
546+
"symbol": "SellerEscrows"
547+
},
548+
{
549+
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
550+
}
551+
]
552+
},
553+
"durability": "persistent"
554+
}
555+
},
556+
[
557+
{
558+
"last_modified_ledger_seq": 0,
559+
"data": {
560+
"contract_data": {
561+
"ext": "v0",
562+
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
563+
"key": {
564+
"vec": [
565+
{
566+
"symbol": "SellerEscrows"
567+
},
568+
{
569+
"address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M"
570+
}
571+
]
572+
},
573+
"durability": "persistent",
574+
"val": {
575+
"vec": [
576+
{
577+
"u64": 1
578+
}
579+
]
580+
}
581+
}
582+
},
583+
"ext": "v0"
584+
},
585+
4095
586+
]
587+
],
490588
[
491589
{
492590
"contract_data": {

0 commit comments

Comments
 (0)