Observed since 21 Oct, 09:20 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
@0xobelisk/react
React integration for Dubhe framework with Sui blockchain support
Modern React integration for Dubhe framework with Provider pattern and multi-chain support.
# Core React package
npm install @0xobelisk/react
# Peer dependencies
npm install react react-dom @0xobelisk/sui-client zod
# Optional dependencies for enhanced features
npm install @0xobelisk/graphql-client @0xobelisk/ecs
| Blockchain | Status | Import Path |
|---|---|---|
| Sui | ✅ Ready | @0xobelisk/react/sui |
| Aptos | 🚧 Coming Soon | @0xobelisk/react/aptos |
| Initia | 🚧 Coming Soon | @0xobelisk/react/initia |
import React from 'react';
import { DubheProvider, useDubhe } from '@0xobelisk/react/sui';
import { Transaction } from '@0xobelisk/sui-client';
import metadata from './contracts/metadata.json';
// App root with Provider
function App() {
const config = {
network: 'devnet' as const,
packageId: process.env.NEXT_PUBLIC_PACKAGE_ID!,
metadata,
credentials: {
secretKey: process.env.NEXT_PUBLIC_PRIVATE_KEY // ⚠️ LOCAL DEVELOPMENT ONLY // ⚠️ LOCAL DEVELOPMENT ONLY
}
};
return (
<DubheProvider config={config}>
<MyDApp />
</DubheProvider>
);
}
// Component using shared clients
function MyDApp() {
const { contract, address, network } = useDubhe();
const handleTransaction = async () => {
try {
const tx = new Transaction();
const result = await contract.tx.counter_system.increment({ tx });
console.log('Success:', result.digest);
} catch (error) {
console.error('Transaction failed:', error);
}
};
return (
<div>
<p>Connected: {address}</p>
<p>Network: {network}</p>
<button onClick={handleTransaction}>Execute Transaction</button>
</div>
);
}
The DubheProvider uses React Context with useRef pattern to ensure single client initialization:
import { DubheProvider } from '@0xobelisk/react/sui';
import type { DubheConfig } from '@0xobelisk/react/sui';
function App() {
const config: DubheConfig = {
network: 'devnet',
packageId: '0x123...',
metadata: contractMetadata,
// Optional features
dubheSchemaId: '0xabc...', // Optional: Dubhe Schema ID
dubheMetadata: dubheConfigMetadata, // Enables GraphQL + ECS
credentials: {
secretKey: process.env.NEXT_PUBLIC_PRIVATE_KEY // ⚠️ LOCAL DEVELOPMENT ONLY // ⚠️ LOCAL DEVELOPMENT ONLY
},
endpoints: {
fullnodeUrls: ['https://fullnode.devnet.sui.io:443'], // Optional: Custom RPC endpoints
graphql: process.env.NEXT_PUBLIC_GRAPHQL_URL,
websocket: process.env.NEXT_PUBLIC_GRAPHQL_WS_URL,
grpc: 'http://localhost:8080' // Optional: Custom gRPC endpoint
},
options: {
enableBatchOptimization: true,
cacheTimeout: 5000,
debounceMs: 100,
reconnectOnError: true
}
};
return (
<DubheProvider config={config}>
<YourApp />
</DubheProvider>
);
}
Access all Dubhe features in one hook:
import { useDubhe } from '@0xobelisk/react/sui';
function MyComponent() {
const {
contract, // Dubhe contract instance
graphqlClient, // GraphQL client (if dubheMetadata provided)
ecsWorld, // ECS World (if GraphQL available)
address, // User address
network, // Current network
packageId, // Contract package ID
metadata // Contract metadata
} = useDubhe();
return (
<div>
<p>Contract: {contract ? '✅' : '❌'}</p>
<p>GraphQL: {graphqlClient ? '✅' : '❌'}</p>
<p>ECS: {ecsWorld ? '✅' : '❌'}</p>
</div>
);
}
For components that only need specific functionality:
import React, { useEffect } from 'react';
import { useDubheContract, useDubheGraphQL, useDubheECS } from '@0xobelisk/react/sui';
import { Transaction } from '@0xobelisk/sui-client';
// Contract-only component
function TransactionComponent() {
const contract = useDubheContract();
const executeTransaction = async () => {
const tx = new Transaction();
await contract.tx.my_system.my_method({ tx });
};
return <button onClick={executeTransaction}>Execute</button>;
}
// GraphQL-only component
function DataComponent() {
const graphqlClient = useDubheGraphQL();
useEffect(() => {
if (graphqlClient) {
graphqlClient.query({ query: '{ entities { id } }' }).then((result) => console.log(result));
}
}, [graphqlClient]);
return <div>Data component</div>;
}
// ECS-only component
function ECSComponent() {
const ecsWorld = useDubheECS();
useEffect(() => {
if (ecsWorld) {
ecsWorld.getComponent('MyComponent').then((components) => console.log(components));
}
}, [ecsWorld]);
return <div>ECS component</div>;
}
interface DubheConfig {
/** Network type */
network: 'mainnet' | 'testnet' | 'devnet' | 'localnet';
/** Contract package ID */
packageId: string;
/** Contract metadata (required for contract instantiation) */
metadata: SuiMoveNormalizedModules;
/** Dubhe Schema ID (optional, for enhanced features) */
dubheSchemaId?: string;
/** Dubhe metadata (enables GraphQL/ECS features) */
dubheMetadata?: any;
/** Authentication credentials */
credentials?: {
secretKey?: string; // ⚠️ LOCAL DEVELOPMENT ONLY - see security warning below
mnemonics?: string; // ⚠️ LOCAL DEVELOPMENT ONLY - see security warning below
};
/** Service endpoints configuration */
endpoints?: {
graphql?: string; // Default: 'http://localhost:4000/graphql'
websocket?: string; // Default: 'ws://localhost:4000/graphql'
};
/** Performance and behavior options */
options?: {
enableBatchOptimization?: boolean; // Default: true
cacheTimeout?: number; // Default: 5000ms
debounceMs?: number; // Default: 100ms
reconnectOnError?: boolean; // Default: true
};
}
// .env.local
NEXT_PUBLIC_NETWORK=devnet
NEXT_PUBLIC_PACKAGE_ID=0x123...
NEXT_PUBLIC_PRIVATE_KEY=suiprivkey...
NEXT_PUBLIC_GRAPHQL_URL=http://localhost:4000/graphql
NEXT_PUBLIC_GRAPHQL_WS_URL=ws://localhost:4000/graphql
// App configuration
function App() {
const config = {
network: process.env.NEXT_PUBLIC_NETWORK as NetworkType,
packageId: process.env.NEXT_PUBLIC_PACKAGE_ID!,
metadata: contractMetadata,
credentials: {
secretKey: process.env.NEXT_PUBLIC_PRIVATE_KEY // ⚠️ LOCAL DEVELOPMENT ONLY // ⚠️ LOCAL DEVELOPMENT ONLY
},
endpoints: {
graphql: process.env.NEXT_PUBLIC_GRAPHQL_URL,
websocket: process.env.NEXT_PUBLIC_GRAPHQL_WS_URL
}
};
return (
<DubheProvider config={config}>
<MyApp />
</DubheProvider>
);
}
⚠️ IMPORTANT SECURITY NOTICE
secretKeyandmnemonicsare for LOCAL DEVELOPMENT ONLY!
- ❌ NEVER use secretKey/mnemonics in production
- ❌ NEVER commit private keys to version control
- ❌ NEVER expose private keys in client-side code
For production applications:
- ✅ Use official wallet providers for transaction signing
- ✅ Implement proper wallet integration (Sui Wallet, etc.)
- ✅ Let users connect their own wallets securely
Proper wallet integration example:
// For production - use wallet providers instead of secretKey const config = { network: 'mainnet', packageId: process.env.NEXT_PUBLIC_PACKAGE_ID!, metadata // ✅ NO credentials - let wallet handle signing };Keep your private keys safe! If compromised, attackers can steal all funds.
import React, { useState } from 'react';
import { DubheProvider, useDubhe } from '@0xobelisk/react/sui';
import { Transaction } from '@0xobelisk/sui-client';
function CounterApp() {
const { contract, address } = useDubhe();
const [counter, setCounter] = useState(0);
const queryCounter = async () => {
try {
const result = await contract.query.counter_system.get({});
setCounter(result);
} catch (error) {
console.error('Query failed:', error);
}
};
const incrementCounter = async () => {
try {
const tx = new Transaction();
const result = await contract.tx.counter_system.increment({ tx });
console.log('Transaction:', result.digest);
await queryCounter(); // Refresh counter
} catch (error) {
console.error('Transaction failed:', error);
}
};
return (
<div>
<h1>Counter: {counter}</h1>
<p>Address: {address}</p>
<button onClick={queryCounter}>Refresh</button>
<button onClick={incrementCounter}>Increment</button>
</div>
);
}
import React, { useEffect, useState } from 'react';
import { useDubhe } from '@0xobelisk/react/sui';
function DataComponent() {
const { graphqlClient, ecsWorld } = useDubhe();
const [entities, setEntities] = useState([]);
// Real-time subscription
useEffect(() => {
if (graphqlClient) {
const subscription = graphqlClient
.subscribe({
query: `
subscription {
entities {
id
components {
type
value
}
}
}
`
})
.subscribe((result) => {
setEntities(result.data.entities);
});
return () => subscription.unsubscribe();
}
}, [graphqlClient]);
// ECS component queries
const queryComponents = async () => {
if (ecsWorld) {
const components = await ecsWorld.getComponent('CounterComponent');
console.log('Components:', components);
}
};
return (
<div>
<h2>Real-time Entities: {entities.length}</h2>
<button onClick={queryComponents}>Query Components</button>
{graphqlClient ? (
<p>✅ GraphQL Connected</p>
) : (
<p>❌ GraphQL Unavailable (add dubheMetadata to config)</p>
)}
</div>
);
}
import React from 'react';
import { DubheProvider, useDubhe } from '@0xobelisk/react/sui';
function ErrorHandlingExample() {
const { contract } = useDubhe();
const handleTransactionWithRetry = async () => {
const maxRetries = 3;
let attempt = 0;
while (attempt < maxRetries) {
try {
const tx = new Transaction();
const result = await contract.tx.my_system.my_method({ tx });
console.log('Success:', result.digest);
return result;
} catch (error) {
attempt++;
console.error(`Attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
throw error;
}
// Wait before retry
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
return <button onClick={handleTransactionWithRetry}>Execute with Retry</button>;
}
import type {
NetworkType,
DubheConfig,
ClientConfig, // Alias for DubheConfig (for consistency with @0xobelisk/client)
DubheReturn,
ContractReturn // Alias for DubheReturn
} from '@0xobelisk/react/sui';
const config: DubheConfig = {
network: 'devnet', // Type-safe network selection
packageId: '0x123...',
metadata: contractMetadata, // Typed metadata
credentials: {
secretKey: process.env.NEXT_PUBLIC_PRIVATE_KEY // ⚠️ LOCAL DEVELOPMENT ONLY
}
};
const result: DubheReturn = useDubhe();
// Full type safety for all return values
# Watch mode for development
npm run watch
# Type checking
npm run type-check
# Build package
npm run build
# Run tests
npm run test
# Format code
npm run format
interface DubheReturn {
contract: Dubhe; // Enhanced contract instance
graphqlClient: DubheGraphqlClient | null; // GraphQL client (if enabled)
grpcClient: DubheGrpcClient; // gRPC client
ecsWorld: DubheECSWorld | null; // ECS World (if enabled)
metadata: SuiMoveNormalizedModules; // Contract metadata
network: NetworkType; // Current network
packageId: string; // Package ID
dubheSchemaId?: string; // Schema ID (if provided)
address: string; // User address
options?: DubheOptions; // Configuration options
metrics?: DubheMetrics; // Performance metrics
}
Configuration aligned with @0xobelisk/client for consistency:
interface DubheConfig {
// Required fields
network: NetworkType; // 'mainnet' | 'testnet' | 'devnet' | 'localnet'
packageId: string; // Contract package ID
metadata: any; // Contract metadata (from dubhe schemagen)
// Optional fields
dubheSchemaId?: string; // Dubhe Schema ID for enhanced features
dubheMetadata?: any; // Dubhe metadata for GraphQL/ECS features
// Credentials
credentials?: {
secretKey?: string; // Private key (base64 or hex)
mnemonics?: string; // 12 or 24 word mnemonic phrase
};
// Endpoints
endpoints?: {
fullnodeUrls?: string[]; // Full node RPC URLs (multiple for redundancy)
graphql?: string; // GraphQL endpoint (default: http://localhost:4000/graphql)
websocket?: string; // WebSocket endpoint (default: ws://localhost:4000/graphql)
grpc?: string; // gRPC endpoint (default: http://localhost:8080)
};
// Options
options?: {
enableBatchOptimization?: boolean; // Default: true
cacheTimeout?: number; // Default: 5000 (ms)
debounceMs?: number; // Default: 100 (ms)
reconnectOnError?: boolean; // Default: true
};
}
// Type alias for consistency with @0xobelisk/client
type ClientConfig = DubheConfig;
useDubhe() → DubheReturn - Complete Dubhe ecosystemuseDubheContract() → Dubhe - Contract instance onlyuseDubheGraphQL() → DubheGraphqlClient | null - GraphQL client onlyuseDubheECS() → DubheECSWorld | null - ECS World onlyuseContract() → DubheReturn - Alias for useDubhe()The package includes comprehensive error handling:
// Configuration validation
try {
const { contract } = useDubhe();
} catch (error) {
console.error('Configuration error:', error.message);
}
// Provider context validation
function MyComponent() {
try {
const dubhe = useDubhe();
} catch (error) {
// Error: useDubhe must be used within a DubheProvider
}
}
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
nanostoresEnvironment{
"local_name": "nanostores",
"alias": false,
"peer_metadata": null
} | ^0.11.4 | runtime |
@0xobelisk/ecsEnvironment{
"local_name": "@0xobelisk/ecs",
"alias": false,
"peer_metadata": null
} | 1.2.0-pre.93 | runtime |
@nanostores/reactEnvironment{
"local_name": "@nanostores/react",
"alias": false,
"peer_metadata": null
} | ^0.8.0 | runtime |
@0xobelisk/sui-clientEnvironment{
"local_name": "@0xobelisk/sui-client",
"alias": false,
"peer_metadata": null
} | 1.2.0-pre.93 | runtime |
@0xobelisk/grpc-clientEnvironment{
"local_name": "@0xobelisk/grpc-client",
"alias": false,
"peer_metadata": null
} | 1.2.0-pre.93 | runtime |
@0xobelisk/graphql-clientEnvironment{
"local_name": "@0xobelisk/graphql-client",
"alias": false,
"peer_metadata": null
} | 1.2.0-pre.93 | runtime |
tsupEnvironment{
"local_name": "tsup",
"alias": false,
"peer_metadata": null
} | ^7.1.0 | development |
reactEnvironment{
"local_name": "react",
"alias": false,
"peer_metadata": null
} | ^18.0.0 | development |
eslintEnvironment{
"local_name": "eslint",
"alias": false,
"peer_metadata": null
} | ^9.0.0 | development |
prettierEnvironment{
"local_name": "prettier",
"alias": false,
"peer_metadata": null
} | 3.3.3 | development |
react-domEnvironment{
"local_name": "react-dom",
"alias": false,
"peer_metadata": null
} | ^18.0.0 | development |
typescriptEnvironment{
"local_name": "typescript",
"alias": false,
"peer_metadata": null
} | ^5.2.2 | development |
@types/nodeEnvironment{
"local_name": "@types/node",
"alias": false,
"peer_metadata": null
} | ^20.8.7 | development |
@types/reactEnvironment{
"local_name": "@types/react",
"alias": false,
"peer_metadata": null
} | ^19.0.0 | development |
@types/react-domEnvironment{
"local_name": "@types/react-dom",
"alias": false,
"peer_metadata": null
} | ^19.0.0 | development |
eslint-config-prettierEnvironment{
"local_name": "eslint-config-prettier",
"alias": false,
"peer_metadata": null
} | ^9.1.0 | development |
eslint-plugin-prettierEnvironment{
"local_name": "eslint-plugin-prettier",
"alias": false,
"peer_metadata": null
} | ^5.0.1 | development |
@typescript-eslint/parserEnvironment{
"local_name": "@typescript-eslint/parser",
"alias": false,
"peer_metadata": null
} | ^6.8.0 | development |
@typescript-eslint/eslint-pluginEnvironment{
"local_name": "@typescript-eslint/eslint-plugin",
"alias": false,
"peer_metadata": null
} | ^6.8.0 | development |
zodEnvironment{
"local_name": "zod",
"alias": false,
"peer_metadata": null
} | ^3.25.0 || ^4.0.0 | peer |
reactEnvironment{
"local_name": "react",
"alias": false,
"peer_metadata": {
"optional": true
}
} | ^18.0.0 || ^19.0.0 | peer |
react-domEnvironment{
"local_name": "react-dom",
"alias": false,
"peer_metadata": {
"optional": true
}
} | ^18.0.0 || ^19.0.0 | peer |
@0xobelisk/sui-clientEnvironment{
"local_name": "@0xobelisk/sui-client",
"alias": false,
"peer_metadata": {
"optional": true
}
} | 1.2.0-pre.93 | peer |
Source distribution
{
"shasum": "8f5b69f04f68aa602dc8ae9c48862d4eab4b0e90",
"tarball": "https://registry.npmjs.org/@0xobelisk/react/-/react-1.2.0-pre.93.tgz",
"fileCount": 29,
"integrity": "sha512-fsKC8EqJXhv9DdTw0a/AZIiajdbg1s+Yvjyu+qaGganjKan0NKnXL7QwRSpAFjLrdCIkEmoM5IENw0S3Yg8xBg==",
"signatures": [
{
"sig": "MEQCIAnCMUwHq1FiOc2tzXwsdHbVlPcfwqCUhbUC2tk1t/P/AiACr/3Sz4yd71du3d3ssqQsX4PAxJLtlnEZHubJThjG0Q==",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 276638
}Observed since 21 Oct, 09:20 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:94c02ec2b37fdd88ff697ea2e4eae882abf55f6844fecaf73930d97c5ce0ece1