YieldStreamdocsLaunch app

Issuer guide

This is the path from a spreadsheet of wallets to tokens in them. It assumes you have a Stellar testnet account and a Soroban token to distribute. The dashboard that automates this is not built yet — today you drive the contract directly.

Worked example

Four recipients sharing 120,000 units. These are the pinned values from merkle_v1.json, so you can check your own tree math against them.

indexrecipientamountproof steps
0…MDR410,0002
1…OLZM25,0002
2…RQG535,0002
3…TYON50,0002

Allocations sum to 120,000 — exactly the escrow. The resulting root is 627e65c5…153a. A 4-leaf tree gives a 2-step proof, which is log₂(4).

The steps

1. Build the recipient list

Two columns: recipient address and amount. Decide the index for each row now and never reorder the file afterwards — the index is part of the leaf hash, so a reshuffled list invalidates every proof you already handed out.

2. Compute the Merkle root

Hash each row into a leaf, then fold pairs up to a single root. The exact format is on the mechanics page; the important part is that the leaf includes the campaign id, which you do not know until the campaign is created.

This is the step with no turnkey tool today. computeLeaf and buildTree in the SDK are unimplemented (sdk #1, #2). Two workable routes right now:

  • call the contract's own compute_leaf view for each row and fold the pairs yourself — the contract is the reference implementation, so it cannot disagree with itself;
  • implement the format in your own script against the pinned vectors and verify your output matches merkle_v1.json before touching a campaign.

3. Commit the root and fund escrow

Call create_distribution with your address, the token address, the root, the total, the recipient count, and a deadline. It transfers the tokens to the contract and returns the campaign id.

Order matters: because the leaf hash commits to distribution_id, you must know the id before you can build proofs. The practical sequence is create the campaign first, read the id it returns, then build the tree and verify the root matches what you committed — or create it with a root you computed for a known next id. On a fresh contract the first campaign is id 1, which is why the vectors use 1 and 2.

4. Hand each recipient their proof

A recipient needs four things: the campaign id, their index, their amount, and their list of sibling hashes. They do not need the rest of the list, and you should not send it to them.

5. Claims happen on their schedule

Each claimer signs and pays their own transaction fee. You are not in the loop. Check progress with get_distribution — claimed_count and claimed_amount — or listen for claimed events.

6. Recover what nobody claimed

After the deadline passes, or immediately after you call cancel, call sweep(distribution_id, to). It returns the remaining balance to the address you name — use a treasury address, not the creator's hot wallet.

Before you do this with real money

  • The contract is unaudited and deployed to testnet only. Treat mainnet use as unsupported.
  • Verify your root against your list before funding. The contract will not check that allocations sum to your escrow. If they do not, you either strand tokens or reject legitimate claims.
  • Keep the list and every proof. If you lose them, nobody can claim, and the funds become recoverable only by cancel and sweep.
  • Set a deadline. end_time = 0 means the campaign never expires on its own — the escrow sits until you cancel it.
  • Read the stats with care. After a sweep, claimed_amount is written as the full total regardless of what was actually claimed (issue #8). Count claimed events if you need true figures.