Skip to content

claim_refund` does not refund donors whose only donations were Native XLM #11

Description

@P3az3

Problem Statement

In CampaignContract::claim_refund (campaign/src/lib.rs), the per-asset refund loop is:

for asset in campaign.accepted_assets.iter() {
    let asset_address = match &asset.issuer {
        Some(addr) => addr.clone(),
        None => continue,  // ⚠️ Native XLM donors silently skipped
    };
    ...
}

The continue; drops any donor whose donations were entirely in Native XLM. Combined with Issue 1 (donors past the deadline can still donate if status is non-terminal) and Issue 2 (Native routes through accepted_assets[].issuer), there is a path where a Native-only donor believes they are entitled to a refund but receives nothing.

Why it matters

This is a direct failure of the documented refund contract. RefundNotPermitted is only returned when none of the eligibility checks pass. A Native-only donor whose refund is otherwise eligible will lose funds.

Expected Outcome

The per-asset refund loop must materialize the network's canonical wrapped-XLM contract address (introduced in Issue 2) and use it as the asset_address for any native donations. For multi-asset campaigns, each accepted asset gets a corresponding refund path.

Acceptance Criteria

  • New test test_claim_refund_native_xlm_donor that donates Native, cancels the campaign, and asserts successful refund with the canonical XLM contract address.
  • New test test_claim_refund_mixed_native_and_sac that donates both Native and a custom SAC, cancels the campaign, and asserts successful refunds for both.
  • Negative tests:
    • Campaign with no native XLM entry in accepted_assets rejects Native donations (the donation-side fix from Issue 2 complements this).
  • docs/events.md updates the refund_claimed event example with a Native contribution.

Implementation Notes

  • Pull out a fn xlm_token_address(env: &Env) -> Address returning the network-fixed canonical wrapped XLM. Use it in both donate and claim_refund.
  • Replace the inner continue with the same loop body but parameterized with the canonical XLM address for None issuer entries.

Affected Files / Modules

  • campaign/src/lib.rs (claim_refund)
  • campaign/src/types.rs (StellarAsset, xlm constants)
  • campaign/src/test/claim_refund_tests.rs (new tests)
  • campaign/src/test/refund_eligibility_tests.rs (mixed-asset test)

Dependencies — Depends on Issue 2 (canonical native XLM address).

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26priority/p0Highest priority. Drop everything else.refund-flowDonor refund / pro-rata math.securitySecurity-impact change.smart-contractSoroban/Stellar contract logic.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions