# Watch Address

> Notify when a Chia address receives or sends XCH, CATs, or NFTs since the last run. Use when the user asks to be alerted on deposits, withdrawals, or activity at an address, or wants to "watch" a wallet. Accepts address, direction, min_amount, channel as arguments for programmatic invocation, or asks the user for whatever is missing.

- Skill: `spacetime-technology/watch-address` (Agent Skill)
- Install (CLI): `npx skillmds@latest add spacetime-technology/watch-address`
- Raw SKILL.md: https://api.skillmd.com/api/skills/spacetime-technology/watch-address/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Spacetime-Technology (https://skillmd.com/u/spacetime-technology)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/spacetime-technology/watch-address

---


# Watch a Chia address

You are watching a Chia address for new activity since the last time this skill ran. You query chia-explorer (read-only, public coinset.org RPC) for recent coin records, diff against state on disk, and notify through whichever channel MCP the user has installed.

This skill follows the conventions in this repo's `SKILLS.md`. Read that if anything below is unclear.

## Required capabilities

- **chia-explorer** (`mcp__chia-explorer__*`) — mandatory, no substitute. If missing, tell the user to install it: `claude mcp add chia-explorer -- npx chia-explorer`. Then stop with `STATUS: failed`, `REASON: chia-explorer not installed`.
- **A notification MCP** — any of email (gmail / smtp), push (ntfy / pushover / pushcut), chat (slack / discord / telegram / signal), or webhook. Pick whatever's installed. If none, write the alert to `~/.chia-skills/alerts/watch-address-<timestamp>.md` and surface it inline. Don't stop the skill.
- **Bash + Read + Write** — for state file management.

## Resolve inputs

You need: `$address`, `$direction`, `$min_amount`, `$channel`.

- `$address` — `xch1...` (mainnet) or `txch1...` (testnet11). Required.
- `$direction` — one of `incoming` (default), `outgoing`, or `both`. Optional.
- `$min_amount` — minimum XCH amount per coin to alert on (default `0`, meaning every coin). Optional.
- `$channel` — `email`, `push`, `slack`, `discord`, `telegram`, `webhook`, or `auto` (default). Optional.

Resolve in this order:

1. **Skill arguments** — trust them if set.
2. **Conversation context** — an earlier turn may have named an address or a min threshold.
3. **Ask the user** — only for what's still missing, in one consolidated message. Don't ping-pong.

If `$address` is still missing after asking, emit `STATUS: aborted`, `REASON: missing required inputs`.

Validate `$address`:

- Must start with `xch1` or `txch1`. Anything else, emit `STATUS: failed`, `REASON: not a Chia address` and stop.
- Network is implied by the prefix. Pass it explicitly to chia-explorer calls anyway.

## Load or initialise state

State file: `~/.chia-skills/state/watch-address__<sanitised_address>.json`.

Sanitisation: lowercase, replace anything that isn't `[a-z0-9]` with `_`.

Shape:

```json
{
  "address": "xch1...",
  "network": "mainnet",
  "last_height": 6532109,
  "last_seen_iso": "2026-05-19T14:33:00Z",
  "skill_version": "0.1.0"
}
```

Steps:

1. Read the state file. If missing, this is a first run.
2. On first run: fetch the current chain peak height, record it as `last_height`, write the state file, and emit `STATUS: success`, `EVENTS: 0`, with a message like "watch started at height N — future runs will report activity since now". Do **not** scan history.
3. On subsequent runs: continue.

Use atomic writes for the state file: write to `<file>.tmp`, then `mv` over the original. Never leave a half-written state file.

## Look up activity

Query chia-explorer for coin records at the address with `start_height = last_height + 1` and `end_height = <current peak>`.

- For `$direction` of `incoming` or `both`: include records where the coin was *created* in this window (received).
- For `$direction` of `outgoing` or `both`: include records where the coin was *spent* in this window (sent).
- Apply `$min_amount`: drop any coin whose XCH value is below the threshold. CATs and NFTs don't have an XCH amount; alert on them regardless unless `$min_amount` is large enough that the user clearly only cares about XCH.

For each remaining coin, classify it via chia-explorer's puzzle-classification tools:

