Stablecoin pause / blacklist monitor
Recipe (no bundled template) ยท category: monitoring ยท risk: monitor_only
Page the moment a stablecoin issuer freezes the token or blacklists an
address. Centrally-controlled stablecoins (USDC/USDT-style) can pause() all
transfers or blacklist a specific account โ if your protocol holds or routes
that token, you want to know before your next transfer reverts. This recipe
watches the issuer's Paused / Unpaused / Blacklisted events and keeps
a paused() read as a backstop in case a log is missed.
Closest bundled starting point: the large transfer alert template โ the same no-signer, event โ notify shape. Swap its ABI + events for the ones below.
When to use it
- you custody or route a centrally-controlled stablecoin (USDC, USDT, โฆ)
- you want to freeze your own flows the instant the issuer pauses the token
- you need to know if one of your addresses gets blacklisted
The config
Four workflows, one contract, no signer. Each event workflow is a thin
notify; the fourth is a read-trigger backstop that pages if paused() ever
reads true.
# recipe: full
rflow_version: 1
name: stablecoin-pause-monitor
config:
port: 3948
db_connection: ${DATABASE_URL}
# monitor-only: no signer and no relayers, so neither embedded engine boots.
# The event workflows index the token; the read workflow polls over eth_call.
networks:
- name: ethereum
chain_id: 1
rpc: ${ETH_RPC}
contracts:
# a USDC/USDT-style token that exposes Pausable + a blacklist. Swap in the
# real token address and an ABI carrying its pause/blacklist events.
Stablecoin:
abi: ./abis/stablecoin.json
addresses:
ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
notifications:
channels:
ops:
telegram:
bot_token: ${TG_BOT_TOKEN}
chat_id: ${TG_CHAT_ID}
workflows:
# the token was paused - every transfer is now frozen. Page immediately.
stablecoin-paused:
trigger:
event:
contract: Stablecoin
name: Paused
network: ethereum
confirmations: 0
start_block: latest
end_block: live
steps:
- id: alert
notify:
channel: ops
message: "STABLECOIN PAUSED on ethereum by ${{ trigger.args.account }} - all transfers frozen (tx ${{ trigger.tx_hash }})"
on_failure: dead_letter
# the token was unpaused - transfers resume.
stablecoin-unpaused:
trigger:
event:
contract: Stablecoin
name: Unpaused
network: ethereum
confirmations: 0
start_block: latest
end_block: live
steps:
- id: alert
notify:
channel: ops
message: "stablecoin UNPAUSED on ethereum by ${{ trigger.args.account }} - transfers resumed (tx ${{ trigger.tx_hash }})"
on_failure: dead_letter
# an address was blacklisted - its balance is frozen. If it is one of yours,
# you want to know before your next transfer reverts.
stablecoin-blacklisted:
trigger:
event:
contract: Stablecoin
name: Blacklisted
network: ethereum
confirmations: 0
start_block: latest
end_block: live
steps:
- id: alert
notify:
channel: ops
message: "stablecoin BLACKLISTED ${{ trigger.args.account }} on ethereum (tx ${{ trigger.tx_hash }})"
on_failure: dead_letter
# backstop: even if a Paused log is missed, this read trigger pages the
# moment paused() first reads true (false->true crossing, edge-triggered)
stablecoin-paused-backstop:
trigger:
read:
contract: Stablecoin
network: ethereum
function: "paused()"
every: 1m
condition: "${{ output == true }}"
mode: threshold
steps:
- id: alert
notify:
channel: ops
message: "stablecoin paused() reads TRUE on ethereum - transfers are frozen"
on_failure: dead_letterRequired env vars
DATABASE_URL, ETH_RPC, TG_BOT_TOKEN, TG_CHAT_ID.
Safety notes โ monitor-only
- No signer, no relayers โ the config declares neither, so the relayer engine never boots and this workflow can only ever read and notify.
on_failure: dead_letterjournals a failed notification instead of losing it โ a paged alert you can replay.confirmations: 0favours latency (you want a pause alert now); a pause in a reorged block would re-alert on the canonical block. Raise it if you prefer certainty over speed. Thepaused()backstop covers a missed log.- The bundled
stablecoin.jsonhere carries genericPaused(address)/Unpaused(address)/Blacklisted(address)events; real issuers differ (USDT usesPause()/Unpause()with no args andAddedBlackList(address)). Fetch the real ABI (rflow abi fetch) and adjust the event names.
Common modifications
- Escalate a pause to PagerDuty/Opsgenie instead of telegram โ add the
channel (
rflow add notification) and point thepausedworkflow at it. - Only my addresses: filter
stablecoin-blacklistedwith awhere: "${{ lower(trigger.args.account) in constants.mine }}"over a list of your addresses. - Freeze your own flows: have the
pausedworkflow flip astate:flag your money workflows gate on with anif:.