Skip to content

Nuel-osas/grpc_class

Repository files navigation

Sui gRPC Class

Learn how to use gRPC with the Sui blockchain — from setup to streaming to production middleware.

Quick Setup

# 1. Install dependencies
npm install

# 2. Get the official Sui proto files
npm run setup:protos

# 3. Run any example
npm run example:01

Project Structure

grpc_class/
├── LESSON_NOTES.md              # Full 2-hour lesson notes
├── src/
│   └── client.ts                # gRPC client wrapping all 7 services (22 methods)
├── examples/
│   ├── 01-get-checkpoint.ts     # Get a checkpoint (unary call)
│   ├── 02-get-transaction.ts    # Get transaction details + batch
│   ├── 03-get-balance.ts        # Balances, coin info, owned objects
│   ├── 04-get-object.ts         # Get object + batch get objects
│   ├── 05-stream-checkpoints.ts # Stream live checkpoints (THE key demo)
│   ├── 06-inspect-package.ts    # Inspect smart contract structure
│   ├── 07-other-services.ts     # ServiceInfo, Epoch, NameService
│   └── 08-filter-contract-events.ts # Stream + filter for contract events
├── middleware/
│   ├── server.ts                # gRPC-to-WebSocket bridge for frontends
│   └── frontend-example.html    # Browser page that connects to middleware
└── protos/                      # Sui proto files (run setup:protos to populate)

Examples

# Command What It Demos
01 npm run example:01 Get checkpoint — basic unary call
02 npm run example:02 Get transaction + batch transactions
03 npm run example:03 Get balance, list balances, coin info, owned objects
04 npm run example:04 Get object + batch get objects
05 npm run example:05 Stream live checkpoints (30s)
06 npm run example:06 Inspect a Move smart contract
07 npm run example:07 Node info, epoch, SuiNS name lookup
08 npm run example:08 Stream + filter for contract events (2min)

Middleware

The middleware bridges gRPC to the browser via WebSocket:

# Start the middleware server
npm run middleware

# Then open middleware/frontend-example.html in your browser

Optional environment variables:

GRPC_ENDPOINT=fullnode.testnet.sui.io:443
WS_PORT=8080
WATCH_PACKAGE_ID=0x...    # Filter for a specific contract
WATCH_ADDRESS=0x...       # Filter for a specific wallet

Setup From Scratch (Without This Repo)

If you're building your own project from zero, these are the 3 required steps:

Step 1: Initialize project & install packages

mkdir my-sui-grpc && cd my-sui-grpc
npm init -y
npm install @grpc/grpc-js @grpc/proto-loader
npm install -D typescript ts-node @types/node

Step 2: Get the proto files

Proto files are the contract between your code and the Sui node. Without them, the client doesn't know what methods exist or what data to send/receive.

git clone https://github.com/MystenLabs/sui-apis.git --depth=1
mkdir -p protos
cp -r sui-apis/proto/* protos/
rm -rf sui-apis

This gives you 32 proto files defining all 7 services and 22 methods.

Step 3: Create the gRPC client

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import * as path from 'path';

// 1. Load the proto file
const packageDefinition = protoLoader.loadSync(
  path.join(__dirname, 'protos/sui/rpc/v2/ledger_service.proto'),
  {
    keepCase: true,           // Keep snake_case field names
    longs: String,             // Big numbers as strings
    enums: String,             // Enum names as strings
    defaults: true,            // Include default values
    oneofs: true,              // Support oneof fields
    includeDirs: [path.join(__dirname, 'protos')],
  }
);

// 2. Get the service constructor
const proto = grpc.loadPackageDefinition(packageDefinition) as any;
const LedgerService = proto.sui.rpc.v2.LedgerService;

// 3. Create the client (connects to Sui testnet, free, no auth)
const client = new LedgerService(
  'fullnode.testnet.sui.io:443',
  grpc.credentials.createSsl()
);

// 4. Make a call
client.GetCheckpoint(
  { read_mask: { paths: ['sequence_number', 'digest'] } },
  new grpc.Metadata(),
  (err: any, response: any) => {
    if (err) console.error(err);
    else console.log(response);
  }
);

Repeat steps 1-3 for each service you want to use (StateService, SubscriptionService, etc.), loading the corresponding proto file.


The 7 Sui gRPC Services

Service Methods Purpose
LedgerService 7 Read historical data (checkpoints, transactions, objects)
StateService 5 Query current state (balances, owned objects, dynamic fields)
SubscriptionService 1 Real-time checkpoint streaming
TransactionExecutionService 2 Execute & simulate transactions
MovePackageService 4 Inspect smart contracts
NameService 2 SuiNS name resolution
SignatureVerificationService 1 Signature validation

Filtering Data

Unary Calls — Filter With Parameters

The server filters for you using method parameters and field masks:

// Filter owned objects by type (only SUI coins)
await client.listOwnedObjects('0xWallet...', 50, undefined,
  { paths: ['object_id', 'object_type'] },
  '0x2::coin::Coin<0x2::sui::SUI>'
);

// Filter balance by coin type
await client.getBalance('0xWallet...', '0x2::sui::SUI');

// Field masks — control WHAT data comes back (smaller response)
await client.getCheckpoint(undefined, undefined, {
  paths: ['sequence_number', 'digest']  // only these fields
});

Streaming — Filter In Your Code

The stream sends every checkpoint. You filter on your side:

const stream = client.subscribeCheckpoints({
  paths: [
    'sequence_number',
    'transactions.digest',
    'transactions.events.events.event_type',
    'transactions.events.events.json',
    'transactions.effects.status',
  ]
});

stream.on('data', (data) => {
  const checkpoint = data.checkpoint;
  if (!checkpoint?.transactions) return;

  checkpoint.transactions.forEach(tx => {
    const txStr = JSON.stringify(tx);

    // Filter by contract package ID
    if (txStr.includes('0xYourPackageId')) {
      console.log('Contract event:', tx.digest);
    }

    // Filter by wallet address
    if (txStr.includes('0xYourWalletAddress')) {
      console.log('Your transaction:', tx.digest);
    }

    // Filter by event type
    tx.events?.events?.forEach(event => {
      if (event.event_type?.includes('GameStarted')) {
        console.log('Game started!');
      }
    });

    // Filter by tx status (failed transactions)
    if (tx.effects?.status?.status === 'FAILURE') {
      console.log('Failed tx:', tx.digest);
    }
  });
});

Key Difference

Unary Streaming
Who filters Server (parameters) You (in your code)
How Pass coin_type, owner, object_type, field masks Check fields or JSON.stringify + .includes()
Field masks Yes — controls response size Yes — controls what data the stream sends

Field masks matter even more for streaming — without them, every checkpoint sends full transaction data. Keep them tight.


Endpoints (Free, No Auth)

Endpoint Network
fullnode.testnet.sui.io:443 Testnet
fullnode.mainnet.sui.io:443 Mainnet

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors