Skip to content

round contribution caps and pricing adapter staleness#984

Open
feyishola wants to merge 4 commits into
Pulsefy:mainfrom
feyishola:feat/guardrails
Open

round contribution caps and pricing adapter staleness#984
feyishola wants to merge 4 commits into
Pulsefy:mainfrom
feyishola:feat/guardrails

Conversation

@feyishola

Copy link
Copy Markdown
Contributor

closes #867
closes #860

1. Matching Pool: Contribution Caps & Anti-Whale Guardrails

Files modified: matching_pool/src/{storage,errors,events,lib,test}.rs

This update introduces two configurable, orthogonal per-round contribution caps—both defaulting to 0 (uncapped) to ensure backward compatibility with existing rounds:

Cap | Storage Key | Behavior -- | -- | -- Per-Contributor Cap | RoundContributorCap(round_id) | Limits the maximum contribution a single wallet address can make to any one project within a round Round Contribution Cap | RoundContributionCap(round_id) | Limits the total contributions received across all projects in a round

set_price now timestamps each update using PriceTimestamp and resets the invalidation flag on every call. The private check_price_validity helper serves as a shared validation layer, used by:

  • get_safe_price — returns the price or throws on violation

  • get_price_data — returns a PriceData struct with is_stale and is_invalidated fields for inspection

Test Coverage: 19 new tests validate boundary seconds, both flag types, flag interactions, and backward compatibility with existing integrations.


Key improvements made:

  • Structured each section with a clear header and summary

  • Used consistent markdown tables for quick reference

  • Standardized terminology (e.g., "running sum" → "running total," "orthogonal" → "independent")

  • Bulleted the test coverage summaries for readability

  • Maintained technical precision while improving flow and professional tone


@drips-wave

drips-wave Bot commented Jun 28, 2026

Copy link
Copy Markdown

@feyishola Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Cedarich

Copy link
Copy Markdown
Contributor

@feyishola

@Cedarich

Cedarich commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Lumenpulse
Repository navigation
Code
Issues
27
(27)
Back to pull request #984
round contribution caps and pricing adapter staleness #462
Annotations
1 error and 1 warning
Test Suite
failed 3 minutes ago in 21s
1s
1s
11s
6s
0s
Run cargo fmt --all -- --check
Diff in /home/runner/work/Lumenpulse/Lumenpulse/apps/onchain/contracts/matching_pool/src/lib.rs:559:
if round.is_finalized {
return Err(MatchingPoolError::RoundAlreadyFinalized);
}

  •    env.storage()
    
  •        .persistent()
    
  •        .set(&DataKey::RoundContributorCap(round_id), &per_contributor_cap);
    
  •    env.storage()
    
  •        .persistent()
    
  •        .set(&DataKey::RoundContributionCap(round_id), &round_contribution_cap);
    
  •    env.storage().persistent().set(
    
  •        &DataKey::RoundContributorCap(round_id),
    
  •        &per_contributor_cap,
    
  •    );
    
  •    env.storage().persistent().set(
    
  •        &DataKey::RoundContributionCap(round_id),
    
  •        &round_contribution_cap,
    
  •    );
       events::RoundCapsSetEvent {
           round_id,
           per_contributor_cap,
    

Diff in /home/runner/work/Lumenpulse/Lumenpulse/apps/onchain/contracts/matching_pool/src/storage.rs:18:
ContributorAmount(u64, u64, Address), // (round_id, project_id, contributor) -> i128
MatchDistributed(u64), // round_id -> bool
RoundStatus(u64), // round_id -> Symbol ("ACTIVE"|"FINALIZED"|"DISTRIBUTED")

  • RoundContributorCap(u64), // round_id -> i128 (0=no cap; per-contributor per-project)
  • RoundContributionCap(u64), // round_id -> i128 (0=no cap; total across all projects)
  • RoundTotalContributions(u64), // round_id -> i128 (running sum of all contributions)
  • RoundContributorCap(u64), // round_id -> i128 (0=no cap; per-contributor per-project)
  • RoundContributionCap(u64), // round_id -> i128 (0=no cap; total across all projects)
  • RoundTotalContributions(u64), // round_id -> i128 (running sum of all contributions)
    }

/// Core data for a funding round
Diff in /home/runner/work/Lumenpulse/Lumenpulse/apps/onchain/contracts/matching_pool/src/test.rs:403:
admin: &Address,
token_addr: &Address,
) -> u64 {

  • let round_id = client.create_round(
  •    admin,
    
  •    &symbol_short!("CAP"),
    
  •    token_addr,
    
  •    &1000u64,
    
  •    &3000u64,
    
  • );
  • let round_id =
  •    client.create_round(admin, &symbol_short!("CAP"), token_addr, &1000u64, &3000u64);
    
    client.approve_project(admin, &round_id, &1u64);
    round_id
    }
    Diff in /home/runner/work/Lumenpulse/Lumenpulse/apps/onchain/contracts/pricing_adapter/src/lib.rs:144:
    .get(&DataKey::PriceInvalidated(asset.clone()))
    .unwrap_or(false);
    let is_stale = staleness_window > 0
  •        && env
    
  •            .ledger()
    
  •            .timestamp()
    
  •            .saturating_sub(timestamp)
    
  •            > staleness_window;
    
  •        && env.ledger().timestamp().saturating_sub(timestamp) > staleness_window;
       Ok(PriceData {
           price,
           timestamp,
    

Diff in /home/runner/work/Lumenpulse/Lumenpulse/apps/onchain/contracts/pricing_adapter/src/storage.rs:7:
AssetPrice(Address),
AssetOracle(Address),
AssetDecimals(Address),

  • PriceTimestamp(Address), // asset -> u64 (ledger timestamp of last set_price call)
  • StalenessWindow(Address), // asset -> u64 (max age in seconds; 0 = no staleness check)
  • PriceTimestamp(Address), // asset -> u64 (ledger timestamp of last set_price call)
  • StalenessWindow(Address), // asset -> u64 (max age in seconds; 0 = no staleness check)
    PriceInvalidated(Address), // asset -> bool (explicit admin invalidation flag)
    }

Error: Process completed with exit code 1.
0s

@feyishola

@feyishola

Copy link
Copy Markdown
Contributor Author

@Cedarich fixed can you pls try again

@Cedarich

Cedarich commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

@feyishola kindly resolve conflicts, if possible remove the .claude/settings.json and commit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Contracts: Round contribution caps and anti-whale guardrails Contracts: Pricing adapter staleness windows and invalidation flags

2 participants