Watch a transaction confirmation
You are watching a specific Chia tx_id (spend bundle name) and notifying when it lands on-chain — or when it evicts without confirming. Stripe-webhook feel for self-custody. Read-only.
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. - A notification MCP — pick whichever is installed (email, push, chat, webhook). If none, write to
~/.chia-skills/alerts/and surface inline. - Bash + Read + Write — state file management.
Resolve inputs
You need: $tx_id, $channel, $timeout_minutes.
$tx_id— the spend bundle name as0x...hex (32 bytes / 64 hex chars). Required.$channel—email,push,slack,discord,telegram,webhook, orauto(default).$timeout_minutes— give up watching after this many minutes if neither confirmed nor evicted. Default120(two hours, comfortably beyond Chia's mempool TTL).
Resolve: arguments → context → ask user. If $tx_id is missing after asking, emit STATUS: aborted, REASON: missing required inputs.
Validate $tx_id:
- Must be 32 bytes hex, with or without leading
0x. Anything else →STATUS: failed,REASON: invalid tx_id.
Load or initialise state
State file: ~/.chia-skills/state/watch-tx-confirmation__<tx_id_no_prefix>.json.
Shape:
{
"tx_id": "0x...",
"first_seen_iso": "2026-05-19T14:33:00Z",
"first_seen_height": 6532109,
"last_state": "pending" | "confirmed" | "evicted" | "unseen",
"confirmed_height": null,
"skill_version": "0.1.0"
}
On first run: record first_seen_iso = now, first_seen_height = current peak, last_state = unseen, write state. (Continue to the check below — first run still does work.)
Check status
Use chia-explorer to determine the current state of the tx_id in this priority:
- Is it in the mempool right now? If yes → state is
pending. - Was it confirmed on-chain? Walk recent blocks since
first_seen_heightlooking for a spend bundle whose name matches$tx_id. Stop at peak. If found → state isconfirmedand record the height. - Otherwise → state is either
unseen(never observed) orevicted(was pending earlier, no longer in mempool, not confirmed).- If
last_statewaspendingand it's no longer in the mempool and not confirmed →evicted. - If
last_statewasunseenand it's still nowhere → stillunseen.
- If
If (now - first_seen_iso) > $timeout_minutes and state is still unseen or pending → treat as timed out.
Decide whether to notify
Notify only on a state transition (the whole point of being schedulable):
| Previous state | New state | Notify? |
|---|---|---|
| any | confirmed (first time) |
yes |
pending |
evicted |
yes |
unseen |
pending |
optional — only if the user asked to know when it hit the mempool |
unseen |
unseen |
no |
pending |
pending |
no |
| any | timed out | yes (one-shot) |
Default behaviour: only notify on confirmed, evicted, or timed out. Mention "still pending" in the run output but don't send a push.
Compose the notification
Confirmed example:
Chia tx confirmed: 0xdead...beef
Block height: 6,532,210
Time to confirm: ~12 minutes
See: https://www.spacescan.io/spend-bundle/0xdeadbeef... (if you want to link)
Evicted example:
Chia tx evicted from mempool: 0xdead...beef
Was pending since: 2026-05-19 14:33 UTC
Likely cause: fee too low for inclusion. Rebroadcast with a higher fee.
Timed out example:
Chia tx watch timed out: 0xdead...beef
Last state: unseen
Watched for: 120 minutes
Action: it may have never been broadcast — check your wallet.
Use chia-explorer's puzzle-classification on at least one spend in the bundle (if confirmed) to enrich the message: "This was a take_offer for X CAT" or "This was a plain XCH send".
Send the notification
Pick a channel by priority (same as watch-address). Fall back to inline file at ~/.chia-skills/alerts/ if none available.
Update state
After successful notify (or fallback write), update last_state and, for confirmed, confirmed_height. Write state atomically.
If new state is terminal (confirmed, evicted, timed out), this watch is done. Tell the user to remove the schedule entry for this tx_id — terminal state means future scheduled runs are wasted work. Emit a TERMINAL: true field in the output block to make this obvious.
Scheduling
This skill is one-shot. Wire to:
scheduleskill / cron — every minute or two for short waits, every 5–10 minutes for longer./loopskill — handy if the user wants to babysit a single tx ("I'll wait while it confirms").
Hard rules
- Read-only. Never re-broadcast, never re-sign. If the tx evicted, the user re-broadcasts from their wallet with a higher fee.
- No retroactive notifications. First-run state recording is the baseline.
- Notify on transitions only. Don't spam the user with a "still pending" alert every minute.
Recovery patterns
- chia-explorer call fails. Don't update state. Emit
STATUS: failed. Next scheduled run resumes. - State file says
confirmedalready. Skill exits immediately withSTATUS: no_change,TERMINAL: true. Tells the user the schedule is stale.
Output
STATUS: success | no_change | partial | failed | aborted
STATE: pending | confirmed | evicted | timed_out | unseen
CONFIRMED_HEIGHT: <int> | none
CHANNEL: <channel used> | inline | none
TERMINAL: true | false
TX_ID: <full tx_id>
ARTIFACT: <path to inline alert file> | none
REASON: <one line, only when STATUS is failed, partial, or aborted>