Summary
Investors who want to diversify across multiple creators must submit one transaction per creator, leaving them exposed to price movements between each submission. This issue implements a multi_buy function that accepts a vector of (creator, amount, max_price_per_key) tuples, executes all purchases atomically in a single Soroban transaction, and panics with a full rollback if any single leg's price exceeds its max_price — guaranteeing either all legs execute or none do.
Scope
1. Function signature
pub fn multi_buy(
env: Env,
buyer: Address,
legs: Vec<MultiBuyLeg>, // { creator: Address, amount: u32, max_price: i128 }
global_deadline_ledger: u32, // panic if current ledger > deadline
) -> Vec<MultiBuyResult> // { creator, amount, total_cost, new_supply }
2. Pre-flight validation
- Panic with
legs_empty if the legs vector is empty
- Panic with
too_many_legs if the vector exceeds 10 entries
- Panic with
deadline_passed if env.ledger().sequence() > global_deadline_ledger
- Panic with
duplicate_creator if any creator appears more than once in the legs vector
- Verify the buyer has sufficient total XLM balance to cover the worst-case cost of all legs (sum of
amount * max_price per leg) before executing any purchase
3. Atomic execution
- Execute legs in the order provided; for each leg call the existing single-buy logic (bonding curve cost, fee split, supply update, holder count update, TTL extension)
- If any leg's computed cost exceeds its
max_price, panic immediately — Soroban's transaction atomicity guarantees all prior legs in the same transaction are rolled back
- Emit a
KeyPurchased event per leg (reuse the existing event format)
- Emit a single
MultiBuyCompleted event at the end with buyer, leg_count, total_cost, and ledger
4. Fee handling
- Fees for each leg are computed and split independently per leg (protocol treasury and creator recipient may differ per creator)
- Total XLM deducted from the buyer equals the sum of all per-leg gross costs (including fees)
5. Unit and integration tests
- Test 3 legs all within
max_price — assert all three creators' supplies increment and events emitted
- Test 3 legs where the second leg's price exceeds
max_price — assert all three creators' supplies are unchanged (full rollback)
- Test duplicate creator in legs — assert panic with
duplicate_creator
- Test legs vector length 11 — assert panic with
too_many_legs
- Test
global_deadline_ledger in the past — assert panic with deadline_passed
- Test buyer balance insufficient for worst-case cost — assert panic with
insufficient_funds before any supply is mutated
Acceptance Criteria
ETA: 24 hours
Coordinate on Telegram
Summary
Investors who want to diversify across multiple creators must submit one transaction per creator, leaving them exposed to price movements between each submission. This issue implements a
multi_buyfunction that accepts a vector of(creator, amount, max_price_per_key)tuples, executes all purchases atomically in a single Soroban transaction, and panics with a full rollback if any single leg's price exceeds itsmax_price— guaranteeing either all legs execute or none do.Scope
1. Function signature
2. Pre-flight validation
legs_emptyif the legs vector is emptytoo_many_legsif the vector exceeds 10 entriesdeadline_passedifenv.ledger().sequence() > global_deadline_ledgerduplicate_creatorif any creator appears more than once in the legs vectoramount * max_priceper leg) before executing any purchase3. Atomic execution
max_price, panic immediately — Soroban's transaction atomicity guarantees all prior legs in the same transaction are rolled backKeyPurchasedevent per leg (reuse the existing event format)MultiBuyCompletedevent at the end withbuyer,leg_count,total_cost, andledger4. Fee handling
5. Unit and integration tests
max_price— assert all three creators' supplies increment and events emittedmax_price— assert all three creators' supplies are unchanged (full rollback)duplicate_creatortoo_many_legsglobal_deadline_ledgerin the past — assert panic withdeadline_passedinsufficient_fundsbefore any supply is mutatedAcceptance Criteria
max_priceMultiBuyCompletedevent emitted only when all legs succeedETA: 24 hours
Coordinate on Telegram