diff --git a/contracts/sharpy/src/lib.rs b/contracts/sharpy/src/lib.rs index dff15eb..e8c6cd4 100644 --- a/contracts/sharpy/src/lib.rs +++ b/contracts/sharpy/src/lib.rs @@ -245,20 +245,26 @@ impl SharpyContract { payer.require_auth(); assert!(!payments.is_empty(), "payments must not be empty"); - let mut total: i128 = 0; + // Phase 1: Validate all invoices and group totals by token + let mut token_totals: Map = Map::new(&env); for p in payments.iter() { let inv = load_invoice(&env, p.invoice_id); assert!(inv.status == InvoiceStatus::Pending, "invoice is not pending"); assert!(p.amount > 0, "payment amount must be positive"); let inv_total: i128 = inv.amounts.iter().sum(); assert!(inv.funded + p.amount <= inv_total, "payment exceeds remaining balance"); - total += p.amount; + let token = inv.tokens.get(0).expect("no token"); + let prev = token_totals.get(token.clone()).unwrap_or(0); + token_totals.set(token, prev + p.amount); } - let first = load_invoice(&env, payments.get(0).unwrap().invoice_id); - let token_client = token::Client::new(&env, &first.tokens.get(0).expect("no token")); - token_client.transfer(&payer, &env.current_contract_address(), &total); + // Phase 2: Transfer tokens — one transfer per unique token + for (token, amount) in token_totals.iter() { + let token_client = token::Client::new(&env, &token); + token_client.transfer(&payer, &env.current_contract_address(), &amount); + } + // Phase 3: Update each invoice's state for p in payments.iter() { let mut inv = load_invoice(&env, p.invoice_id); inv.payments.push_back(Payment { payer: payer.clone(), amount: p.amount, tip: 0 }); diff --git a/contracts/sharpy/src/test.rs b/contracts/sharpy/src/test.rs index 5b82e34..7a3ab1c 100644 --- a/contracts/sharpy/src/test.rs +++ b/contracts/sharpy/src/test.rs @@ -7,6 +7,21 @@ mod tests { SharpyContractClient, }; + fn setup_with_tokens( + env: &Env, + payer: &Address, + amounts: &[i128], + ) -> (Address, Address) { + let admin = Address::generate(env); + let token_a = env.register_stellar_asset_contract(admin.clone()); + let token_b = env.register_stellar_asset_contract(admin); + let sac_a = token::StellarAssetClient::new(env, &token_a); + let sac_b = token::StellarAssetClient::new(env, &token_b); + sac_a.mint(payer, &amounts[0]); + sac_b.mint(payer, &amounts[1]); + (token_a, token_b) + } + fn setup() -> (Env, SharpyContractClient<'static>) { let env = Env::default(); env.mock_all_auths();