Skip to content

Latest commit

 

History

36 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multisig Monitor

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.

Architecture

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.

Project Structure

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

Quick Start

Rust (Cloudflare Workers)

cd rs
rustup target add wasm32-unknown-unknown
cargo test --workspace
npx wrangler dev   # starts on http://localhost:8787

Rust (AWS Lambda)

cd rs
cargo install cargo-lambda
cargo test --workspace
cargo lambda build -p multisig-monitor-lambda --release --arm64

TypeScript (Cloudflare Workers)

cd ts
npm install
npm test
npx wrangler dev   # starts on http://localhost:8787

TypeScript (Vercel Edge)

cd ts
npm install
npm test
npx vercel dev     # starts on http://localhost:3000

See the examples/ directory for full deployment guides.

Slack App Setup

Import the included manifest to create a Slack app with incoming webhooks:

  1. Go to api.slack.com/apps
  2. Click Create New AppFrom an app manifest
  3. Paste the contents of examples/slack-manifest.yml
  4. Go to Incoming WebhooksAdd New Webhook to Workspace
  5. Select your notification channel and copy the webhook URL

For multi-multisig routing, repeat steps 4-5 for each channel and configure MULTISIG_ROUTES.

Configuration

All configuration is provided through environment variables. The same variable names apply to every implementation and deployment target.

Required

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"

Optional Filters

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-Specific Configuration

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

Filter Behavior

  • When INSTRUCTION_TYPES is empty: all 32 instruction types produce notifications.
  • When INSTRUCTION_TYPES is set: only listed types produce notifications. Any instruction not in the list is silently dropped.
  • When CONFIG_TYPES is empty: all 7 config action types are included in config_transaction_create notifications.
  • When CONFIG_TYPES is set: only listed config action types are included. If a config_transaction_create instruction contains no matching actions after filtering, the notification is suppressed entirely.
  • When INSTRUCTION_TYPES is set and does not include config_transaction_create: the CONFIG_TYPES setting 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.

Instruction Types Glossary

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.

Multisig Creation

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).

Proposal Lifecycle

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.

Transaction Creation

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.

Transaction Execution

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.

Direct Config Changes (Controlled Multisigs)

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.

Spending Limits

Value Description
spending_limit_use A member uses a spending limit to transfer tokens without a proposal. Notification includes the amount and decimals.

Buffer Operations

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.

Batch Operations

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.

Account Cleanup

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.

Config Types Glossary

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.

Notification Format

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 Approval

Proposal Approved

Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Approver: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS

Proposal: Prop1ABCDEFGHijk234567890abcdefghijklmnopqrstuv

Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)

Config Transaction Created (Proposal-Based)

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

Vault Transaction Executed

Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Executor: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS

Transaction: TxAddr1234567890abcdefghijklmnopqrstuvwxyz12
Proposal: PropAddr567890abcdefghijklmnopqrstuvwxyz1234

Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)

Direct Member Added (Controlled Multisig)

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 (Controlled Multisig)

Direct: Threshold Changed

Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Config Authority: AuthKeyABCDEF1234567890abcdefghijklmnopqrstuv

New Threshold: 3

Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)

Spending Limit Used

Spending Limit Used

Multisig: SMPLDaGKqbPfi8NhZMNGH2fRYU3WbNRZVj3xnTjEjXc
Member: 9xKzmR2pLn4sHj7wBcDfAe8qYt6vXkZ3nPo1uWr5mQjS

Amount: 1000000 (decimals: 6)

Tx: 5nNtjezQMYBHvgSQmoRmJPiXGsPAWmJPoGSa64xanqrauogiVz (link to Solscan)

Endpoints

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.

Squads v4 Program

This monitor targets the Squads Multisig Program v4:

  • Program ID: SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf
  • Network: Solana Mainnet
  • Framework: Anchor (instructions identified by 8-byte SHA-256 discriminators)

Why the Squads types are duplicated

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.

Development

Rust

cd rs
cargo test --workspace
cargo clippy --workspace

TypeScript

cd ts
npm install
npm test
npm run lint

License

AGPL-3.0

About

A simple monitoring application for screening and emitting notifications to multisig config and state modifications

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages