Program Subscriptions

Monitor program deployments and upgrades in real time by watching account changes owned by Solana's BPF Loader programs. This is an application-level pattern built on top of Account Subscriptions -- there is no dedicated "programs" filter in the gRPC proto definition.

When a program is deployed or upgraded on Solana, the BPF Loader creates or modifies accounts owned by BPFLoaderUpgradeab1e11111111111111111111111. By subscribing to all accounts owned by this program, you receive real-time notifications whenever any program on the network is deployed, upgraded, or has its upgrade authority changed.

This is valuable for security monitoring, governance tracking, protocol analytics, and detecting when competitors deploy new versions of their programs.

Filter Parameters

Since program monitoring uses the Account subscription filter, the parameters are:

ParameterTypeRequiredDescription
accountstring[]NoSpecific program account public keys to monitor (e.g., the program's executable account)
ownerstring[]Yes*Set to ["BPFLoaderUpgradeab1e11111111111111111111111"] to watch all BPF Loader accounts
filtersSubscribeRequestFilterAccountsFilter[]NoAdditional data filters to narrow results
nonempty_txn_signatureboolNoOnly include updates with a non-empty transaction signature

*For monitoring all program activity, set owner to the BPF Loader Upgradeable program ID.

Update Payload

The update payload is identical to Account Subscriptions:

FieldTypeDescription
pubkeybytesThe public key of the updated account (program data account or executable account)
lamportsuint64Current lamport balance
ownerbytesWill be the BPF Loader Upgradeable program ID
executableboolWhether this is the executable account (true) or the program data account (false)
databytesRaw account data -- for program data accounts, this contains the ELF binary
write_versionuint64Monotonically increasing version number
txn_signaturebytesSignature of the deploy/upgrade transaction
slotuint64Slot in which the deploy/upgrade occurred
is_startupboolWhether this is an initial snapshot during connection startup

Code Examples

Monitor All Program Deployments (Node.js)

1const BPF_LOADER_UPGRADEABLE = 'BPFLoaderUpgradeab1e11111111111111111111111';
2
3stream.write({
4  accounts: {
5    programDeploys: {
6      account: [],
7      owner: [BPF_LOADER_UPGRADEABLE],
8      filters: [],
9      nonempty_txn_signature: true  // Only real deploys, not startup snapshots
10    }
11  },
12  slots: {},
13  transactions: {},
14  transactionsStatus: {},
15  blocks: {},
16  blocksMeta: {},
17  commitment: 1,
18  entry: {},
19  accountsDataSlice: [],
20  ping: null
21});
22
23stream.on('data', (update) => {
24  if (update.account) {
25    const acct = update.account.account;
26    const pubkey = Buffer.from(acct.pubkey).toString('base58');
27    const isExecutable = acct.executable;
28    const dataLen = acct.data.length;
29    const txSig = Buffer.from(acct.txn_signature).toString('base58');
30
31    if (isExecutable) {
32      console.log('Program executable updated: ' + pubkey);
33    } else {
34      console.log('Program data account updated: ' + pubkey + ' (' + dataLen + ' bytes)');
35    }
36    console.log('  Transaction: ' + txSig);
37    console.log('  Slot: ' + update.account.slot);
38  }
39});

Watch for Program Upgrades (Rust)

1use yellowstone_grpc_client::GeyserGrpcClient;
2use yellowstone_grpc_proto::prelude::*;
3use std::collections::HashMap;
4use futures::StreamExt;
5
6const BPF_LOADER_UPGRADEABLE: &str = "BPFLoaderUpgradeab1e11111111111111111111111";
7
8#[tokio::main]
9async fn main() -> anyhow::Result<()> {
10    let mut client = GeyserGrpcClient::build_from_uri("http://[IP_ADDRESS]:[PORT]")
11        .connect()
12        .await?;
13
14    let mut accounts = HashMap::new();
15    accounts.insert("programDeploys".to_string(), SubscribeRequestFilterAccounts {
16        account: vec![],
17        owner: vec![BPF_LOADER_UPGRADEABLE.to_string()],
18        filters: vec![],
19        nonempty_txn_signature: Some(true),
20    });
21
22    let request = SubscribeRequest {
23        accounts,
24        slots: HashMap::new(),
25        transactions: HashMap::new(),
26        transactions_status: HashMap::new(),
27        blocks: HashMap::new(),
28        blocks_meta: HashMap::new(),
29        commitment: Some(CommitmentLevel::Confirmed as i32),
30        entry: HashMap::new(),
31        accounts_data_slice: vec![],
32        ping: None,
33        from_slot: None,
34    };
35
36    let (_, mut stream) = client.subscribe_with_request(Some(request)).await?;
37
38    while let Some(msg) = stream.next().await {
39        match msg?.update_oneof {
40            Some(UpdateOneof::Account(update)) => {
41                if let Some(account) = update.account {
42                    let is_exec = account.executable;
43                    let data_len = account.data.len();
44                    println!("{} account: {:?} ({} bytes) at slot {}",
45                        if is_exec { "Executable" } else { "Program data" },
46                        account.pubkey,
47                        data_len,
48                        update.slot);
49                }
50            }
51            _ => {}
52        }
53    }
54
55    Ok(())
56}

Common Use Cases

Use CaseFilter Configuration
Monitor all program deploysowner: ["BPFLoaderUpgradeab1e11111111111111111111111"]
Watch a specific programaccount: ["yourProgramDataAccount"]
Security monitoringowner: ["BPFLoaderUpgradeab1e11111111111111111111111"], nonempty_txn_signature: true
Track DeFi protocol upgradesaccount: ["specificProgramExecutable"] or watch program data accounts
Governance trackingMonitor upgrade authority changes on critical programs
Competitor intelligenceWatch specific program accounts for upgrades and new deployments