The relayer
The relayer submits every send and withdrawal and pays the network fee, so an account needs no public SOL.
Who submits
Offrecord runs the relayer: a funded Solana account that signs as fee payer. The owner’s wallet signs the transfer’s message and never signs the transaction, so the owner’s address is not on it.
| Kind | Submitted by |
|---|---|
| Go off the record | The funding wallet, which signs and pays for its own deposit. |
| Send | The relayer. |
| Go back on the record | The relayer. |
ring() returns the relayer’s address and whether it is live. The Ring shows the same.
How it is paid
Every relayed transfer carries one more public leg: the relay cost, paid to the relayer out of the transfer, in the asset being moved. Nothing is charged apart from the transfer.
The leg is bound by the proof. Its recipient and amount are among the things the owner’s signature and the proof cover, along with every other recipient and amount. A transaction with any of them changed fails verification.
The quote before signing
account.quote() returns the costs of one transfer before anything is signed.
const quote = await account.quote({ kind: 'send', to, mint, amount })
quote.route // 'ring', or 'withdrawal' when the address has no account
quote.relayCost // paid to the relayer
quote.exitFee // '0' on the ring route
quote.receives // what the recipient or destination ends up with
quote.expiresAtamount is the total that leaves the off-record balance. On the ring route the recipient receives the amount less the relay cost. On the withdrawal route the destination receives the amount less the relay cost and the exit fee.
The app shows these figures on the review step. A transfer submitted after expiresAt is refused with the code expired, and a new quote replaces it.
What it sees
The relayer receives a finished transaction: two proofs, the sealed outputs, and its own leg. It checks the leg against the quote, adds the fee payer signature and sends it.
What it handles is what the chain shows afterwards. The notes inside stay sealed to their owners.
Your own fee payer
send and withdraw take an optional feePayer. With one, that signer pays the network fee and submits, and the transfer carries no relay leg.
await account.send({ to, mint, amount }, { feePayer })