Dubhe Client SDK for Sui
Before getting started with Dubhe Client SDK, please install the required dependencies:
pnpm install @0xobelisk/sui-client @0xobelisk/sui-common
Note: @0xobelisk/sui-common contains essential configuration type definitions like DubheConfig, which are necessary for contract development.
Dubhe is a client-agnostic SDK that supports various platforms including browsers, Node.js, and the COCOS game engine. It provides a simple interface to interact with your Sui Move contracts.
Getting Started
Prerequisites
Before using the SDK, make sure you have:
- Created and deployed your contract using the Dubhe CLI
- Obtained the
packageId
after deployment
Data Model Setup
First, define your contract’s configuration using DubheConfig
:
import { DubheConfig } from "@0xobelisk/sui-common";
export const dubheConfig = {
name: "counter",
description: "counter",
systems: ["counter"],
schemas: {
counter: {
structure: {
value: "StorageValue<u32>",
},
},
},
} as DubheConfig;
Generate the contract code using CLI:
pnpm dubhe schemagen
Initializing the Client
There are two ways to initialize the Dubhe client:
- Using dynamic metadata loading:
import { loadMetadata, Dubhe, NetworkType } from "@0xobelisk/sui-client";
const network = "testnet" as NetworkType;
const packageId = "YOUR_PACKAGE_ID";
const metadata = await loadMetadata(network, packageId);
const dubhe = new Dubhe({
networkType: network,
packageId: packageId,
metadata: metadata,
secretKey: privkey,
});
- Using pre-saved metadata (recommended for better performance):
import metadata from "./metadata.json";
const dubhe = new Dubhe({
networkType: network,
packageId: packageId,
metadata: metadata,
secretKey: privkey,
});
Configuration Parameters
The Dubhe constructor accepts the following parameters:
interface DubheParams {
// Authentication (one of these is required)
mnemonics?: string; // 12 or 24 mnemonic words, separated by space
secretKey?: string; // Base64 or hex string or bech32 format private key
// Note: if mnemonics is provided, secretKey will be ignored
// Network Configuration
networkType?: NetworkType; // 'mainnet' | 'testnet' | 'devnet' | 'localnet'
// Default: 'mainnet'
fullnodeUrls?: string[]; // Array of fullnode URLs
// Default: Network-specific default URL
faucetUrl?: string; // Custom faucet URL for testnet/devnet/localnet
// Contract Configuration
packageId?: string; // Your contract's package ID
metadata?: SuiMoveNormalizedModules; // Contract metadata
// Indexer Configuration
indexerUrl?: string; // Custom indexer API URL
indexerWsUrl?: string; // Custom indexer WebSocket URL
// Default: Derived from indexerUrl
// HTTP Configuration
customFetch?: typeof fetch; // Custom fetch implementation
defaultOptions?: FetchOptions; // Default fetch options for all requests
}
Examples
- Basic initialization with private key:
const dubhe = new Dubhe({
networkType: "testnet",
packageId: "0x123...",
metadata: metadata,
secretKey: "YOUR_PRIVATE_KEY",
});
- Using mnemonics with custom network configuration:
const dubhe = new Dubhe({
mnemonics: "word1 word2 ... word24",
networkType: "testnet",
fullnodeUrls: ["https://custom-fullnode.example.com"],
packageId: "0x123...",
metadata: metadata,
});
- With custom indexer configuration:
const dubhe = new Dubhe({
secretKey: "YOUR_PRIVATE_KEY",
networkType: "mainnet",
packageId: "0x123...",
metadata: metadata,
indexerUrl: "https://custom-indexer.example.com",
indexerWsUrl: "wss://custom-indexer-ws.example.com",
});
Executing Transactions
To call contract methods:
import { Transaction } from "@0xobelisk/sui-client";
// Create transaction
const tx = new Transaction();
// Execute transaction with callbacks
const response = await dubhe.tx.counter_system.inc({
tx,
params: [
/* your parameters */
],
typeArguments: ["0x2::coin::Coin<0x2::sui::SUI>"], // optional
isRaw: false, // optional, defaults to false
onSuccess: async (result) => {
// Handle successful transaction
console.log("Transaction succeeded:", result.digest);
await dubhe.waitForTransaction(result.digest);
},
onError: (error) => {
// Handle transaction error
console.error("Transaction failed:", error);
},
});
// For wallet integration
const walletTx = await dubhe.tx.counter_system.inc({
tx,
params: [
/* your parameters */
],
typeArguments: [],
isRaw: true,
});
const response = await dubhe.signAndSendTxn({
tx: walletTx,
onSuccess: async (result) => {
// Handle successful transaction
console.log("Transaction succeeded:", result.digest);
await dubhe.waitForTransaction(result.digest);
},
onError: (error) => {
// Handle transaction error
console.error("Transaction failed:", error);
},
});
Transaction Parameters
Both query and transaction methods accept a parameter structure with the following fields:
{
tx: Transaction; // Required: Transaction instance
params?: TransactionArg[]; // Optional: Array of transaction arguments
typeArguments?: string[]; // Optional: Generic type arguments
isRaw?: boolean; // Optional: Return raw transaction instead of executing
onSuccess?: (result: SuiTransactionBlockResponse) => void | Promise<void>; // Optional: Success callback
onError?: (error: Error) => void | Promise<void>; // Optional: Error callback
}
Example Usage with Callbacks
Here’s a practical example showing how to use callbacks:
const tx = new Transaction();
try {
await dubhe.tx.map_system.register({
tx,
params: [
/* your parameters */
],
onSuccess: async (result) => {
// Add delay if needed
setTimeout(async () => {
// Show success notification
toast("Register Successful", {
description: new Date().toUTCString(),
action: {
label: "Check in Explorer",
onClick: () =>
window.open(dubhe.getTxExplorerUrl(result.digest), "_blank"),
},
});
}, 2000);
// Wait for transaction to be confirmed
await dubhe.waitForTransaction(result.digest);
},
onError: (error) => {
console.error("Transaction failed:", error);
toast.error("Transaction failed. Please try again.");
},
});
} catch (error) {
// Handle any unexpected errors
console.error("Unexpected error:", error);
}
Querying Data
To query contract state:
// Create transaction
const tx = new Transaction();
// Query with struct parameters
const result = await dubhe.query.counter_system.get({
tx,
params: [
/* your parameters */
],
typeArguments: [], // optional
isRaw: false, // optional
});
// For BCS encoded results
const decodedData = dubhe.view(result);
BCS Data Decoding
The SDK provides a view()
method to decode BCS-encoded return values from contract queries.
For detailed examples and advanced usage of querying data, please refer to our Query With Client Guide.
Supported Types
- Basic types (u8, u16, u32, u64, u128, u256)
- Boolean
- String
- Vector
- Struct
- Option
- Custom objects
Example with Complex Types
// Example contract return type
struct GameState {
score: u64,
player_name: String,
is_active: bool,
items: vector<Item>
}
// Query and decode
const tx = new Transaction();
const result = await dubhe.query.game_system.get_state(tx, params);
const decodedState = dubhe.view(result);
Known Limitations
⚠️ Important Note:
Some complex nested structures might require additional handling.
Querying Schema State
To query the state of schema fields defined in your Dubhe config, you can use either parseState()
or state()
method:
Using parseState
const result = await dubhe.parseState({
schema: "counter", // Schema name from your Dubhe config
objectId: "0x123...", // Object ID of the schema instance
storageType: "StorageValue<u64>", // Storage type of the field
params: [], // Parameters for StorageMap/StorageDoubleMap keys
});
Using state
const tx = new Transaction();
const result = await dubhe.state({
tx, // Transaction instance
schema: "counter", // Schema name from your Dubhe config
params: [tx.object("0x123...")], // Parameters including object ID
});
The storage types support three formats:
StorageValue<V>
- For single value storage
// Query a simple storage value
const value = await dubhe.parseState({
schema: "player",
objectId: "0x123...",
storageType: "StorageValue<u64>",
params: [], // No params needed for StorageValue
});
StorageMap<K,V>
- For key-value map storage
// Query a value from storage map
const mapValue = await dubhe.parseState({
schema: "inventory",
objectId: "0x123...",
storageType: "StorageMap<address,u64>",
params: ["0x456..."], // Key to look up in the map
});
StorageDoubleMap<K1,K2,V>
- For double key-value map storage
// Query a value from double map
const doubleMapValue = await dubhe.parseState({
schema: "game",
objectId: "0x123...",
storageType: "StorageDoubleMap<address,u64,u64>",
params: ["0x456...", 42], // Two keys needed for double map
});
Supported Key Types
The following key types are supported for StorageMap and StorageDoubleMap:
- Basic types:
u8
,u16
,u32
,u64
,u128
,u256
,bool
address
- Custom object types (format:
package::module::type
)
Error Handling
The method will throw an error if:
- Invalid storage type format is provided
- Wrong number of parameters for the storage type
- Unsupported key type is used
- Schema doesn’t exist
- Object ID is invalid
Parameter Validation
- StorageValue: No additional parameters required
- StorageMap: Exactly one key parameter required
- StorageDoubleMap: Exactly two key parameters required
Best Practices
-
Use pre-saved metadata for better performance in production
- Generate and save metadata during deployment
- Load metadata from file instead of fetching it every time
-
Implement proper error handling for BCS decoding
- Handle potential decoding errors for complex types
- Validate data types before decoding
- Use try-catch blocks for parsing operations
-
Consider the limitations of enum type handling when designing your contract return types
- Be aware of Sui metadata limitations for enums
- Design simpler enum structures when possible
- Test enum handling thoroughly
Support
For more information or support, please visit our GitHub repository or join our community channels.