Webhook icon
Schedule icon
If icon
Fail icon
Request icon
Script icon
Process icon
Loop icon
Set icon
SlackIncomingWebhook icon
Pause icon

Watch Solana Token and Program Authorities for Unexpected Changes

Alert on Solana mint authority, permanent delegate and program upgrade changes, and approve them from Kestra.

Categories
BusinessInfrastructure

On Solana, whoever holds a token's mint authority can create unlimited supply. Whoever holds its freeze authority, or a Token-2022 permanent delegate, can freeze or move anyone's balance. And whoever holds a program's upgrade authority can replace the code behind it. Wallets, exchanges and DeFi apps depend on these keys staying where they are, yet a key handover or a redeploy usually goes unnoticed until something breaks. This blueprint keeps an approved baseline for every mint and program you rely on and turns each change into a reviewed decision.

How it works

  1. account_changed (Webhook) runs a check when a Helius or QuickNode webhook sees activity on a watched account. periodic_check (Schedule) runs every 10 minutes and measures supply growth between checks.

  2. read_accounts fetches every mint and program in one getMultipleAccounts call, decoded by the node (jsonParsed, confirmed commitment).

  3. read_program_data reads only the 45-byte header of each program's ProgramData account (dataSlice), which holds the last deploy slot and the upgrade authority, and never the program binary.

  4. analyze (Python, standard library only) builds each account's state:

    • mints: token program, mint authority, freeze authority, and every Token-2022 extension with its authorities (permanent delegate, transfer hook, transfer fee, metadata, close authority…);
    • programs: upgrade authority and last deploy slot.

    It compares the state with the approved baseline that the KV store holds for that account, then classifies each change:

    • CRITICAL when control moved;
    • UPGRADE for a redeploy by the same authority;
    • NOTICE when an authority was revoked (fixed supply, immutable program).

    It also flags supply growth above supply_jump_percent.

  5. save_state (Loop + kv.Set) stores each account's state before anyone is notified.

  6. report posts new changes, restorations and supply jumps; propose asks on-call through a Pause (4 hours) to Approve as new baseline or Not yet, optionally for some accounts only, with a note.

  7. approve_each (Loop) re-reads the KV store and approves an account only if its state is still the one that was shown.

Error handling

  • Approvals never cover more than was shown. Each proposal carries a fingerprint of the state. If the account changed again while the approval waited, it is "NOT approved: it changed again after the proposal" and the new change is reported on its own. An only_accounts answer that matches nothing approves nothing and says so.
  • No alert storms. A change is reported once, proposed again only after remind_minutes, and reported again when the account returns to its approved state. Supply jumps are alerts, not proposals.
  • No silent empty baselines:
    • an address with no account ("no account at this address on this cluster", usually a typo or the wrong cluster) fails the run;
    • a mint listed under programs, or a program listed under mints, fails with a hint;
    • malformed entries fail before any RPC call.
  • JSON-RPC errors arrive with HTTP 200. accounts_read fails with the node's message.
  • Clear failures. RPC calls retry with backoff, and the errors alert names the task and cause and links the execution.

What this blueprint teaches about Kestra

  • The KV store as a small state database: kv() reads the baselines into a script, and kv.Set in a Loop writes them back. No external database is needed.
  • jq inside Pebble to combine list inputs and build JSON-RPC requests.
  • Reading only what you need from a large on-chain account (dataSlice) and decoding it in a dependency-free Python script on the Process task runner.
  • A Pause whose approval is bound to a fingerprint, so a late answer cannot approve something newer.

Prerequisites

  • A Solana RPC endpoint: any provider, or the public endpoint for light use. Use the cluster where your tokens live.
  • A Slack incoming webhook.
  • Python 3 on the Kestra worker (included in the Kestra image) for the Process task runner.

Secrets

  • SOLANA_RPC_URL: Solana JSON-RPC endpoint.
  • SLACK_WEBHOOK_URL: Slack incoming webhook.

Inputs

  • mints (ARRAY): Label=mintAddress entries, SPL Token or Token-2022. The defaults watch USDT and PYUSD on mainnet.
  • programs (ARRAY): Label=programId entries. The defaults watch Raydium AMM v4 and Orca Whirlpool.
  • supply_jump_percent (FLOAT, default 5): supply growth between checks that triggers an alert.
  • remind_minutes (INT, default 60): how often an unapproved change is proposed again.
  • explorer_url, kestra_url (STRING): links in Slack.

Outputs

  • accounts (JSON): each mint and program with its state, whether it is approved, what changed, and any supply jump.

Quick start

  1. Add the secrets and run the flow with the default inputs against mainnet. Slack lists each account with its authorities; PYUSD shows its Token-2022 extensions, including the permanent delegate.
  2. Replace mints and programs with what your product holds, routes through or integrates with. Up to 100 accounts per flow.
  3. Point a Helius or QuickNode webhook for these accounts at account_changed, and change its key.
  4. When an issuer or protocol announces a change, approve it with a link to the announcement as the note.
  5. Adjust supply_jump_percent to the normal issuance rate of each token you watch; split fast-growing tokens into their own copy of the flow if needed.

Plugins used

See How

New to Kestra?

Use blueprints to kickstart your first workflows.