DubheSuiClient

Dubhe Client SDK

Dubhe provides a layered set of client packages for interacting with on-chain contracts, the indexer, and the ECS world from TypeScript/JavaScript.

Package Overview

PackagePurpose
@0xobelisk/client/suiRecommended entry point — bundles all clients in one call
@0xobelisk/reactReact Provider + hooks for frontend apps
@0xobelisk/sui-clientLow-level Sui contract client (Dubhe class)
@0xobelisk/graphql-clientApollo-based indexer GraphQL client
@0xobelisk/ecsEntity-Component-System query / subscription layer

@0xobelisk/client/sui — Recommended Entry Point

createClient initialises all clients at once and returns a DubheClientBundle. Use this for any project that is not using React (Node.js scripts, tests, non-React frontends).

Install

pnpm add @0xobelisk/client

Usage

import { createClient } from '@0xobelisk/client/sui';
import metadata from './metadata.json';
import { Network, PackageId, DappHubId, DappStorageId, FrameworkPackageId } from './deployment';
 
const client = createClient({
  network: Network, // 'mainnet' | 'testnet' | 'devnet' | 'localnet'
  packageId: PackageId,
  metadata, // SuiMoveNormalizedModules from loadMetadata()
  dappHubId: DappHubId, // DappHub shared object ID
  dappStorageId: DappStorageId, // DappStorage shared object ID
  frameworkPackageId: FrameworkPackageId, // only needed for localnet / devnet
  credentials: {
    secretKey: process.env.PRIVATE_KEY // base64 or hex private key
    // mnemonics: '...'                 // alternative: 12/24-word mnemonic
  },
  endpoints: {
    graphql: 'http://localhost:8080/graphql', // dubhe-indexer proxy (default port 8080)
    websocket: 'ws://localhost:8080/graphql',
    grpc: 'http://localhost:8085' // dubhe-indexer gRPC backend (default port 8085)
    // fullnodeUrls: ['https://...'],             // optional: override RPC endpoint
    // channelUrl: 'http://...',                  // optional: DubheChannel server
  }
  // options: {                                   // optional performance tuning
  //   enableBatchOptimization: true,
  //   cacheTimeout: 5000,
  //   debounceMs: 100,
  // }
});
 
// client.contract      — Dubhe instance (call Move functions)
// client.graphqlClient  — DubheGraphqlClient (query indexer via GraphQL)
// client.grpcClient     — DubheGrpcClient (query indexer via gRPC)
// client.ecsWorld       — DubheECSWorld (entity/component queries)
// client.address        — user wallet address string

Calling Move functions

import { Transaction } from '@mysten/sui/transactions';
 
// Send a transaction
const tx = new Transaction();
await client.contract.tx.player_system.level_up({ tx });
 
// Read-only query (devInspect)
const result = await client.contract.query.player_system.get_level({
  tx: new Transaction()
});

@0xobelisk/react — React Provider + Hooks

For React / Next.js apps. Wraps createClient in a Context Provider with useRef-based single-initialisation (no re-renders on config re-evaluation).

Install

pnpm add @0xobelisk/react

Setup: Wrap your app with DubheProvider

import { DubheProvider } from '@0xobelisk/react';
import metadata from './metadata.json';
import { Network, PackageId, DappHubId, DappStorageId, FrameworkPackageId } from './deployment';
 
function App() {
  return (
    <DubheProvider
      config={{
        network: Network,
        packageId: PackageId,
        metadata,
        dappHubId: DappHubId,
        dappStorageId: DappStorageId,
        frameworkPackageId: FrameworkPackageId,
        credentials: { secretKey: process.env.NEXT_PUBLIC_PRIVATE_KEY },
        endpoints: {
          graphql: 'http://localhost:8080/graphql',
          websocket: 'ws://localhost:8080/graphql',
          grpc: 'http://localhost:8085'
        }
      }}
    >
      <MyApp />
    </DubheProvider>
  );
}

Hooks

All hooks must be called inside a component tree wrapped by DubheProvider.

useDubhe — full bundle

import { useDubhe } from '@0xobelisk/react';
import { Transaction } from '@mysten/sui/transactions';
 
function PlayerCard() {
  const { contract, graphqlClient, grpcClient, ecsWorld, address } = useDubhe();
 
  const handleLevelUp = async () => {
    const tx = new Transaction();
    await contract.tx.player_system.level_up({ tx });
  };
 
  return <button onClick={handleLevelUp}>Level Up ({address})</button>;
}

useDubheContract — contract only

const contract = useDubheContract();
await contract.tx.my_system.my_action({ tx });

useDubheGraphQL — GraphQL client only

const graphqlClient = useDubheGraphQL();
const data = await graphqlClient.getAllTables('player', { first: 10 });

The GraphQL endpoint is the dubhe-indexer proxy at port 8080 (default: http://localhost:8080/graphql). Set it via endpoints.graphql in DubheProvider.

useDubheECS — ECS world only

const ecsWorld = useDubheECS();
const players = await ecsWorld.queryWith('player');

grpcClient from useDubhe

The full bundle returned by useDubhe() also contains grpcClient — a DubheGrpcClient instance connected to the dubhe-indexer gRPC backend (default port 8085):

const { grpcClient } = useDubhe();
 
const result = await grpcClient.dubheGrpcClient.queryTable({
  tableId: 'player',
  filters: [],
  pagination: { page: 0, pageSize: 20 }
});

@0xobelisk/sui-client — Low-Level Contract Client

Use Dubhe directly when you need fine-grained control, or are not using the bundle factory.

Install

pnpm add @0xobelisk/sui-client

Usage

import { Dubhe, loadMetadata } from '@0xobelisk/sui-client';
import { Transaction } from '@mysten/sui/transactions';
import { Network, PackageId, DappStorageId, FrameworkPackageId } from './deployment';
 
const metadata = await loadMetadata(Network, PackageId);
 
const dubhe = new Dubhe({
  networkType: Network,
  packageId: PackageId,
  metadata,
  secretKey: process.env.PRIVATE_KEY,
  // mnemonics:       '...',          // alternative: 12/24-word mnemonic
  // fullnodeUrls:    ['https://...'], // override default RPC endpoint
  frameworkPackageId: FrameworkPackageId, // dubhe framework package ID (localnet/devnet only; testnet/mainnet use SDK defaults)
  dappStorageId: DappStorageId
  // channelUrl:      'http://...',    // DubheChannel endpoint (if using Channel)
});
 
const address = dubhe.getAddress();
 
// Send a transaction
const tx = new Transaction();
await dubhe.tx.player_system.level_up({
  tx,
  onSuccess: async (result) => {
    console.log('tx:', result.digest);
    await dubhe.waitForTransaction(result.digest);
  },
  onError: (err) => console.error(err)
});
 
// Read-only query
const result = await dubhe.query.player_system.get_level({ tx: new Transaction() });
const level = dubhe.view(result); // decode BCS return value

Transaction with raw PTB (wallet integration)

// Build the transaction without executing it
const rawTx = await dubhe.tx.player_system.level_up({
  tx,
  isRaw: true // returns the Transaction object instead of executing
});
 
// Sign and send via an external wallet adapter
const response = await dubhe.signAndSendTxn({
  tx: rawTx,
  onSuccess: (result) => console.log('success:', result.digest)
});

DubheParams Reference

type DubheParams = {
  // Authentication — all optional.
  // If neither secretKey nor mnemonics is provided, a random 24-word mnemonic is generated.
  secretKey?: string; // base64, hex, or bech32 private key
  mnemonics?: string; // 12 or 24 words, space-separated
 
  // Network
  networkType?: 'mainnet' | 'testnet' | 'devnet' | 'localnet'; // default: 'mainnet'
  fullnodeUrls?: string[]; // override default RPC endpoints
  faucetUrl?: string;
 
  // Contract
  packageId?: string; // your contract's package ID
  metadata?: SuiMoveNormalizedModules; // from loadMetadata()
 
  // Dubhe Framework (required for session / settlement features)
  frameworkPackageId?: string; // dubhe framework package ID — from deployment.ts FrameworkPackageId
  // needed for localnet and devnet; testnet/mainnet use SDK defaults
  dappStorageId?: string; // DappStorage object ID — from deployment.ts DappStorageId
 
  // DubheChannel (optional)
  channelUrl?: string; // channel server URL
};

Loading Metadata

metadata is required by all contract clients. Load it at startup:

import { loadMetadata } from '@0xobelisk/sui-client';
 
// Fetch from the network at runtime (one network call)
const metadata = await loadMetadata('testnet', '0x<packageId>');
 
// Or import a pre-generated JSON file (faster — no network call)
import metadata from './metadata.json';

To generate metadata.json locally after deployment:

dubhe load-metadata --network testnet --package-id 0x<packageId>

Deployment Artifacts

After dubhe publish, run dubhe store-config to generate a typed TypeScript file with all deployment IDs:

dubhe store-config --network testnet --output-ts-path ./src/deployment.ts
// src/deployment.ts — generated by dubhe store-config
export const Network: NetworkType = 'testnet';
export const PackageId = '0x...';
export const DappHubId = '0x...'; // DappHub shared object ID
export const DappStorageId = '0x...'; // DappStorage shared object ID
export const FrameworkPackageId = undefined; // set automatically for localnet

Import these values to initialise the client — do not import .history/…/latest.json directly.


UserStorage and Session Management

The Dubhe class exposes helper methods for the UserStorage lifecycle and session key management. All methods require frameworkPackageId and packageId to be set in the constructor.

initUserStorage

Create a UserStorage shared object for the signer. Each address may only call this once per DApp.

await dubhe.initUserStorage({
  dappHubId: DappHubId,
  dappStorageId: DappStorageId
});

getUserStorageId

Resolve the UserStorage object ID for a given address.

const userStorageId = await dubhe.getUserStorageId(userAddress);
if (!userStorageId) {
  // User has not initialised their storage yet
  await dubhe.initUserStorage({ dappHubId, dappStorageId });
}

getUserStorageFields

Read all fields from a UserStorage object.

const fields = await dubhe.getUserStorageFields(userStorageId);
// fields.canonical_owner, fields.write_count, fields.session_key, ...

getDappStorageFields

Read all metadata and fee-state fields from a DappStorage object.

const fields = await dubhe.getDappStorageFields(dappStorageId);
// fields.version, fields.admin, fields.credit_pool, fields.paused, ...

activateSession

Grant a session wallet permission to write to UserStorage on behalf of the canonical owner.

await dubhe.activateSession({
  userStorageId,
  sessionWallet: ephemeralAddress,
  durationMs: 3_600_000 // 1 hour (range: 60_000 – 604_800_000)
});

deactivateSession

Revoke the active session key. Can be called by the canonical owner, the session key itself, or anyone after expiry.

await dubhe.deactivateSession({ userStorageId });

settleWrites

Settle accumulated write debt for a user. Deducts from the DApp’s credit pool.

await dubhe.settleWrites({
  dappHubId,
  dappStorageId,
  userStorageId
});

DubheChannel

DubheChannel provides a server-side transaction relay and real-time data layer. Set channelUrl in the constructor to enable Channel features.

submitToChannel

Submit a PTB to the channel server for execution. The channel server signs and broadcasts the transaction, removing the user’s need to sign each action.

const nonce = await dubhe.latestNonce();
const tx = new Transaction();
// ... build tx ...
await dubhe.submitToChannel({ tx, nonce });

latestNonce

Fetch the latest nonce for an account from the channel server.

const nonce = await dubhe.latestNonce(); // signer address
const nonce = await dubhe.latestNonce({ account: '0x...' }); // specific address

queryChannelTable

Read a resource table entry from the channel server’s state.

const data = await dubhe.queryChannelTable({
  table: 'player',
  key: [] // composite key fields (empty for no-key resources)
  // account: '0x...', // defaults to signer address
});

subscribeChannelTable

Subscribe to real-time updates for a resource table via Server-Sent Events.

const unsubscribe = await dubhe.subscribeChannelTable(
  { table: 'player' },
  { onMessage: (data) => console.log('update:', data) }
);
 
// Stop listening:
unsubscribe();

Utility Methods

// Get the signer's wallet address
dubhe.getAddress(): string
 
// Decode BCS return values from a query
dubhe.view(devInspectResult): any
 
// Get the Sui explorer URL for a transaction
dubhe.getTxExplorerUrl(txDigest: string): string
 
// Wait for a transaction to be confirmed
await dubhe.waitForTransaction(txDigest: string)