Skip to content

Commit a413d23

Browse files
committed
perf(grant): O(1) claim_milestone recipient lookup + increase get_winners cap
1 parent 84a386f commit a413d23

4 files changed

Lines changed: 62 additions & 28 deletions

File tree

contracts/events/src/event_ops.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub fn create_event(env: &Env, params: CreateEventParams, op_id: BytesN<32>) ->
155155
reputation_bump: None,
156156
},
157157
);
158+
storage::set_grant_recipient_idx(env, id, &params.owner, 0);
158159
}
159160

160161
evt::EventCreated {
@@ -670,7 +671,8 @@ pub fn select_winners(
670671
}
671672
}
672673
ReleaseKind::Multi(_) => {
673-
for spec in winners.iter() {
674+
for (i, spec) in winners.iter().enumerate() {
675+
let anchor_idx = existing_count + (i as u32);
674676
storage::append_winner(
675677
env,
676678
event_id,
@@ -683,6 +685,7 @@ pub fn select_winners(
683685
reputation_bump: Some(spec.reputation_bump),
684686
},
685687
);
688+
storage::set_grant_recipient_idx(env, event_id, &spec.recipient, anchor_idx);
686689
}
687690
}
688691
}
@@ -849,7 +852,7 @@ pub fn get_winners(env: &Env, event_id: u64) -> Result<Vec<Winner>, Error> {
849852
Ok(storage::winners_snapshot(
850853
env,
851854
event_id,
852-
MAX_WINNERS_PER_SELECT,
855+
MAX_WINNERS_PER_SELECT.saturating_mul(20),
853856
))
854857
}
855858

contracts/events/src/grant.rs

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,33 +54,24 @@ pub fn claim_milestone(
5454
return Err(Error::MilestoneAlreadyClaimed);
5555
}
5656

57-
let count = storage::winner_count(env, event_id);
58-
let mut winner_position: Option<u32> = None;
59-
let mut reputation_bump: u32 = 0;
60-
let mut already_claimed_for_recipient: u32 = 0;
61-
let mut already_paid_to_recipient: i128 = 0;
62-
for idx in 0..count {
63-
let w = match storage::winner_at(env, event_id, idx) {
64-
Some(w) => w,
65-
None => continue,
66-
};
67-
if w.recipient != recipient {
68-
continue;
69-
}
70-
match w.milestone {
71-
None => {
72-
winner_position = Some(w.position);
73-
reputation_bump = w.reputation_bump.unwrap_or(0);
74-
}
75-
Some(_) => {
76-
already_claimed_for_recipient = already_claimed_for_recipient.saturating_add(1);
77-
already_paid_to_recipient = already_paid_to_recipient.saturating_add(w.amount);
78-
}
79-
}
80-
}
81-
let position = winner_position.ok_or(Error::NoSubmissions)?;
82-
8357
let is_crowdfunding = matches!(event.pillar, Pillar::Crowdfunding);
58+
59+
let anchor_idx =
60+
storage::get_grant_recipient_idx(env, event_id, &recipient).ok_or(Error::NoSubmissions)?;
61+
let anchor = storage::winner_at(env, event_id, anchor_idx).ok_or(Error::NoSubmissions)?;
62+
let position = anchor.position;
63+
let reputation_bump = anchor.reputation_bump.unwrap_or(0);
64+
let already_claimed_for_recipient =
65+
storage::get_grant_recipient_claim_count(env, event_id, &recipient);
66+
let already_paid_to_recipient = if is_crowdfunding {
67+
0
68+
} else {
69+
let percent = event.winner_distribution.get(position).unwrap_or(0) as i128;
70+
let total_share = event.total_budget.saturating_mul(percent) / 100_i128;
71+
let per_milestone_floored = total_share / (total_milestones as i128);
72+
(already_claimed_for_recipient as i128).saturating_mul(per_milestone_floored)
73+
};
74+
8475
let amount: i128 = if is_crowdfunding {
8576
let claimed_count = storage::get_crowdfunding_milestones_claimed(env, event_id);
8677
let remaining_milestones = total_milestones.saturating_sub(claimed_count);
@@ -124,6 +115,7 @@ pub fn claim_milestone(
124115
}
125116
event.remaining_escrow = event.remaining_escrow.saturating_sub(amount);
126117
storage::mark_milestone_claimed(env, event_id, &recipient, milestone);
118+
storage::increment_grant_recipient_claim_count(env, event_id, &recipient);
127119
if is_crowdfunding {
128120
let claimed = storage::get_crowdfunding_milestones_claimed(env, event_id);
129121
storage::set_crowdfunding_milestones_claimed(env, event_id, claimed.saturating_add(1));

contracts/events/src/storage.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,39 @@ pub fn get_winner_index(env: &Env, id: u64, recipient: &Address, position: u32)
466466
idx
467467
}
468468

469+
pub fn get_grant_recipient_idx(env: &Env, id: u64, recipient: &Address) -> Option<u32> {
470+
let key = DataKey::GrantRecipientIdx(id, recipient.clone());
471+
let idx: Option<u32> = env.storage().persistent().get(&key);
472+
if idx.is_some() {
473+
touch_event_persistent(env, &key);
474+
}
475+
idx
476+
}
477+
478+
pub fn set_grant_recipient_idx(env: &Env, id: u64, recipient: &Address, idx: u32) {
479+
let key = DataKey::GrantRecipientIdx(id, recipient.clone());
480+
env.storage().persistent().set(&key, &idx);
481+
touch_event_persistent(env, &key);
482+
}
483+
484+
pub fn get_grant_recipient_claim_count(env: &Env, id: u64, recipient: &Address) -> u32 {
485+
let key = DataKey::GrantRecipientClaimCount(id, recipient.clone());
486+
let count: Option<u32> = env.storage().persistent().get(&key);
487+
if count.is_some() {
488+
touch_event_persistent(env, &key);
489+
}
490+
count.unwrap_or(0)
491+
}
492+
493+
pub fn increment_grant_recipient_claim_count(env: &Env, id: u64, recipient: &Address) {
494+
let key = DataKey::GrantRecipientClaimCount(id, recipient.clone());
495+
let count: u32 = env.storage().persistent().get(&key).unwrap_or(0);
496+
env.storage()
497+
.persistent()
498+
.set(&key, &count.saturating_add(1));
499+
touch_event_persistent(env, &key);
500+
}
501+
469502
pub fn winners_snapshot(env: &Env, id: u64, max: u32) -> Vec<Winner> {
470503
let count = winner_count(env, id);
471504
let upper = if count < max { count } else { max };

contracts/events/src/types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ pub enum DataKey {
202202

203203
// Winner anchor index for O(1) claim_prize lookup (added 2026-07).
204204
WinnerIndex(u64, Address, u32),
205+
206+
// Grant anchor index for O(1) claim_milestone recipient lookup.
207+
GrantRecipientIdx(u64, Address),
208+
209+
// Per-recipient milestone claim counter for claim_milestone.
210+
GrantRecipientClaimCount(u64, Address),
205211
}
206212

207213
// ============================================================

0 commit comments

Comments
 (0)