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).
Problem Statement
In
CampaignContract::claim_refund(campaign/src/lib.rs), the per-asset refund loop is: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 throughaccepted_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.
RefundNotPermittedis 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_addressfor any native donations. For multi-asset campaigns, each accepted asset gets a corresponding refund path.Acceptance Criteria
test_claim_refund_native_xlm_donorthat donates Native, cancels the campaign, and asserts successful refund with the canonical XLM contract address.test_claim_refund_mixed_native_and_sacthat donates both Native and a custom SAC, cancels the campaign, and asserts successful refunds for both.accepted_assetsrejects Native donations (the donation-side fix from Issue 2 complements this).docs/events.mdupdates therefund_claimedevent example with a Native contribution.Implementation Notes
fn xlm_token_address(env: &Env) -> Addressreturning the network-fixed canonical wrapped XLM. Use it in bothdonateandclaim_refund.continuewith the same loop body but parameterized with the canonical XLM address forNoneissuer 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).