Docs / The Pipeline / Steps 4–5 · Relay Distribution

Steps 4–5 · Relay Distribution

The bundle must not be traceable to one funding transaction. Wallet 0 splits the budget across every bundle wallet through relay.link, on your schedule.

Step 4 — Choose the window

Enter the total duration in hours (e.g. 0.5 = 30 min) and click Build plan. The app reads wallet 0's live balance on the source chain and generates a plan:

  • Amounts — randomized around the average share, within ±30%
  • Timing — Poisson-spaced intervals rescaled to fill your window, with a randomized gap between every transfer
  • Routing — each entry is a relay.link cross-chain transfer into a different bundle wallet

The plan is persisted in localStorage (jb:fundplan) — inspectable, resumable, and it survives a closed tab.

Step 5 — Execute

Click Execute. The vault must be unlocked (the app asks for the password if needed). A one-time platform fee of $20 gates the first execution: pay it in USDC, USDT or USDG on Ethereum, Base or Robinhood to the fee address shown in the app. You can send it straight from wallet 0 (USDC/USDT on Ethereum or Base) or pay from any external wallet and paste the tx hash; the app verifies the transfer on-chain (token symbol, decimals, amount and recipient all checked against the receipt) and remembers the payment per vault (`jb:fee:`). The app then works through the plan, signing each bridge transfer with wallet 0:

  • Each transfer goes pending → sending → confirmed (or error)
  • Every send re-quotes relay.link just before broadcasting — the plan-time quote is hours old by then
  • Failed entries are retried on the next run — the per-entry journal makes the whole job crash-safe
  • The Setup step flips running while it works and done only when every entry is confirmed

fromBalance mode means the plan always matches wallet 0's real balance — an empty funder refuses at plan time, not mid-execution.

Keep the tab open while the window runs. If you close it anyway, the next open detects the interrupted plan (JBFund.wasInterrupted()) and JBFund.resume() re-spreads the overdue sends over the remaining window — ambiguous entries go back to pending, so check the explorer before resuming if a transaction may have gone out.

In the browser

CallNotes
JBFund.buildPlan({hours, fromBalance, srcChain, sourceIndex})srcChain accepts 8453, 1, or 42161
JBFund.execute({dryRun})requires unlocked vault and a verified fee payment; dryRun: true quotes everything, signs nothing
JBFee.pay({chainId, symbol}) / JBFee.verifyTx(txHash, chainId)the $20 platform fee: in-app payment from wallet 0, or on-chain verification of an external payment
JBFund.planStatus(){total, byStatus, etaSeconds} — step-5 completion is derived from this, never from a flag you could fake

Until this is done

Launch remains locked: the pipeline gate counts confirmed plan entries, and the launch wizard stays closed while any entry is unconfirmed.