Eclipse Documentation
HomeBridge
  • 🐮Users
    • Getting Started
      • 1. Set Up Your Eclipse Wallet
      • 2. Bridge Assets for Gas and Transactions
      • 3. Explore the Eclipse Ecosystem
      • 4. Engage with the Community on Discord
    • User Resources
    • tETH
    • tUSD
  • 🛠️Developers
    • Getting Started
    • Wallet
      • Mainnet Wallets
      • Testnet & Devnet Wallets
        • Adding Eclipse Wallet to dApp
        • Custom Wallets
    • RPC & Block Explorers
      • Dragon's Mouth gRPC Subscriptions
    • Bridges
      • Eclipse Canonical Bridge
      • Hyperlane
    • Oracles
      • Pyth Network
      • Switchboard
    • NFTs
      • Metaplex
      • Nifty Asset
      • Libreplex (Token-2022)
    • Developer Tooling
      • Faucet
      • Benchmarking
        • Running AMM benchmarking tests
      • Decentralized Identities
        • AllDomains
      • OpenBook Quickstart
      • Multisig
    • Eclipse Bug Bounty Program
    • Eclipse Status Page
    • Frequently Asked Questions
    • Differences Between Eclipse and Solana
    • Eclipse Program Registry Guide
  • 📖Tutorials & Guides
    • Developer Guides
      • Quick Start: "Hello World"
        • Deployment Walkthrough
      • Reading from the blockchain
      • Modifying a Solana dApp to Support Eclipse: "Chomping Glass"
        • Developing on the Solana Virtual Machine (SVM)
        • Multi-chain toggle frontend component
      • Dapp Deployment Tutorial - Eclipse Devnet
        • ⚙️Install Dependencies - Windows
          • Step 1: Install Visual Studio Code (VSC)
          • Step 2: Install Rust and Cargo
          • Step 3: Download Visual Studio C++ Build Tools
          • Step 4: Download Node.js
          • Step 5: Install Git on Windows
          • Step 6: Install the Solana CLI
          • Step 7: Install WSL on Visual Studio Code and Upgrade to WSL2
          • Step 8: Set Up Development Environment in Ubuntu WSL
          • Step 9: Install Anchor on Windows and WSL
        • 🏝️Solana CLI & Solana Keypair
          • Step 1: Set Solana CLI to Use Eclipse Devnet
          • Step 2: Verify Solana CLI Configuration
          • Step 3: Generate a New Solana Keypair
          • Step 4: Claim Devnet ETH for Transaction Fees
          • Optional Step: View Balance on Devnet Explorer
        • 🖥️Creating an Anchor Project in Visual Studio Code
          • Step 1: Initialize Anchor Project
          • Step 2: Update the lib.rs File with Smart Contract Code
          • Step 3: Update the Smart Contract's Cargo.toml File
          • Step 4: Update the Project's Root Cargo.toml File
          • Step 5: Compile Your Program with anchor build
          • Step 6: Deploy Your Project to the Eclipse Devnet
          • Step 7: Verify Program Deployment on the Eclipse Devnet Explorer
        • ✨Building a React App Front-End
          • Step 1: Create a New React Project with TypeScript
          • Step 2: Install Solana Web3.js and Wallet Adapter Dependencies
          • Step 3: Install Additional Dependencies for Enhanced Functionality and Compatibility
          • Step 4: Configure Webpack for Browser Compatibility
          • Step 5: Start the Development Server and Verify Setup
          • Step 6: Implement the UI for Your NFT Minter in App.tsx with Updated Code
      • Eclipse Testnet ETH Transfer Transaction Fee Estimator
        • Program Breakdown
        • Program JSX & CSS
        • Program Execution
      • Pyth: How to Use Real-Time Data in Solana Programs
      • Quick Start: User Guide - Testnet
      • cNFTs on Eclipse
        • Create 1 Million NFTs on Eclipse
        • How to Interact with cNFTs
  • 🧠Eclipse Architecture
    • What is Eclipse Mainnet?
      • Settlement - Ethereum
      • Execution - Solana Virtual Machine (SVM)
      • Data Availability - Celestia
      • Proving - RISC Zero
      • Why Eclipse, Why Ethereum, Why Now
    • Lifecycle of an Eclipse Transaction
  • 📚Additional Resources
    • External Documentation
    • Disclosures
