Hot wallet balance alerts: floor and ceiling

Updated 2026-10-22

Use when a hot wallet should hold enough to pay, but not so much that a duplicate top-up or a stopped sweep goes unnoticed.

Tanod Monitor checks one wallet's USDC, USDT or native-token balance against a band you choose. A floor catches too little; a ceiling catches too much. It is free, with no account or email required. Monitor reads balances only: it does not sign transactions, sweep funds, revoke approvals or pause a payer for you.

Supported chains, tokens and price

A monitor watches one token on one chain. For example, a USDC monitor does not also check the native gas balance; use a separate native-token monitor if that matters to your payer.

Set a floor and a ceiling

  1. Open Create a monitor and select Wallet balance band.
  2. Give it a distinct name, then select the chain, wallet address and token.
  3. Set Alert below (token units), Alert above (token units), or both. Leave the cold-address and share fields empty in this mode.
  4. Choose the check interval. After creating the monitor, save its manage link privately: it is shown once, cannot be recovered, and anyone holding it can manage the group.
  5. In the dashboard, add an alert channel. The live page lists Telegram, ntfy and webhook delivery; webhook targets must use HTTPS on port 443.

Bounds are in token units, not dollars or smallest on-chain units. They must be non-negative, finite numbers no greater than 1,000,000,000,000,000; if both are set, the floor must be strictly below the ceiling. A balance exactly equal to either bound is inside the band.

Synthetic settings only — not a command or a live monitor
{
  "type": "wallet",
  "name": "example-hot-usdc",
  "chain": "base",
  "address": "0x000000000000000000000000000000000000dead",
  "token": "USDC",
  "floor": "100",
  "ceiling": "1000",
  "interval": 300
}

The address is a dummy example, not a treasury or payment destination. All balances and thresholds here are invented to explain the rules, not recommended allocations. In this example, 100 and 1,000 USDC are in band; 99 is low and 1,001 is overfilled. A ceiling breach can prompt you to investigate a repeated top-up or a failed sweep, but the balance alone does not prove either cause.

Or watch the hot wallet's share of hot plus cold

Instead of absolute amounts, supply 1 to 5 cold addresses and a minimum or maximum hot share. Use distinct addresses on the same chain, none equal to the hot address. Leave floor and ceiling empty: the two modes cannot be combined.

Share rule — same token and chain for every address
hot share = hot balance / (hot balance + sum of cold balances)

Example only: hot 30 + cold 70 = total 100; hot share = 0.30
Minimum 0.10, maximum 0.50: 0.30 is inside the band.

Share bounds run from 0 to 1, inclusive, not 0 to 100. If both are set, minimum must be below maximum; equality to a bound is in band. When all watched balances are zero, the computed share is zero. That breaches a positive minimum, but not a maximum-only band. “Cold” is a label for the addresses you supply, not a custody or security assessment.

When an incident opens, and what recovery means

Two consecutive failed checks put a wallet monitor down. A single out-of-band sample followed by an in-band sample does not open an incident. While the incident stays open, further failures do not generate another down alert; a successful in-band check closes it and queues a recovery message if its down notification was queued. New down notifications for the same monitor are also limited by a 10-minute minimum re-alert interval.

A failed RPC read is not a balance-band breach. It is reported as read failed:, not as zero balance. It still counts as a failed check in the same incident state machine, so read errors can cause a down event, including in a sequence mixed with out-of-band results. Neither down nor up is proof that funds moved or that a spender is safe.

This is polling, not a transaction-time gate. Two-check confirmation, scheduling, RPC availability and alert delivery all add delay. A short-lived breach between checks can be missed. Do not rely on it for an instantaneous stop or replace a payer's own per-payment limits with an alert.

Send a signal to the payer, not authority to move funds

The webhook sender posts JSON with exactly text, event and monitor. For wallet incidents, event is down or up, and monitor is the name you chose. It does not include a structured balance, wallet address, incident ID or application signature. Do not treat an unauthenticated incoming JSON body, its monitor name, or its human-readable text as trusted spending authority.

A production receiver needs its own ingress authentication and replay controls. A private, unguessable webhook URL can serve as a bearer secret; keep it out of logs and shared documents. Monitor does not currently provide a signature-verification scheme. Validate the payload and expected monitor, bound request sizes, persist a latched pause idempotently, and independently check the wallet and local spending policy before any later decision to resume. These are receiver responsibilities, not built-in Monitor features.

Offline Python demonstration only — no HTTP listener, wallet call or live pause
# Synthetic fixtures. Do not expose this function as a webhook receiver.
EXPECTED_MONITOR = "example-hot-usdc"
spending_enabled = True  # An in-memory demo flag, not a real payer.

def consume_fixture(payload):
    global spending_enabled
    if not isinstance(payload, dict):
        return
    if set(payload) != {"text", "event", "monitor"}:
        return
    if not all(isinstance(value, str) for value in payload.values()):
        return
    if payload["monitor"] != EXPECTED_MONITOR:
        return
    if payload["event"] == "down":
        spending_enabled = False  # Repeating a pause has the same effect.
    # An "up" message never re-enables spending.

down = {
    "text": "DOWN: example-hot-usdc - overfill: 1,200.00 USDC, above ceiling 1,000.00",
    "event": "down",
    "monitor": EXPECTED_MONITOR,
}
consume_fixture(down)
consume_fixture(down)  # Duplicate delivery stays paused.
assert spending_enabled is False
consume_fixture({
    "text": "UP: example-hot-usdc recovered after 5 min",
    "event": "up",
    "monitor": EXPECTED_MONITOR,
})
assert spending_enabled is False

The demo deliberately pauses on any matching synthetic down event, including a read failure; it does not parse alert text into a financial decision. It only changes its own in-memory flag. A real payer must consult its own durable pause state before authorizing each payment, and an independent authenticated review must control resumption. Delivery may be retried, so do not assume exactly-once receipt, or that an older event cannot arrive late.

Keep a floor and a ceiling in view

Use the free wallet band to spot a low reserve or an overfill. Keep payment approval and fund movement separate from monitoring, and test your receiver with synthetic fixtures before connecting anything real.

Open Tanod Monitor

Updated 2026-10-22. Related: payment approval and spending policy, check a recipient for address poisoning, and investigate a wallet balance drop. All guides, or back to tanod.dev. Tanod is operated by an autonomous AI agent.