Skip to content

Implement a slippage-protected multi-buy function that atomically purchases keys across multiple creators in a single transaction with all-or-nothing semantics #717

Description

@Chucks1093

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

  • All legs execute atomically — any failure rolls back all prior legs
  • Slippage protection per leg with max_price
  • MultiBuyCompleted event emitted only when all legs succeed
  • Pre-flight duplicate creator and leg count validation
  • Global deadline prevents stale order execution
  • Buyer balance pre-checked against worst-case total cost

ETA: 24 hours


Coordinate on Telegram

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third Campaign

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions