Real-time Slack notifications for Squads Protocol v4 multisig activity on Solana.
Monitors configured multisig addresses via Helius raw webhooks and sends rich Slack notifications when members create, vote on, or execute transactions. Decodes Squads v4 instruction data on-chain — including config transaction actions like adding members or changing thresholds — so notifications include full operational context.
Helius (raw webhook) ──POST──> Worker ──POST──> Slack Incoming Webhook
A stateless HTTP handler receives raw transaction data from Helius, parses Squads v4 instructions by matching Anchor discriminators, resolves the acting member from the transaction's account keys, and posts a formatted notification to Slack.
The monitor is implemented in two languages with equivalent functionality. Choose whichever fits your toolchain:
| Implementation | Directory | Deployment Targets |
|---|---|---|
| Rust | rs/ |
Cloudflare Workers (WASM), AWS Lambda (native binary) |
| TypeScript | ts/ |
Cloudflare Workers, Vercel Edge Functions |
Both implementations produce identical Slack notifications, support the same configuration variables, and parse all 32 Squads v4 instructions.
multisig-monitor/
├── README.md
├── examples/ # Deployment and setup guides
│ ├── rust-cloudflare-worker.md # Rust → Cloudflare Workers
│ ├── rust-aws-lambda.md # Rust → AWS Lambda
│ ├── rust-local-development.md # Rust local dev
│ ├── typescript-cloudflare-worker.md # TypeScript → Cloudflare Workers
│ ├── typescript-vercel.md # TypeScript → Vercel Edge Functions
│ └── typescript-local-development.md # TypeScript local dev
├── rs/ # Rust implementation
│ ├── Cargo.toml # Workspace root
│ ├── wrangler.toml # CF Workers deployment config
│ └── crates/
│ ├── core/ # Shared library (multisig-monitor-core)
│ │ └── src/
│ │ ├── error.rs # Error types
│ │ ├── filter.rs # Notification filtering
│ │ ├── squads/ # Squads v4 instruction parsing
│ │ │ ├── discriminator.rs # Anchor discriminator computation
│ │ │ ├── accounts.rs # Instruction account index mapping
│ │ │ ├── config_action.rs # Borsh deserialization of instruction args
│ │ │ └── types.rs # SquadsOperation, DetectedOperation, ConfigChange
│ │ ├── webhook/ # Helius webhook payload handling
│ │ └── slack/ # Slack notification building
│ ├── worker-cf/ # Cloudflare Worker binary (WASM)
│ └── worker-lambda/ # AWS Lambda binary (native)
└── ts/ # TypeScript implementation
├── package.json
├── tsconfig.json
├── wrangler.toml # CF Workers deployment config
├── vercel.json # Vercel Edge Functions config
├── src/
│ ├── index.ts # Shared fetch handler
│ ├── config.ts # Env var parsing
│ ├── handler.ts # Webhook request handler
│ ├── filter.ts # Notification filtering
│ ├── webhook.ts # Auth validation, payload types
│ ├── slack.ts # Slack Block Kit payload building
│ └── squads/ # Squads v4 instruction parsing
│ ├── discriminator.ts # SHA-256 via Web Crypto API
│ ├── accounts.ts # Instruction account index mapping
│ ├── decoder.ts # Minimal Borsh reader + instruction decoders
│ ├── types.ts # SquadsOperation, ConfigChange, display helpers
│ └── index.ts # extractOperations pipeline
└── api/ # Vercel Edge Function entry points
├── webhook.ts
└── health.ts
cd rs
rustup target add wasm32-unknown-unknown
cargo test --workspace
npx wrangler dev # starts on http://localhost:8787cd rs
cargo install cargo-lambda
cargo test --workspace
cargo lambda build -p multisig-monitor-lambda --release --arm64cd ts
npm install
npm test
npx wrangler dev # starts on http://localhost:8787cd ts
npm install
npm test
npx vercel dev # starts on http://localhost:3000See the examples/ directory for full deployment guides.
Import the included manifest to create a Slack app with incoming webhooks:
- Go to api.slack.com/apps
- Click Create New App → From an app manifest
- Paste the contents of
examples/slack-manifest.yml - Go to Incoming Webhooks → Add New Webhook to Workspace
- Select your notification channel and copy the webhook URL
For multi-multisig routing, repeat steps 4-5 for each channel and configure MULTISIG_ROUTES.
All configuration is provided through environment variables. The same variable names apply to every implementation and deployment target.
| Variable | Description |
|---|---|
MULTISIG_ADDRESSES |
Comma-separated list of Squads v4 multisig addresses to monitor. |
HELIUS_AUTH_TOKEN |
Shared secret you create and configure in both your worker and the Helius webhook. Helius sends it in the Authorization header of each POST; the worker validates it. This is not a Helius API key. |
SLACK_WEBHOOK_URL |
Slack incoming webhook URL where notifications are sent. |
Example:
MULTISIG_ADDRESSES="7nYBkp3kFd9Gf2jK8mNpQr5tUvWx,9xKzmR2pLn4sHj7wBcDfAe8qYt6v"
HELIUS_AUTH_TOKEN="whsec_a1b2c3d4e5f6"
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"| Variable | Default | Description |
|---|---|---|
INSTRUCTION_TYPES |
"" (all) |
Comma-separated list of Squads instruction types to notify on. If empty or omitted, all instruction types generate notifications. |
CONFIG_TYPES |
"" (all) |
Comma-separated list of config action types to include in config_transaction_create notifications. If empty or omitted, all config action types are included. Ignored if config_transaction_create is not in INSTRUCTION_TYPES (when INSTRUCTION_TYPES is explicitly set). |
SHOW_PERMISSIONS |
false |
Show member permission badges on relevant operations (true/false). |
SHOW_TIMESTAMPS |
false |
Include block timestamp in notifications (true/false). |
TRUNCATE_ADDRESSES |
false |
Shorten addresses in display text while keeping full addresses in code blocks (true/false). |
LOG_LEVEL |
error |
Structured log verbosity: error, info, or debug. |
MULTISIG_ROUTES |
"" |
Comma-separated address:webhook_url pairs for per-multisig notification routing. Unrouted multisigs fall back to SLACK_WEBHOOK_URL. Example: addr1:https://hooks.slack.com/a,addr2:https://hooks.slack.com/b |
Example — monitor only proposal votes and config changes:
INSTRUCTION_TYPES="proposal_approve,proposal_reject,config_transaction_create"
CONFIG_TYPES="add_member,remove_member,change_threshold"Example — monitor everything (default):
INSTRUCTION_TYPES=""
CONFIG_TYPES=""Example — monitor only proposal approvals, nothing else:
INSTRUCTION_TYPES="proposal_approve"Example — monitor all instruction types but only alert on member changes in config transactions:
INSTRUCTION_TYPES=""
CONFIG_TYPES="add_member,remove_member"| Platform | Config method |
|---|---|
| Rust CF Worker | rs/wrangler.toml [vars] + wrangler secret put |
| Rust Lambda | Standard environment variables or cargo lambda deploy --env-var |
| TS CF Worker | ts/wrangler.toml [vars] + wrangler secret put |
| TS Vercel | vercel env add or Vercel dashboard |
| Local (Rust CF) | rs/wrangler.toml [vars] or rs/.dev.vars |
| Local (TS CF) | ts/wrangler.toml [vars] or ts/.dev.vars |
| Local (TS Vercel) | Shell env vars or vercel env pull |
| Local (Rust Lambda) | Shell environment variables |
- When
INSTRUCTION_TYPESis empty: all 32 instruction types produce notifications. - When
INSTRUCTION_TYPESis set: only listed types produce notifications. Any instruction not in the list is silently dropped. - When
CONFIG_TYPESis empty: all 7 config action types are included inconfig_transaction_createnotifications. - When
CONFIG_TYPESis set: only listed config action types are included. If aconfig_transaction_createinstruction contains no matching actions after filtering, the notification is suppressed entirely. - When
INSTRUCTION_TYPESis set and does not includeconfig_transaction_create: theCONFIG_TYPESsetting is ignored (config transactions are not processed at all).
Invalid values in either list cause a startup error with a message listing all valid values.
These are the valid values for INSTRUCTION_TYPES. Each maps to a Squads v4 Anchor instruction. The monitor covers all 32 non-program-config instructions in the Squads v4 program.
| Value | Description |
|---|---|
multisig_create |
Deprecated multisig creation instruction. |
multisig_create_v2 |
Creates a new multisig. Notification includes the creator, initial members with permissions, threshold, and config authority (if set). |
| Value | Description |
|---|---|
proposal_create |
A member creates a new proposal for a pending transaction. Notification includes the creator and proposal address. |
proposal_activate |
A member activates a draft proposal, making it eligible for voting. |
proposal_approve |
A member with Voter permission casts an approval vote. |
proposal_reject |
A member with Voter permission casts a rejection vote. |
proposal_cancel |
A member cancels an approved proposal before execution. |
proposal_cancel_v2 |
V2 variant of proposal cancellation with updated account handling. |
| Value | Description |
|---|---|
vault_transaction_create |
Creates a vault transaction (arbitrary instructions to execute from the multisig vault). Notification includes the transaction address for correlation. |
config_transaction_create |
Creates a config transaction that modifies multisig settings. Notification includes config actions (see Config Types Glossary) and transaction address. |
vault_transaction_create_from_buffer |
Creates a vault transaction from a previously uploaded buffer. |
| Value | Description |
|---|---|
vault_transaction_execute |
Executes an approved vault transaction. Notification includes the executor, transaction address, and proposal address for correlation back to the originating create and proposal. |
config_transaction_execute |
Executes an approved config transaction, applying config changes. Notification includes transaction address and proposal address. |
These instructions bypass the proposal flow entirely. They are only available on multisigs with a non-zero config_authority — the authority can unilaterally modify the multisig without member voting. Notifications for these operations are prefixed with "Direct:" and show the Config Authority as the acting party.
| Value | Description |
|---|---|
multisig_add_member |
Immediately adds a member. Notification includes the new member address and permissions. |
multisig_remove_member |
Immediately removes a member. Notification includes the removed member address. |
multisig_change_threshold |
Immediately changes the approval threshold. Notification includes the new threshold. |
multisig_set_time_lock |
Immediately sets the time lock duration. Notification includes the duration in seconds. |
multisig_set_config_authority |
Transfers config authority to a new address. Notification includes the new authority address. |
multisig_set_rent_collector |
Sets or removes the rent collector. Notification includes the new collector address or indicates removal. |
multisig_add_spending_limit |
Creates a spending limit. Notification includes amount, mint, and vault index. |
multisig_remove_spending_limit |
Removes a spending limit. |
| Value | Description |
|---|---|
spending_limit_use |
A member uses a spending limit to transfer tokens without a proposal. Notification includes the amount and decimals. |
| Value | Description |
|---|---|
transaction_buffer_create |
Creates a transaction buffer for large transactions that exceed a single instruction. |
transaction_buffer_extend |
Appends data to an existing transaction buffer. |
transaction_buffer_close |
Closes (deallocates) a transaction buffer. |
| Value | Description |
|---|---|
batch_create |
Creates a batch for grouping multiple vault transactions. |
batch_add_transaction |
Adds a transaction to an existing batch. Notification includes transaction and proposal addresses. |
batch_execute_transaction |
Executes a transaction within a batch. Notification includes transaction and proposal addresses. |
| Value | Description |
|---|---|
config_transaction_accounts_close |
Closes config transaction accounts and reclaims rent. |
vault_transaction_accounts_close |
Closes vault transaction accounts and reclaims rent. |
vault_batch_transaction_account_close |
Closes a vault batch transaction account. |
batch_accounts_close |
Closes batch accounts and reclaims rent. |
These are the valid values for CONFIG_TYPES. Each maps to a variant of the ConfigAction enum in the Squads v4 program, passed as arguments to the config_transaction_create instruction. A single config transaction can contain multiple actions.
| Value | Config Action | Description |
|---|---|---|
add_member |
AddMember |
Adds a new member to the multisig. The notification includes the new member's address and their permission set (any combination of Initiate, Vote, Execute). |
remove_member |
RemoveMember |
Removes an existing member from the multisig. The notification includes the removed member's address. |
change_threshold |
ChangeThreshold |
Changes the approval threshold — the number of member votes required to approve a proposal. The notification includes the new threshold value. |
set_time_lock |
SetTimeLock |
Sets or updates the time lock period (in seconds) that must elapse between proposal approval and execution. The notification includes the duration in seconds. |
add_spending_limit |
AddSpendingLimit |
Creates a spending limit allowing designated members to transfer tokens from a vault without a full proposal/vote cycle. The notification includes the token mint, amount, and vault index. |
remove_spending_limit |
RemoveSpendingLimit |
Removes an existing spending limit. The notification includes the spending limit account address. |
set_rent_collector |
SetRentCollector |
Sets or removes the rent collector address for the multisig. The notification includes the new collector address, or indicates removal if set to none. |
Notifications are sent as Slack Block Kit messages. All public key addresses and transaction signatures are shown in full (not truncated) for easy copy-paste and auditability. Each notification includes:
- Operation name with an emoji indicator
- Multisig address — full public key
- Member address and their role (Creator, Approver, Rejector, Executor, Config Authority, etc.)
- Transaction address and Proposal address when available, for correlating creates, votes, and executions
- Transaction signature as a clickable link to Solscan
- Operation-specific details: config actions, member permissions, threshold values, spending limit parameters, etc.
Proposal Approved
Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Approver: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS
Proposal: Prop1ABCDEFGHijk234567890abcdefghijklmnopqrstuv
Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)
Config Transaction Created
Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Creator: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS
Transaction: TxAddr1234567890abcdefghijklmnopqrstuvwxyz12
Config Actions:
- Add member HjK4NewMember567890abcdefghijklmnopqrstuv12 (permissions: Vote, Execute)
- Change threshold to 3
Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)
Vault Transaction Executed
Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Executor: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS
Transaction: TxAddr1234567890abcdefghijklmnopqrstuvwxyz12
Proposal: PropAddr567890abcdefghijklmnopqrstuvwxyz1234
Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)
Direct config changes bypass the proposal flow and are flagged with a warning indicator.
Direct: Member Added
Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Config Authority: AuthKeyABCDEF1234567890abcdefghijklmnopqrstuv
New Member: HjK4NewMember567890abcdefghijklmnopqrstuv12
Permissions: Initiate, Vote, Execute
Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)
Direct: Threshold Changed
Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Config Authority: AuthKeyABCDEF1234567890abcdefghijklmnopqrstuv
New Threshold: 3
Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)
Spending Limit Used
Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Member: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS
Amount: 1000000 (decimals: 6)
Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)
The webhook and health check paths differ depending on the deployment target. When configuring the Helius webhook URL, use the correct path for your runtime:
| Runtime | Webhook path | Health path |
|---|---|---|
| Rust CF Worker | /webhook |
/health |
| Rust Lambda | /webhook |
/health |
| TS CF Worker | /webhook |
/health |
| TS Vercel | /api/webhook |
/api/health |
Vercel uses the /api/ prefix because its routing is based on the api/ file directory convention. All other runtimes use the root path.
| Method | Path | Description |
|---|---|---|
POST |
/webhook (or /api/webhook on Vercel) |
Receives Helius raw webhook payloads. Validates the Authorization header, parses transactions, and sends Slack notifications. Returns 200 on success. |
GET |
/health (or /api/health on Vercel) |
Returns 200 ok. Use for uptime monitoring. |
This monitor targets the Squads Multisig Program v4:
- Program ID:
SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf - Network: Solana Mainnet
- Framework: Anchor (instructions identified by 8-byte SHA-256 discriminators)
Both the Rust and TypeScript implementations define their own Squads v4 type definitions (instruction args, ConfigAction enum, Member struct, etc.) rather than importing them from the official SDKs (squads-multisig Rust crate or @sqds/multisig npm package).
This is intentional. Both official SDKs transitively depend on heavy runtime libraries (solana-program/anchor-lang in Rust, @solana/web3.js v1.x in TypeScript) that do not compile or run in edge runtimes like Cloudflare Workers. The Rust crate requires the Solana BPF runtime and won't compile for wasm32-unknown-unknown. The npm package pulls in rpc-websockets which uses dynamic code evaluation forbidden in edge runtimes. Neither SDK offers feature flags to opt out of these dependencies.
The duplicated types in rs/crates/core/src/squads/config_action.rs (~100 lines of Borsh-derived structs) and ts/src/squads/decoder.ts (~90 lines with a minimal Borsh reader) mirror the on-chain layout exactly as defined in the Squads v4 IDL. Both are verified by unit tests. If the Squads program updates its type layouts in a future version, these definitions will need to be updated to match.
cd rs
cargo test --workspace
cargo clippy --workspacecd ts
npm install
npm test
npm run lint