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).

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