Staking Monitoring (all contract events)

{
  "subscribe": {
    "events": ["TxnLog"],
    "filters": [{
      "event_name": "TxnLog",
      "field_filters": [
        {"field": "address", "filter": {"values": ["0x0000000000000000000000000000000000001000"]}}
      ]
    }]
  }
}

Uses TxnLog filtered by the staking contract address.

Only Delegate + Undelegate (OR logic)

{
  "subscribe": {
    "events": ["TxnLog"],
    "filters": [
      {
        "event_name": "TxnLog",
        "field_filters": [
          {"field": "address", "filter": {"values": ["0x0000000000000000000000000000000000001000"]}},
          {"field": "topics", "filter": {"values": ["0xe4d4df1e1827dd28252fd5c3cd7ebccd3da6e0aa31f74c828f3c8542af49d840"]}}
        ]
      },
      {
        "event_name": "TxnLog",
        "field_filters": [
          {"field": "address", "filter": {"values": ["0x0000000000000000000000000000000000001000"]}},
          {"field": "topics", "filter": {"values": ["0x3e53c8b91747e1b72a44894db10f2a45fa632b161fdcdd3a17bd6be5482bac62"]}}
        ]
      }
    ]
  }
}

Distinguishing delegate() vs compound() via function_selector

Both calls emit the Delegate event. The difference is in the function selector:

{
  "subscribe": {
    "events": ["TxnHeaderStart"],
    "filters": [{
      "event_name": "TxnHeaderStart",
      "field_filters": [
        {"field": "to", "filter": {"values": ["0x0000000000000000000000000000000000001000"]}},
        {"field": "function_selector", "filter": {"values": ["0x84994fec"]}}
      ]
    }],
    "correlate": true
  }
}

0x84994fec = delegate(uint64). With correlate: true you get the full chain: TxnHeaderStart → TxnLog → TxnEvmOutput → TxnEnd.

ERC-20 Transfer Monitoring

{
  "subscribe": {
    "events": ["TxnLog"],
    "filters": [{
      "event_name": "TxnLog",
      "field_filters": [
        {"field": "address", "filter": {"values": ["0xTOKEN_ADDRESS"]}},
        {"field": "topics", "filter": {"values": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}}
      ]
    }]
  }
}

0xddf252ad... = keccak256("Transfer(address,address,uint256)").

Bridge Relay — Finalized Events

{
  "subscribe": {
    "events": ["TxnLog"],
    "filters": [{
      "event_name": "TxnLog",
      "field_filters": [
        {"field": "address", "filter": {"values": ["0xBRIDGE_CONTRACT"]}}
      ]
    }],
    "min_stage": "Finalized"
  }
}

min_stage: "Finalized" ensures only irreversible events are received.

Wallet Tracking (all transactions by sender)

{
  "subscribe": {
    "events": ["TxnHeaderStart"],
    "filters": [{
      "event_name": "TxnHeaderStart",
      "field_filters": [
        {"field": "sender", "filter": {"values": ["0xYOUR_WALLET"]}}
      ]
    }],
    "correlate": true
  }
}

Dashboard: TPS + Lifecycle + ContentionData

{"subscribe": ["TPS", "Lifecycle", "ContentionData"]}

3 items = within the default limit. Uses TPS, Lifecycle, and ContentionData.

Only Finalized Logs

{
  "subscribe": {
    "events": ["TxnLog", "BlockFinalized"],
    "min_stage": "Finalized"
  }
}

Finality-Safe Event Processing (Lifecycle Buffering)

For applications that must only act on finalized data (bridges, exchanges, payment processors):

// Subscribe to events + Lifecycle (3 items = within default limit)
ws.send(JSON.stringify({ subscribe: ["TxnLog", "Lifecycle", "BlockFinalized"] }));

const pendingBlocks = new Map(); // block_number → events[]

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  // Buffer events by block
  if (msg.Events) {
    for (const e of msg.Events) {
      if (e.block_number == null) continue;
      if (!pendingBlocks.has(e.block_number)) {
        pendingBlocks.set(e.block_number, []);
      }
      pendingBlocks.get(e.block_number).push(e);
    }
  }

  // Process on finalization, discard on rejection
  if (msg.lifecycle) {
    const { block_number, to_stage } = msg.Lifecycle;

    if (to_stage === "Finalized") {
      const events = pendingBlocks.get(block_number);
      if (events) {
        for (const e of events) {
          processConfirmedEvent(e); // safe — this block is irreversible
        }
        pendingBlocks.delete(block_number);
      }
    }

    if (to_stage === "Rejected") {
      pendingBlocks.delete(block_number); // block was dropped, discard events
    }
  }

  // Cleanup: remove old pending blocks (safety net)
  for (const [bn] of pendingBlocks) {
    if (pendingBlocks.size > 100) {
      pendingBlocks.delete(bn);
      break; // remove oldest
    }
  }
};

Why not just use min_stage: "Finalized"? You can, and it's simpler. But the buffering pattern gives you:

  • Real-time visibility: You see events as they arrive (Proposed stage) and can show pending state
  • Confirmed processing: You only act on finalized events
  • Rejection awareness: You know when a block is rejected and can update UI accordingly

Staking Precompile

Contract address: 0x0000000000000000000000000000000000001000

Event Signatures (topic[0])

Eventtopic[0] (keccak256)
Delegate(uint64,address,uint256,uint64)0xe4d4df1e1827dd28252fd5c3cd7ebccd3da6e0aa31f74c828f3c8542af49d840
Undelegate(uint64,address,uint8,uint256,uint64)0x3e53c8b91747e1b72a44894db10f2a45fa632b161fdcdd3a17bd6be5482bac62
Withdraw(uint64,address,uint8,uint256,uint64)0x63030e4238e1146c63f38f4ac81b2b23c8be28882e68b03f0887e50d0e9bb18f
ClaimRewards(uint64,address,uint256,uint64)0xcb607e6b63c89c95f6ae24ece9fe0e38a7971aa5ed956254f1df47490921727b
ValidatorRewarded(uint64,address,uint256,uint64)0x3a420a01486b6b28d6ae89c51f5c3bde3e0e74eecbb646a0c481ccba3aae3754
ValidatorCreated(uint64,address,uint256)0x6f8045cd38e512b8f12f6f02947c632e5f25af03aad132890ecf50015d97c1b2
CommissionChanged(uint64,uint256,uint256)0xd1698d3454c5b5384b70aaae33f1704af7c7e055f0c75503ba3146dc28995920
ValidatorStatusChanged(uint64,uint64)0xc95966754e882e03faffaf164883d98986dda088d09471a35f9e55363daf0c53
EpochChanged(uint64,uint64)0x4fae4dbe0ed659e8ce6637e3c273cd8e4d3bf029b9379a9e8b3f3f27dbef809b

Function Selectors

FunctionSelector
delegate(uint64)0x84994fec
undelegate(uint64,uint256,uint8)0x5cf41514
withdraw(uint64,uint8)0xaed2ee73
compound(uint64)0xb34fea67
claimRewards(uint64)0xa76e2ca5