- Plain `p2` → XCH receive/send.
- CAT → note the `asset_id` and the CAT-unit amount. If the asset_id isn't a well-known one (USDS, SBX, etc.), call it "unknown CAT".
- NFT → note the `launcher_id`. The amount is always 1 mojo for NFTs.
- DID → rare at a normal address but possible.

Fetch the current chain peak height once more after the scan; you'll write that as `last_height` to avoid missing coins added during the scan.

## Compose the notification

Build a single message body covering all events in this run. Aim for human-readable, not a wall of JSON.

Example:

```
Chia watch — xch1yxq...8yl (3 new events, mainnet)

INCOMING
  +1.250 XCH    block 6,532,210   coin 0xabc...123
  +500 USDS     block 6,532,210   asset_id 0xdef...456 (CAT)

OUTGOING
  -0.050 XCH    block 6,532,217   coin 0x789...

Balance now: 12.347 XCH (~$617 at $50.00/XCH)
Heights scanned: 6,532,109 → 6,532,225
```

For fiat values, call chia-explorer's price tool once and apply to XCH amounts. If the price tool fails, drop the fiat line — don't block the alert.

## Send the notification

Pick the channel:

- If `$channel` is set, use that specific channel.
- If `$channel = auto` (default), use the first available in this priority: email, push, slack, discord, telegram, webhook.
- If no channel MCP is available, write the message to `~/.chia-skills/alerts/watch-address-<iso8601>.md` and tell the user inline.

Subject line for email / Slack / push:

> `Chia: <N> new event(s) at <last 6 chars of address>`

Body is the message composed above. Don't strip information — channel limits (Slack 4000 chars, Discord 2000) can be handled by trimming the per-event list to the first 20 entries and noting "+M more events not shown".

## Update state

After successful notification (or after writing the inline fallback), update `last_height` to the post-scan peak you captured, update `last_seen_iso` to now, and write the state file atomically.

If the notification channel failed but the scan succeeded, still update state and emit `STATUS: partial`, `REASON: notification channel failed`. Next run won't redeliver these events — that's intentional; double-alerts are worse than a missed one. (To handle channel outages, run with `$channel` set to a backup.)

## Scheduling

This skill is one-shot. To make it periodic:

- **Cron / `schedule` skill** — recommended for always-on monitoring. Example: every 5 minutes.
- **`/loop` skill** — fine for "watch this for the next hour" foreground polling.
- **Manual** — re-invoke when you want a fresh check.

Don't run more often than every 30 seconds; coinset.org has rate limits, and chain peak only advances every ~52 seconds on average anyway.

## Hard rules

- **Read-only.** Never sign, never push. If the user wants to act on what landed, tell them to use their wallet.
- **No retroactive alerts on first run.** Record current height and start fresh.
- **State writes are atomic.** Never corrupt the state file mid-write.
- **Mojos to XCH for display.** Always.
- **Truncate addresses and hashes in the message body** to the first 8 and last 6 chars unless the user explicitly wants full values.
- **Network from prefix.** If the address is `txch1...`, pass `network: "testnet11"` explicitly.
- **Respect channel failures.** If notification fails, emit `STATUS: partial`, update state, and move on.

## Recovery patterns

- **chia-explorer call fails (network error / 5xx).** Don't update state. Emit `STATUS: failed`, `REASON: chia-explorer call failed`. Next scheduled run picks up from the same `last_height`.
- **State file is corrupt.** Back it up to `<file>.broken-<timestamp>` and treat as first-run. Don't pretend to scan history.
- **Address is unknown to chia-explorer** (no coins ever).  Emit `STATUS: no_change`, `EVENTS: 0` and write a state file with current peak — first watch on a fresh address.

## Output

When you're done, emit this fenced block as the last thing in your response. A calling skill or agent will parse it; an interactive user will skim it.

```
STATUS: success | no_change | partial | failed | aborted
EVENTS: <count of events reported this run>
CHANNEL: <channel used> | inline | none
LAST_HEIGHT: <height written to state>
ADDRESS: <full address>
ARTIFACT: <absolute path to inline alert file> | none
REASON: <one line, only when STATUS is failed, partial, or aborted>
```

