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.

KindSubmitted by
Go off the recordThe funding wallet, which signs and pays for its own deposit.
SendThe relayer.
Go back on the recordThe 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.

quote.ts
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.expiresAt

amount 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.

TypeScript
await account.send({ to, mint, amount }, { feePayer })