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:
| Parameter | Type | Required | Description |
|---|---|---|---|
account | string[] | No | Specific program account public keys to monitor (e.g., the program's executable account) |
owner | string[] | Yes* | Set to ["BPFLoaderUpgradeab1e11111111111111111111111"] to watch all BPF Loader accounts |
filters | SubscribeRequestFilterAccountsFilter[] | No | Additional data filters to narrow results |
nonempty_txn_signature | bool | No | Only 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:
| Field | Type | Description |
|---|---|---|
pubkey | bytes | The public key of the updated account (program data account or executable account) |
lamports | uint64 | Current lamport balance |
owner | bytes | Will be the BPF Loader Upgradeable program ID |
executable | bool | Whether this is the executable account (true) or the program data account (false) |
data | bytes | Raw account data -- for program data accounts, this contains the ELF binary |
write_version | uint64 | Monotonically increasing version number |
txn_signature | bytes | Signature of the deploy/upgrade transaction |
slot | uint64 | Slot in which the deploy/upgrade occurred |
is_startup | bool | Whether 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 Case | Filter Configuration |
|---|---|
| Monitor all program deploys | owner: ["BPFLoaderUpgradeab1e11111111111111111111111"] |
| Watch a specific program | account: ["yourProgramDataAccount"] |
| Security monitoring | owner: ["BPFLoaderUpgradeab1e11111111111111111111111"], nonempty_txn_signature: true |
| Track DeFi protocol upgrades | account: ["specificProgramExecutable"] or watch program data accounts |
| Governance tracking | Monitor upgrade authority changes on critical programs |
| Competitor intelligence | Watch specific program accounts for upgrades and new deployments |