mirrorworld-sonic-hyperfuse-node-sdk
v0.1.5
Published
TypeScript SDK for client-side WebSocket connection with validator backend to validate batch block hash
Maintainers
Readme
Hyperfuse Client SDK
TypeScript SDK for client-side WebSocket connection with validator backend to validate batch block hash in client side.
Features
- 🔌 WebSocket-based real-time validator connection
- 🔐 Secure keypair management with AES encryption
- 🌐 Support for both browser and Node.js environments
- 📦 Service Worker support for background validation
- 🎯 Comprehensive API coverage for validators, delegations, rewards, and voting
Installation
npm install mirrorworld-sonic-hyperfuse-node-sdkor
yarn add mirrorworld-sonic-hyperfuse-node-sdkQuick Start
Initialize SDK
import { SDK } from 'mirrorworld-sonic-hyperfuse-node-sdk';
import { Keypair } from '@solana/web3.js';
import bs58 from 'bs58';
// Initialize SDK
const sdk = new SDK({
sonicRpc: process.env.SONIC_RPC!,
backendDomain: process.env.BACKEND_DOMAIN!,
backendWssDomain: process.env.BACKEND_WSS_DOMAIN!,
runtime: 'browser', // or 'node'
});Configuration Options
| Option | Type | Required | Description | Default |
|--------|------|----------|-------------|---------|
| sonicRpc | string | Yes | Sonic RPC endpoint URL | - |
| backendDomain | string | Yes | Backend API domain URL | - |
| backendWssDomain | string | Yes | Backend WebSocket domain URL | - |
| runtime | 'browser' | 'node' | No | Runtime environment | 'browser' |
| storage | KeypairStorage | No | Custom storage implementation | localStorage |
| backendWssEndpoint | string | No | Custom WebSocket endpoint | 'ws/node/light_client' |
| heartBeatInterval | number | No | WebSocket heartbeat interval (ms) | 15000 |
| limitConcurrency | number | No | Concurrent connection limit | 3 |
| retryTimes | number | No | Connection retry attempts | 3 |
| secretKeyPrefix | string | No | Storage key prefix | 'KEY_FOR_ADDR_' |
| hexKey | string | No | AES encryption key (hex) | (default key) |
| hexIv | string | No | AES encryption IV (hex) | (default IV) |
API Reference
User API
Add User
Register a new user in the system.
const keypair = Keypair.fromSecretKey(bs58.decode(process.env.USER_SECRET!));
const result = await sdk.user.addUser({
owner: keypair.publicKey.toBase58(),
receive_rewards_account: 'REWARD_ACCOUNT_ADDRESS',
name: 'My Username', // optional
sign_callback: async (msg: Uint8Array) => {
return keypair.sign(msg).signature;
}
});Get User Info
Retrieve user information including NFT nodes, rewards, and delegation stats.
const userInfo = await sdk.user.getUser('USER_WALLET_ADDRESS');
// Response includes:
// - owner: string
// - name: string
// - receive_rewards_account: string
// - all_rewards: string
// - node_work_load: number
// - validator_work_load: number
// - all_nft_nodes: number
// - running_nodes: number
// - other_delegated_nft_nodes: number
// - remain_nft_nodes: number
// - stake_amount: string
// - auto_stake: booleanChange Rewards Account
Update the account where rewards will be sent.
const result = await sdk.user.changeReceiveRewardsAccount({
owner: keypair.publicKey.toBase58(),
receive_rewards_account: 'NEW_REWARD_ACCOUNT_ADDRESS',
sign_callback: async (msg: Uint8Array) => {
return keypair.sign(msg).signature;
}
});Refresh User NFT Nodes
Sync user NFT information from on-chain to backend.
const result = await sdk.user.refreshUserNftNode('USER_WALLET_ADDRESS');Get User Activity
Retrieve user activity history with pagination.
const activities = await sdk.user.getActivity({
owner: 'USER_WALLET_ADDRESS',
action_type: 1, // Optional: 1=delegate, 2=undelegate, 3=run node, 4=disable node, 5=create validator, 6=receive return nodes
page_index: 0,
page_size: 20,
sort_type: 'create_at,desc' // Optional: sort by field,order
});Validator API
Register Validator
Create and register a new validator node with automatic keypair storage.
const validatorKeypair = Keypair.generate();
const ownerKeypair = Keypair.fromSecretKey(bs58.decode(process.env.OWNER_SECRET!));
const result = await sdk.registerValidatorForLocalStorage({
owner: ownerKeypair.publicKey.toBase58(),
keypair: validatorKeypair,
name: 'My Validator',
max_delegate_num: 1000,
delegate_fee: 5, // 5% commission
delegate_nft_node_num: 10,
sign_callback: async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
}
});Get Validators
Query validators with flexible filtering.
const validators = await sdk.validator.getValidators({
owner: 'OWNER_WALLET_ADDRESS', // Optional: filter by owner
show_delegatable: 'true', // Optional: show only validators accepting delegations
page_index: 0,
page_size: 20,
sort_type: 'last_1_day_online_perc,desc' // Sort by daily uptime
});
// Each validator includes:
// - pk: string (validator address)
// - name: string
// - is_online: boolean
// - all_online_time: number
// - all_online_perc: number
// - last_1_day_online_perc: number (Daily Uptime)
// - last_7_day_online_perc: number
// - rewards: number
// - work_load: number
// - max_delegate_num: number
// - current_delegate_num: number
// - owner_delegate_num: number
// - delegate_fee: number
// - status: numberGet Delegated Validators for User
Get validators that a user has delegated to.
const delegatedValidators = await sdk.validator.getDelegetedValidatorsForUser({
delegated_user: 'USER_WALLET_ADDRESS',
page_index: 0,
page_size: 20
});Get Brief Validator Stats
Get summary statistics for validators.
const briefInfo = await sdk.validator.getBriefValidators('OWNER_WALLET_ADDRESS');
// Returns:
// - validator_num: number
// - delegated_num: number
// - community_num: number
// - running_num: number
// - not_running_num: number
// - avg_daily_uptime: numberDisable Validator
Disable a validator and stop its WebSocket connection.
const result = await sdk.disableValidatorForLocalStorage({
owner: ownerKeypair.publicKey.toBase58(),
validator: 'VALIDATOR_PUBLIC_KEY',
sign_callback: async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
}
});Re-import Validator
Re-import an existing validator's private key.
const validatorKeypair = Keypair.fromSecretKey(bs58.decode(process.env.VALIDATOR_SECRET!));
const result = await sdk.reImportSecretForLocalStorage({
validator: 'VALIDATOR_PUBLIC_KEY',
keypair: validatorKeypair
});Delegation API
Delegate to Validator
Delegate NFT nodes to a validator.
const result = await sdk.delegation.delegateTo({
owner: ownerKeypair.publicKey.toBase58(),
validator: 'VALIDATOR_PUBLIC_KEY',
num: 10, // Number of NFT nodes to delegate
sign_callback: async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
}
});Undelegate from Validator
Remove delegated NFT nodes from a validator (note: validators cannot undelegate their own nodes).
const result = await sdk.delegation.undelegate({
owner: ownerKeypair.publicKey.toBase58(),
validator: 'VALIDATOR_PUBLIC_KEY',
num: 5, // Number of NFT nodes to undelegate
sign_callback: async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
}
});Get Delegation Info
Query delegation information with filtering.
const delegationInfo = await sdk.delegation.getDelegationInfo({
validator: 'VALIDATOR_PUBLIC_KEY', // Optional
owner: 'OWNER_WALLET_ADDRESS', // Optional
if_exclude_owner: 'true', // Optional: exclude validator owner's delegation
page_index: 0,
page_size: 20
});
// Each delegation record includes:
// - owner: string (delegator address)
// - delegate_to: string (validator address)
// - delegate_num: number (number of NFT nodes)Reward API
Get Rewards Activity
Retrieve rewards activity history.
const rewardsActivity = await sdk.reward.getRewardsActivity({
owner: 'USER_WALLET_ADDRESS',
action_type: 0, // Optional: 0=node rewards, 1=validator rewards as delegator, 2=claim rewards
page_index: 0,
page_size: 20
});
// Each activity includes:
// - id: string
// - owner: string
// - amount: number
// - action_type: number
// - other_address: string
// - desc: string
// - create_at: numberGet Rewards Chart Data
Get historical rewards data for charting.
const rewardsData = await sdk.reward.getRewardsData({
owner: 'USER_WALLET_ADDRESS',
start_time: '2024-01-01' // ISO date string
});Claim Rewards
Claim accumulated rewards (two-step process).
// Step 1: Prepare claim request
const claimRequest = await sdk.reward.claimRewards({
owner: ownerKeypair.publicKey.toBase58(),
claim_rewards: '1000.5', // Amount to claim
sign_callback: async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
}
});
// Step 2: Process the claim
const result = await sdk.reward.processClaimRewards({
base64_tx: claimRequest.data.base64_tx,
sign_data: claimRequest.data.sign_data,
extra_data: claimRequest.data.extra_data
});Change Stake Amount
Adjust the amount of rewards to stake.
const result = await sdk.reward.changeStakeAmount({
owner: ownerKeypair.publicKey.toBase58(),
amount: 100, // Amount to stake
sign_callback: async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
}
});Change Auto-Stake Setting
Enable or disable automatic staking of rewards.
const result = await sdk.reward.changeAutoStake({
owner: 'USER_WALLET_ADDRESS',
auto_stake: true
});Get Stake Amount Records
Retrieve staking history.
const records = await sdk.reward.getStakeAmountRecord({
owner: 'USER_WALLET_ADDRESS',
change_type: 0, // 0=increase, 1=decrease, 2=auto stake
page_index: 0,
page_size: 20
});Sync On-chain Rewards
Synchronize on-chain rewards data.
const result = await sdk.reward.syncOnchainRewards('WALLET_ADDRESS');Vote API
Get Voting Projects
Retrieve available voting projects.
const projects = await sdk.vote.getVotingProjects({
id: 'PROJECT_ID', // Optional: filter by project ID
page_index: 0,
page_size: 20
});Get Voting Records
Query voting history.
const votingRecords = await sdk.vote.getVotingRecords({
user: 'USER_WALLET_ADDRESS', // Optional
project_id: 'PROJECT_ID', // Optional
page_index: 0,
page_size: 20
});Vote for Project
Cast a vote on a project.
const result = await sdk.vote.voteForProject({
user: userKeypair.publicKey.toBase58(),
project_id: 'PROJECT_ID',
amount: 100, // Must be a positive integer
vote_type: 0, // 0=vote yes, 1=vote no
sign_callback: async (msg: Uint8Array) => {
return userKeypair.sign(msg).signature;
}
});Get User Voting Stats
Get voting statistics for a user.
const stats = await sdk.vote.getUserVotingStats({
user: 'USER_WALLET_ADDRESS'
});WebSocket Listener (Node.js)
Start Validator WebSocket
In Node.js environment, start a single validator WebSocket connection.
const validatorKeypair = Keypair.fromSecretKey(bs58.decode(process.env.VALIDATOR_SECRET!));
await sdk.listener.startClientValidator(validatorKeypair);Start All Validators
Start all validators stored in local storage (automatically filters validators with keys).
const ownerAddress = 'OWNER_WALLET_ADDRESS';
await sdk.startAllValidatorFromLocalStorage(ownerAddress);Close WebSocket
await sdk.listener.closeWebSocket('VALIDATOR_PUBLIC_KEY');Service Worker Support (Browser)
For browser environments, the SDK supports running validators in a Service Worker for background processing.
Build Service Worker
Generate the service worker file:
yarn webpackThis creates dist/hyperfuseServiceWorker.js.
Register Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/hyperfuseServiceWorker.js')
.then(registration => {
console.log('Service Worker registered:', registration);
});
}Service Worker Commands
Get WebSocket State
sdk.getTimestampForWsStateInServiceWorker();Start Validator in Service Worker
sdk.startValidatorInServiceWorker('VALIDATOR_PUBLIC_KEY');Stop Validator in Service Worker
sdk.stopValidatorInServiceWorker('VALIDATOR_PUBLIC_KEY');Local Storage Management
Store Keypair
const keypair = Keypair.generate();
sdk.storeSecretInLocalStorage(keypair);Get Keypair from Storage
const secretKey = sdk.getSecretInLocalStorage('PUBLIC_KEY');
if (secretKey) {
const keypair = Keypair.fromSecretKey(bs58.decode(secretKey));
}Remove Keypair
sdk.removeSecretInLocalStorage('PUBLIC_KEY');Get Cached Validators
Get list of validators with keys in local storage (with caching).
const validators = await sdk.getCachedValidators('OWNER_WALLET_ADDRESS');
// Returns array of validator public keysResponse Format
All API methods return a standard response format:
interface ApiResponse<T> {
code: number; // 0 = success, non-zero = error
data: T; // Response data
err_msg: string; // Error message (empty on success)
}Paginated Responses
List endpoints return paginated data:
interface PageResp<T> {
total_count: number; // Total number of items
list: T[]; // Current page items
}Pagination Parameters
interface PageReq {
page_index?: number; // Page number (starts from 0)
page_size?: number; // Items per page
sort_type?: string; // e.g., "create_at,desc" or "status,asc"
}Error Handling
try {
const result = await sdk.user.getUser('WALLET_ADDRESS');
if (result.code === 0) {
console.log('Success:', result.data);
} else {
console.error('API Error:', result.err_msg);
}
} catch (error) {
console.error('Network Error:', error);
}Advanced Examples
Complete Validator Setup
import { SDK } from 'mirrorworld-sonic-hyperfuse-node-sdk';
import { Keypair } from '@solana/web3.js';
import bs58 from 'bs58';
async function setupValidator() {
// Initialize SDK
const sdk = new SDK({
sonicRpc: process.env.SONIC_RPC!,
backendDomain: process.env.BACKEND_DOMAIN!,
backendWssDomain: process.env.BACKEND_WSS_DOMAIN!,
runtime: 'node'
});
// Create keypairs
const ownerKeypair = Keypair.fromSecretKey(bs58.decode(process.env.OWNER_SECRET!));
const validatorKeypair = Keypair.generate();
// Sign callback
const signCallback = async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
};
// Register user if needed
await sdk.user.addUser({
owner: ownerKeypair.publicKey.toBase58(),
receive_rewards_account: ownerKeypair.publicKey.toBase58(),
name: 'My Node',
sign_callback: signCallback
});
// Register validator
const result = await sdk.registerValidatorForLocalStorage({
owner: ownerKeypair.publicKey.toBase58(),
keypair: validatorKeypair,
name: 'My Validator Node',
max_delegate_num: 1000,
delegate_fee: 5,
delegate_nft_node_num: 10,
sign_callback: signCallback
});
console.log('Validator registered:', result);
}
setupValidator();Monitor Validator Performance
async function monitorValidator(validatorPk: string) {
// Get validator info
const validators = await sdk.validator.getValidators({
owner: ownerAddress,
page_index: 0,
page_size: 1000
});
const validator = validators.data.list.find(v => v.pk === validatorPk);
if (validator) {
console.log({
name: validator.name,
isOnline: validator.is_online,
dailyUptime: validator.last_1_day_online_perc,
weeklyUptime: validator.last_7_day_online_perc,
totalRewards: validator.rewards,
delegations: validator.current_delegate_num,
commission: validator.delegate_fee
});
}
}Delegate and Claim Workflow
async function delegateAndClaim() {
const ownerKeypair = Keypair.fromSecretKey(bs58.decode(process.env.OWNER_SECRET!));
const signCallback = async (msg: Uint8Array) => {
return ownerKeypair.sign(msg).signature;
};
// Delegate nodes
await sdk.delegation.delegateTo({
owner: ownerKeypair.publicKey.toBase58(),
validator: 'VALIDATOR_PUBLIC_KEY',
num: 10,
sign_callback: signCallback
});
// Check rewards after some time
const userInfo = await sdk.user.getUser(ownerKeypair.publicKey.toBase58());
console.log('Total rewards:', userInfo.data.all_rewards);
// Claim rewards
const claimRequest = await sdk.reward.claimRewards({
owner: ownerKeypair.publicKey.toBase58(),
claim_rewards: userInfo.data.all_rewards,
sign_callback: signCallback
});
await sdk.reward.processClaimRewards({
base64_tx: claimRequest.data.base64_tx,
sign_data: claimRequest.data.sign_data,
extra_data: claimRequest.data.extra_data
});
}TypeScript Support
The SDK is written in TypeScript and provides full type definitions. Import types as needed:
import {
SDK,
Config,
ValidatorInfo,
DelegatationInfo,
RewardsActivity,
GetUserResp,
ApiResponse,
PageResp
} from 'mirrorworld-sonic-hyperfuse-node-sdk';Development
Build
Build both CommonJS and ES modules:
yarn buildTesting
Run tests:
yarn testRun specific test:
yarn test tests/validator.test.tsBuild Service Worker
yarn webpackPublishing
yarn build
yarn publishEnvironment Variables
Create a .env file for local development:
SONIC_RPC=https://api.mainnet-beta.sonic.game
BACKEND_DOMAIN=https://your-backend-domain.com
BACKEND_WSS_DOMAIN=wss://your-wss-domain.com
OWNER_SECRET=your_base58_encoded_private_key
VALIDATOR_SECRET=your_validator_base58_encoded_private_keySecurity Notes
- Never expose private keys: Always use environment variables or secure key management systems
- AES Encryption: Keypairs are encrypted before storage using AES-256
- Signature Verification: All state-changing operations require Ed25519 signatures
- Service Worker: Runs validators in isolated context for enhanced security
Browser Compatibility
- Chrome/Edge: ✅ Full support
- Firefox: ✅ Full support
- Safari: ✅ Full support
- Service Worker: Requires HTTPS in production
Node.js Compatibility
- Node.js 14+: ✅ Supported
- Node.js 16+: ✅ Recommended
- Node.js 18+: ✅ Recommended
License
MIT License - see LICENSE file for details
Support
- GitHub Issues: https://github.com/mirrorworld-universe/hyperfuse-client-sdk/issues
- Documentation: https://github.com/mirrorworld-universe/hyperfuse-client-sdk
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Changelog
See CHANGELOG.md for version history and updates.