Powered by GitBook
On this page
  • Protocol files
  • NodeJS/Typescript Client
  • Initializing the client
  • Subscription Streams
  • grpcurl Client
  • Rust Client
  • Goland Client
  • Examples of Subscription Requests
  • Managing commitment levels
  • Benefits of working at processed
  • How to manage `confirmed` and `finalized`
  • The special thing about finalized

Was this helpful?

Edit on GitHub
  1. Developers
  2. RPC & Block Explorers

Dragon's Mouth gRPC Subscriptions

Streaming Account Updates for Backend Applications

PreviousRPC & Block ExplorersNextBridges

Last updated 9 months ago

Was this helpful?

Dragon's Mouth is Triton's Geyser-fed gRPC interface that supports streaming:

  • Account Writes

  • Transactions

  • Entries

  • Block notifications

  • Slot notifications

It also supports unary operations:

  • getLatestBlockhash

  • getBlockHeight

  • getSlot

  • isValidBlockhash

You can find the official Triton docs:

Protocol files

You can find the latest version of protobuf files in:

  • Github:

  • Rust Crate:

NodeJS/Typescript Client

You can include NodeJS Yellowstone gRPC client as a dependency by running the following command:

npm install --save @triton-one/yellowstone-grpc

# or, for yarn:

yarn add @triton-one/yellowstone-grpc

Initializing the client

Once you have installed the client dependency, you can initialize it as follows:

import Client from "@triton-one/yellowstone-grpc";

const client = new Client("https://api.rpcpool.com:443", "<insert your token here>");

// now you can call the client methods, e.g.:

const version = await client.getVersion(); // gets the version information
console.log(version);

Please note that the client is asynchronous, so it is expected that all calls are executed inside an async block or async function.

Subscription Streams

You can get updates and send requests through the subscription stream. You can create it by calling the client.subscribe() method:

import { SubscribeRequest } from "@triton-one/yellowstone-grpc";

// Create a subscription stream.
const stream = client.subscribe();

// Collecting all incoming events.
stream.on("data", (data) => {
  console.log("data", data);
});

// Create a subscription request.
const request: SubscribeRequest = {
  // you can use the standard JSON request format here.
  // the following documentation describes available requests and filters.
  ...
};

// Sending a subscription request.
await new Promise<void>((resolve, reject) => {
  stream.write(request, (err) => {
    if (err === null || err === undefined) {
      resolve();
    } else {
      reject(err);
    }
  });
}).catch((reason) => {
  console.error(reason);
  throw reason;
});

grpcurl Client

grpcurl is a good client for testing. You will also need the following two Protobuf proto files to describe the protocol:

Once you have these two downloaded, you can access Triton's staging environment with your token (contact Triton customer support for a token) to run gRPC requests:

./grpcurl \
  -proto geyser.proto \
  -d '{"slots": { "slots": { } }, "accounts": { "usdc": { "account": ["9wFFyRfZBsuAha4YcuxcXLKwMxJR43S7fPfQLusDBzvT"] } }, "transactions": {}, "blocks": {}, "blocks_meta": {}}' \
  -H "x-token: <token>" \
  api.rpcpool.com:443 \
  geyser.Geyser/Subscribe

Rust Client

Goland Client

Examples of Subscription Requests

Subscribe to an account
NodeJS
import { CommitmentLevel } from "@triton-one/yellowstone-grpc";

