Available on devnet. Production support is rolling out; until then, production requires
recipientWallet and ignores destinations. Send exactly one of the two.destinations instead of recipientWallet, and the route pays each destination its share after the last hop.
It costs noticeably less than one route per recipient. Most of a route’s cost is per hop, not per destination, so an extra destination adds only its payout transactions and a small, refundable step account.
How it differs from a single recipient
No destination is written into the route’s hop list. Each one is paid from an on-chain commitment after the route finishes.
1. Estimate
PassdestinationCount so the SOL estimate includes the payout accounts:
sol.breakdown.dispersionRentLamports is the extra rent. It comes back when the step accounts close.
2. Create
Each destination’samountRaw is its share of the transfer’s amountRaw, and the shares must sum to it exactly. Fees come off the total once, and each destination receives its proportional share of what’s left (the hop amount):
recipientReceivesRaw:
- exactly one of
recipientWalletordestinations; - shares sum to
amountRaw; wallets are distinct; at most 20; - SOL: each destination must receive at least the rent-exempt minimum after fees, because a payout creates the destination account (
MH_017); - SPL: needs a program version with SPL payout support on the cluster you’re calling, otherwise
MH_017.
3. Prepare and broadcast
/prepare returns one extra group, dispersionInitTxs. It sets up the payout orchestrator, one committed step per destination, and the route’s exit binding. Sign each tx with sourceOwner and broadcast in order, after routeInitTxs:
resume.dispersion shows how much of the binding is already on chain, and resumed calls omit anything that has landed.
4. Confirm
Pass the new signatures asdispersionInitSignatures:
5. Track payouts
After the last hop, the keeper pays each destination and records the payout signature.GET /transfers/{id} shows each destination’s status (pending → paid), payoutSignature and paidAt.
The transfer stays active until every destination is paid, then becomes completed, and transfer.completed fires. For webhooks, completed means everyone was paid.
