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

contracts

A global registry: every contract declared here is usable from any workflow, trigger, or step by name.

rflow_version: 1
name: contract-registry
 
config:
  port: 3940
  db_connection: ${DATABASE_URL}
 
networks:
  - name: ethereum
    chain_id: 1
    rpc: ${ETH_RPC}
  - name: base
    chain_id: 8453
    rpc: ${BASE_RPC}
 
contracts: 
  USDC: 
    abi: ./abis/erc20.json
    addresses:                       # per-network map 
      ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
      base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
 
  Treasury: 
    abi: ./abis/treasury.json
    network: ethereum                # single-network shorthand 
    address: "0xDep..."
 
  UniV3Factory:                      # the factory AnyPool tracks (see below)
    abi: ./abis/univ3-factory.json
    network: ethereum
    address: "0x1F98431c8aD98523631AE4a59f267346ea31F984"
 
  AnyPool: 
    abi: ./abis/univ3-pool.json
    factory:                         # every child the factory ever deployed 
      contract: UniV3Factory
      event: PoolCreated
      network: ethereum
 
workflows:
  treasury-inflow:
    trigger:
      event: { contract: USDC, name: Transfer, network: ethereum }
    steps:
      - id: treasury-balance
        read:
          contract: USDC
          network: ethereum
          function: "balanceOf(address)"
          args: ["${{ contracts.Treasury.address }}"]
FieldDescription
abi✅ Path to the ABI json file, relative to the project directory; rflow abi fetch downloads the verified one for you
addressesPer-network map: network → address (or a list of addresses)
network + addressSingle-network shorthand; address also accepts a list
factoryFactory tracking: match every child deployed by a factory
index_eventsIndex these events for query: WITHOUT firing any workflow (see below)

Use exactly one address form (addresses, network/address, or factory); validation checks this and that every referenced network exists.

index_events — index-only analytics

index_events: indexes a contract's events into Postgres without any workflow firing. The rows are the historical dataset for query: steps and the query trigger:

rflow_version: 1
name: usdc-analytics
 
config:
  port: 3940
  db_connection: ${DATABASE_URL}
 
networks:
  - name: ethereum
    chain_id: 1
    rpc: ${ETH_RPC}
 
contracts:
  USDC:
    abi: ./abis/erc20.json
    network: ethereum
    address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    index_events: [Transfer, Approval] 
 
workflows:
  daily-volume:
    trigger:
      cron:
        expression: "0 9 * * *"
    steps:
      - id: volume
        query:
          sql: "SELECT COALESCE(SUM(value::numeric), 0) FROM rflow_idx_usdc.transfer"

Rows land in rflow_idx_<contract>.<event> (rflow_idx_usdc.transfer here; run rflow tables to see the live catalog). Indexing writes nothing to the run journal: no runs, no claims, no cursors.

Limits:

  • Indexing starts at the current head when rflow start boots the indexer; there is no per-contract backfill knob yet. For deep history, point an event-trigger workflow (which owns start_block semantics) at the contract instead.
  • index_events on a factory:-tracked contract is not supported yet; rflow validate (and boot) errors.
  • Each event name must exist in the contract's ABI (validated); declaring an event some workflow also triggers on is fine but flagged as a warning because the rows are stored twice (once per table-naming family).

Factory tracking

factory: scopes a contract to every child a factory ever deployed, past and future. The classic use is DEX pools:

rflow_version: 1
name: pool-watch
 
config:
  port: 3940
  db_connection: ${DATABASE_URL}
 
networks:
  - name: ethereum
    chain_id: 1
    rpc: ${ETH_RPC}
 
contracts:
  UniV3Factory:
    abi: ./abis/univ3-factory.json
    network: ethereum
    address: "0x1F98431c8aD98523631AE4a59f267346ea31F984"
  AnyPool: 
    abi: ./abis/univ3-pool.json
    factory: { contract: UniV3Factory, event: PoolCreated, network: ethereum } 
 
notifications:
  channels:
    ops:
      console: {}
 
workflows:
  any-pool-swap:
    trigger:
      event: { contract: AnyPool, name: Swap, network: ethereum }
    steps:
      - notify:
          channel: ops
          message: "swap in pool ${{ trigger.address }}"

An event trigger on AnyPool fires for a Swap in any pool, including pools created after rflow started.

FieldDescription
contract✅ Name of the factory contract in this registry
event✅ The deployment event on the factory (e.g. PoolCreated)
network✅ Network to track the factory on

Using registry entries

  • Event triggers: trigger: { event: { contract: USDC, name: Transfer, ... } }
  • Read steps: read: { contract: USDC, function: "balanceOf(address)", ... }
  • Send steps: send_transaction: { contract: USDC, function: "transfer(address,uint256)", ... }
  • Expressions: ${{ contracts.Treasury.address }}

Send steps may also target a raw to: "0x..." address without a registry entry; the registry is for contracts you reference more than once or need ABIs for.