const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {
    "wsol/usdc": {
      "account": ["8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6"]
    }
  },
  "transactions": {},
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": [],
  "commitment": CommitmentLevel.CONFIRMED
};
gRPC
{"slots": { "slots": {} }, "accounts": { "wsol/usdc": { "account": ["8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6"] } }, "transactions": {}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": [], "commitment": 1}
Subscribe to an account with account_data_slice
NodeJS
import { CommitmentLevel } from "@triton-one/yellowstone-grpc";

const request = {
  "slots": {},
  "accounts": {
    "usdc": {
      "owner": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
      "filters": [{
          "tokenAccountState": true
      }, {
          "memcmp": {
              "offset": 0,
              "data": {
                  "base58": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
              }
          }
      }]
    }
  },
  "transactions": {},
  "blocks": {},
  "blocksMeta": {},
  "entry": {},
  "commitment": CommitmentLevel.CONFIRMED
  "accountsDataSlice": [{ "offset": 32, "length": 40 }],
};
gRPC
{
    "accounts": {
        "usdc": {
            "owner": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
            "filters": [{
                "token_account_state": true
            }, {
                "memcmp": {
                    "offset": 0,
                    "data": {
                        "base58": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
                    }
                }
            }]
        }
    },
    "accounts_data_slice": [{ "offset": 32, "length": 40 }]
}
Subscribe to a program
NodeJS
import { CommitmentLevel } from "@triton-one/yellowstone-grpc";

const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {
    "solend": {
      "owner": ["So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"]
    }
  },
  "transactions": {},
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": [],
  "commitment": CommitmentLevel.PROCESSED
}
gRPC
{"slots": { "slots": {} }, "accounts": { "solend": {  "owner": ["So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"] } }, "transactions": {}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": [], "commitment": 0}
Subscribe to multiple programs
NodeJS
import { CommitmentLevel } from "@triton-one/yellowstone-grpc";

const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {
    "programs": {
      "owner": [
        "So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo",
        "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
      ]
    }
  },
  "transactions": {},
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": { "slots": {} }, "accounts": { "programs": {  "owner": [ "So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo", "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"] } }, "transactions": {}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to all finalized non-vote and non-failed transactions
NodeJS
import { CommitmentLevel } from "@triton-one/yellowstone-grpc";

const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {},
  "transactions": {
    "alltxs": {
      "vote": false,
      "failed": false
    }
  },
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": [],
  "commitment": CommitmentLevel.FINALIZED
};
gRPC
{"slots": { "slots": {} }, "accounts": {}, "transactions": { "alltxs": { "vote": false, "failed": false }}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": [], "commitment": 2}
Subscribe to non-vote transactions mentioning an account
NodeJS
const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {},
  "transactions": {
    "serum": {
      "vote": false,
      "accountInclude": [
        "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
      ]
    }
  },
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": { "slots": {} }, "accounts": {}, "transactions": { "serum": { "vote": false, "account_include": [ "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" ]}}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to transactions excluding accounts
NodeJS
const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {},
  "transactions": {
    "serum": {
      "accountExclude": [
        "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
        "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
      ]
    }
  },
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": { "slots": {} }, "accounts": {}, "transactions": { "serum": { "account_exclude": [ "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ]}}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to transactions mentioning accounts & excluding certain accounts
NodeJS
const request = {
  "slots": {
    "slots": {}
  },
  "accounts": {},
  "transactions": {
    "serum": {
      "accountInclude": [
        "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
      ],
      "accountExclude": [
        "9wFFyRfZBsuAha4YcuxcXLKwMxJR43S7fPfQLusDBzvT"
      ]
    }
  },
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": { "slots": {} }, "accounts": {}, "transactions": { "serum": { "account_include": [ "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" ], "account_exclude": [ "9wFFyRfZBsuAha4YcuxcXLKwMxJR43S7fPfQLusDBzvT" ] }}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to a transaction signature
NodeJS
const request = {
  "slots": {},
  "accounts": {},
  "transactions": {
    "sign": {
      "signature": "5rp2hL9b6kexex11Mugfs3vfU9GhieKruj4CkFFSnu52WLxiGn4VcLLwsB62XURhMmT1j4CZiXT6FFtYbXsLq2Zs"
    }
  },
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": {}, "accounts": {}, "transactions": { "sign": { "signature": "5rp2hL9b6kexex11Mugfs3vfU9GhieKruj4CkFFSnu52WLxiGn4VcLLwsB62XURhMmT1j4CZiXT6FFtYbXsLq2Zs"}}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to slots
NodeJS
const request = {
  "slots": {
    "incoming_slots": {}
  },
  "accounts": {},
  "transactions": {},
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": { "incoming_slots": {} }, "accounts": {}, "transactions": {}, "blocks": {}, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to blocks
NodeJS
const request = {
  "slots": {},
  "accounts": {},
  "transactions": {},
  "blocks": {
    "blocks": {}
  },
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": {}, "accounts": { }, "transactions": {}, "blocks": { "blocks": {} }, "blocks_meta": {}, "accounts_data_slice": []}
Subscribe to block metadata
NodeJS
const request = {
  "slots": {},
  "accounts": {},
  "transactions": {},
  "blocks": {},
  "blocksMeta": {
    "blockmetadata": {}
  },
  "accountsDataSlice": []
};
gRPC
{"slots": {}, "accounts": {}, "transactions": {}, "blocks": {}, "blocks_meta": { "blockmetadata": {} }, "accounts_data_slice": []}
Unsubscribing
NodeJS
const request = {
  "slots": {},
  "accounts": {},
  "transactions": {},
  "blocks": {},
  "blocksMeta": {},
  "accountsDataSlice": []
};
gRPC
{"slots": {}, "accounts": {}, "transactions": {}, "blocks": {}, "blocks_meta": {}}

Managing commitment levels

The gRPC streams happen by default on the processed commitment level.

Triton also supports specifying confirmed and finalized commitment levels. In these cases, Dragon's Mouth will buffer the incoming updates for you and release them once the updates have become confirmed or finalized.

For maximum performance, however, it is recommended to use handling commitment levels client side.

To specify commitment level in your Dragon's Mouth gRPC calls provide the following values:

enum CommitmentLevel {
  PROCESSED = 0;
  CONFIRMED = 1;
  FINALIZED = 2;
}

Benefits of working at processed

The benefit of working on processed is that you can process transactions as soon as they arrive, but only commit to them once you know whether they are confirmed or finalized. This means that you can get faster response times in your UI by doing a lot of the processing work at a lower commitment level and then be able to surface the changes as soon as you see that the event is committed.

How to manage `confirmed` and `finalized`

To manage confirmed and finalized you need to buffer events by slot. Each event (transaction or account write) will have a slot attached to it. You store these events in a buffer ordered by slot.

You will receive all the transaction notifications or account write notifications for the slot before you receive the "confirmed" and "finalized" notification for that slot.

The special thing about finalized

Unfortunately, due to a quirk in the way that Geyser works on SVM not every slot finalized notification is issued. This means that you need some special processing if you want to handle finalized correctly.

The special handling is the following: whenever you see a finalized slot notification, you need to check the parents and grand parents (and great-grandparents and so on) of that slot and mark those as finalized too even if you didn't receive a notification for them.

A sample Typescript/Nodejs client is available at .

A sample Rust client is available at .

A sample Golang client is available at .

You then also make sure you subscribe to . This will give you information about when a slot is confirmed or finalized. Depending on the commitment level you are interested in, you should release your buffer when you receive the slot notification for a particular slot at a particular commitment level.

🛠️
https://docs.triton.one/
https://github.com/rpcpool/yellowstone-grpc/tree/master/yellowstone-grpc-proto/proto
https://crates.io/crates/yellowstone-grpc-proto
https://github.com/rpcpool/yellowstone-grpc/tree/master/examples/typescript
https://github.com/rpcpool/yellowstone-grpc/tree/master/examples/rust
https://github.com/rpcpool/yellowstone-grpc/tree/master/examples/golang
slot notifications