Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

relayers

Named wallets your workflows send transactions from. The names are your labels β€” nothing about them is special, and you never touch a relayer API or manage the name↔key mapping yourself.

relayers:
  payout:
    networks: [ethereum, base]     # same address on all listed chains
    speed: FAST                    # SLOW | MEDIUM | FAST | SUPER
    policy:
      max_gas_price: 100 gwei      # abort sends when gas is above this
      whitelist_receivers:         # the only addresses this relayer may send to
        - "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326"
  treasury:
    networks: [ethereum]
    import: { id: "b47c..." }      # adopt an existing relayer (see below)
FieldRequiredDescription
networksβœ…Networks the relayer exists on β€” created on the first, cloned to the rest, same address everywhere
speedrrelayer transaction speed. Gas bumping/escalation is rrelayer's job
policyPer-relayer guard rails, checked by rflow on every send from this relayer β€” see below
importAdopt an existing relayer by rrelayer uuid

policy β€” per-relayer guard rails

policy.max_gas_price (a wei literal, validated statically) and policy.whitelist_receivers apply to every send from this relayer, whatever workflow issues it.

Lifecycle

Wallet creation is rflow's job and it is seamless:

  1. You declare a signing provider (the key material) and named relayers (pure labels).

  2. At first rflow start (or standalone via rflow relayers sync), rflow reconciles: each name missing from its mapping table is created through the embedded rrelayer admin client (rrelayer name rflow/<name>) on its first listed network, then cloned to the rest β€” clones reuse the wallet index, which is why the address is identical cross-chain.

  3. The mapping (name β†’ relayer id per chain, address) is persisted in the rflow schema β€” stable forever after.

  4. First boot prints a funding table:

    payout β†’ 0xAbc... on ethereum, base β€” fund me

    rflow relayers ls / rflow relayers balance show it any time, and funding operations below cover estimating, funding and auditing balances. Ongoing funding can be automated with the automatic_top_up passthrough.

Renames are handled honestly

The YAML key is the identity, so renaming it in the file would create a new wallet. rflow detects the pattern β€” a previously-mapped name gone plus a new unmapped name β€” and tells you before creating anything:

'payout' is unmapped; orphaned relayer 'hot' still holds funds at 0xAbc...
run `rflow relayers rename hot payout` to keep the wallet, or continue to create a fresh one.

rflow relayers rename <old> <new> re-labels the mapping without touching keys. rflow relayers ls always shows every mapping including orphans, and removing a name from the YAML never deletes a relayer β€” rflow validate warns about orphans instead.

Importing existing rrelayer relayers

If you share an rrelayer database with other services (a standalone rrelayer, or another rflow project on the same Postgres), adopt one of its relayers instead of creating a fresh wallet. The guided flow is discover β†’ import β†’ verify:

rflow relayers discover                # list every relayer in the shared rrelayer db
rflow relayers import treasury --id b47c1e2a-... --network ethereum
rflow relayers import --interactive    # pick from discover, prompt for the name
rflow relayers verify-imports          # re-check every import (CI-friendly)

discover shows each relayer's uuid, address, network and a signer-compatibility check β€” whether the signing provider you configured derives that relayer's address. Only compatible relayers can be imported.

import writes the block into rflow.yaml through the same comment-preserving editor as rflow add (your comments/ordering survive), refusing to clobber an existing relayer of the same name:

relayers:
  treasury:
    networks: [ethereum]
    import:
      id: "b47c1e2a-..."

The derived-address check runs before anything is written β€” a signer mismatch refuses the import loudly, because otherwise every simulation would run as the old wallet while the engine signs with a different key. The same verification runs again at every boot (a mismatch is a boot error), and rflow relayers verify-imports runs it standalone: a table of ok / MISMATCH / NOT FOUND per import, exiting non-zero on any failure so CI can gate on it. rflow doctor also checks each import's adoption state.

Funding operations

Knowing every address is half the job β€” the other half is keeping them funded:

rflow relayers balance          # balances per chain with low-balance flags (--json)
rflow relayers funding-plan     # balance vs estimated need per relayer/network (--json)
rflow relayers qr payout        # the address + a terminal QR code to fund from a wallet
rflow relayers topup-plan       # which relayers are short, and by how much
rflow relayers topup --from treasury --to "*" --network ethereum --to-target --yes
rflow relayers history --since 30d   # recent gas spend + top-up actions

funding-plan estimates how much native each relayer wants: targeting money workflows Γ— sends-of-headroom Γ— safety factor Γ— live gas price Γ— typical gas (defaults 50 sends, 3Γ—, 150k gas β€” all overridable with --sends-per-workflow / --safety-factor / --typical-gas). This is an honest gas-headroom heuristic, not a value forecast: transfer values are unknowable statically, so the safety factor is the coarse knob covering them. A relayer no money workflow targets needs nothing and is never flagged.

topup funds shortfalls from a funding relayer as normal relayer sends: the plan prints first, --yes is required to broadcast, and the command refuses when the funding relayer is itself underfunded or cannot cover the total. Every top-up lands in the native spend ledger (visible in rflow spend and the explorer) plus an rflow.operator_audit row, so rflow relayers history shows who topped up whom.

For automated funding, the automatic_top_up network passthrough lets rrelayer itself top relayers up from a funding relayer or Safe β€” the relayer-underfunded lint rule nudges money-moving projects toward it, and rflow doctor warns live when a relayer's balance drops below the funding-plan estimate. The explorer's Relayers view (backed by GET /api/relayers) shows the same address book: balances, low-balance flags, funding estimates and recent spend.

Using relayers

  • Send steps reference them by name: send_transaction: { relayer: payout, ... }
  • Expressions can read the address: ${{ relayers.payout.address }}
  • Workflows can be restricted to specific relayers via permissions