@0xio/sdk
v2.7.0
Published
Official TypeScript SDK for 0xio Wallet - Secure Octra blockchain integration for dApps
Maintainers
Readme
0xio Wallet SDK
Version: 2.7.0
Official TypeScript SDK for integrating DApps with 0xio Wallet on Octra Network.
What's New in v2.7.0
Security hardening release (post-audit remediation):
- MessageChannel transport (H-2 fix): Extension now uses a private
MessageChannelport instead ofwindow.postMessage, preventing page scripts from intercepting or injecting wallet messages. SDK validates a per-session nonce on every response. - Pluggable wallet adapters: New
src/supports/system — implementWalletTransportAdapterand drop a file insupports/to add any wallet without touching core SDK code. Seesrc/supports/template.tsfor the starting point. - Fixed wildcard postMessage target leaking request payloads to any origin (HIGH)
- Removed
Math.random()fallback for request IDs — throws ifcryptounavailable (MEDIUM) - Fixed
requestTimestampsunbounded memory growth in idle tabs (LOW) - Removed all
octraWalletReadylegacy event listeners andcreateOctraWalletalias (LOW) - Extension fixes: balance/network events now delivered end-to-end;
accountChangedon wallet switch; exact hostname tab matching; approval listener spoofing from content scripts blocked - All 17 post-audit findings resolved. See
AUDIT_REMEDIATION.mdfor full details.
What's New in v2.6.0
sendPrivateTransfer(to, amount): Send encrypted (stealth) transfers — amount is hidden from everyone except sender and recipient. Uses PVAC-HFHE for ciphertext subtraction + zero-knowledge proofs. The node re-encrypts under the recipient's key.getPendingPrivateTransfers(): List incoming private transfers claimable by your wallet.claimPrivateTransfer(transferId): Claim a pending private transfer into your encrypted balance.- Privacy-first DApps: Build prediction markets, private payments, stealth bets — all amounts stay encrypted on-chain.
- Requires 0xio Wallet Extension v2.4.0+
v2.5.0
switchNetwork(networkId): Switch the extension's network silently — no popup, no user action. DApps can detect network mismatch and offer one-click switch (like Rabby).getNetworkId(): Get the extension's current active network.- Network-aware DApps: Detect if user is on mainnet vs devnet, show banner, switch with one click.
v2.4.5
- Correct network detection: SDK reads active network from extension instead of hardcoding mainnet.
- Full networkInfo in connect response: Extension returns complete network config.
- Live balance on reconnect: Fresh balance from chain instead of stale cached zeros.
- Transaction history: Real on-chain transactions with pagination.
- Event alignment:
transactionConfirmedevent fires correctly.
v2.4.4
- Fix double popup on timeout: Transaction/signing methods no longer retry, preventing duplicate approval popups.
- 120s timeout for interactive methods: Users have 2 minutes to review and approve transactions.
v2.4.2
- Cross-origin iframe bridge: Localhost origins now accepted as trusted parents for dev/testing DApp browser scenarios.
- Parent origin capture:
walletReadysignal carriesparentOriginfor reliable cross-origin reply targeting. - postMessage fix: Replies to parent frame use captured origin instead of
window.location.origin(which fails cross-port).
v2.4.1
- No retry on user rejection: Transactions, contract calls, and sign requests rejected by the user no longer trigger automatic retry. Prevents double confirmation popups.
- Security hardening: postMessage origin validation, removed iframe auto-trust, response ID binding.
- Type fixes: Replaced
NodeJS.TimeoutwithReturnType<typeof setTimeout>for browser compatibility.
v2.4.0
- Desktop/Mobile DApp Bridge: SDK now supports running inside iframes (0xio Desktop) and WebViews (0xio App). Requests are relayed to the parent frame automatically.
- Auto Frame Detection: When
window.parent !== window, the SDK assumes a wallet bridge is available and marks the wallet as detected. - Frame-Aware Messaging:
postMessageToExtension()posts to bothwindowandwindow.parentwhen running inside a frame;setupMessageListener()accepts messages fromwindow.parent. - walletReady via postMessage: Extension detection now recognizes
walletReadyevents from parent frames. - Fully backward compatible — extension-based DApps work unchanged with no code changes needed.
Installation
npm install @0xio/sdkQuick Start
import { ZeroXIOWallet } from '@0xio/sdk';
// 1. Initialize
const wallet = new ZeroXIOWallet({
appName: 'My DApp',
requiredPermissions: ['read_balance', 'sign_messages']
});
await wallet.initialize();
// 2. Connect
const connection = await wallet.connect();
console.log('Connected:', connection.address);
console.log('Public Key:', connection.publicKey); // Base64 Ed25519 key
// 3. Get Balance
const balance = await wallet.getBalance();
console.log('Total:', balance.total, 'OCT');
// 4. Sign a Message
const signature = await wallet.signMessage('Hello, 0xio!');
console.log('Signature:', signature);
// 5. Send Transaction
const result = await wallet.sendTransaction({
to: 'oct1recipient...',
amount: 10.5,
message: 'Payment'
});
console.log('TX Hash:', result.txHash);API Reference
Connection
wallet.initialize(): Promise<boolean>
Initialize the SDK. Must be called first.
wallet.connect(options?): Promise<ConnectEvent>
Connect to the user's wallet. Shows approval popup if first time.
wallet.disconnect(): Promise<void>
Disconnect from the wallet.
wallet.isConnected(): boolean
Check if currently connected.
Balance
wallet.getBalance(forceRefresh?: boolean): Promise<Balance>
Get wallet balance (public + private).
interface Balance {
public: number; // Visible on-chain balance
private: number; // Encrypted (FHE) balance
total: number; // public + private
currency: 'OCT';
}Transactions
wallet.sendTransaction(txData): Promise<TransactionResult>
Send a transaction. Returns result with transaction finality status.
interface TransactionData {
to: string; // Recipient address (oct1...)
amount: number; // Amount in OCT
message?: string; // Optional memo
}
interface TransactionResult {
txHash: string;
success: boolean;
finality?: 'pending' | 'confirmed' | 'rejected';
message?: string;
explorerUrl?: string;
}Smart Contracts
wallet.callContract(data: ContractCallData): Promise<TransactionResult>
Execute a state-changing contract call. The extension signs and submits via octra_submit.
const result = await wallet.callContract({
contract: 'oct26Lia...', // Contract address
method: 'swap', // AML method name
params: [100, true, 90], // Method arguments (flat, not array-wrapped)
amount: '0', // Native OCT to send (optional, default '0')
ou: '10000', // Operational units (optional, default '10000')
});
console.log('TX Hash:', result.txHash);wallet.contractCallView(data: ContractViewCallData): Promise<any>
Read-only contract query. No signing, no approval popup, no wallet unlock required.
const price = await wallet.contractCallView({
contract: 'oct26Lia...',
method: 'get_active_price',
params: [],
});
console.log('Price:', price);wallet.getContractStorage(contract: string, key: string): Promise<string | null>
Read contract storage by key.
const value = await wallet.getContractStorage('oct26Lia...', 'total_supply');
console.log('Total supply:', value);Message Signing
wallet.signMessage(message: string): Promise<string>
Sign an arbitrary message with the wallet's private key. User will be prompted to approve.
// Sign a message for authentication
const message = `Login to MyDApp\nTimestamp: ${Date.now()}`;
const signature = await wallet.signMessage(message);
// Signature is base64-encoded Ed25519
console.log('Signature:', signature);Use cases:
- Prove wallet ownership for API authentication
- Sign login challenges
- Authorize off-chain actions
- Create verifiable attestations
Events
wallet.on('connect', (event) => console.log('Connected:', event.data.address));
wallet.on('disconnect', (event) => console.log('Disconnected'));
wallet.on('balanceChanged', (event) => console.log('New balance:', event.data.newBalance.total));
wallet.on('accountChanged', (event) => console.log('Account changed:', event.data.newAddress));
wallet.on('networkChanged', (event) => console.log('Network:', event.data.newNetwork.name));Error Handling
import { ZeroXIOWalletError, ErrorCode } from '@0xio/sdk';
try {
const result = await wallet.sendTransaction({ to: 'oct1...', amount: 10 });
} catch (error) {
if (error instanceof ZeroXIOWalletError) {
switch (error.code) {
case ErrorCode.USER_REJECTED:
console.log('User rejected the request');
break;
case ErrorCode.INSUFFICIENT_BALANCE:
console.log('Not enough balance');
break;
case ErrorCode.INVALID_SIGNATURE:
console.log('Invalid transaction signature');
break;
case ErrorCode.DUPLICATE_TRANSACTION:
console.log('Transaction already submitted');
break;
case ErrorCode.SELF_TRANSFER:
console.log('Cannot send to yourself');
break;
case ErrorCode.NONCE_TOO_FAR:
console.log('Transaction nonce too far ahead');
break;
case ErrorCode.WALLET_LOCKED:
console.log('Please unlock your wallet');
break;
}
}
}Networks
The SDK ships with built-in configurations for Octra networks:
import { NETWORKS, getNetworkConfig } from '@0xio/sdk';
// Get devnet config
const devnet = getNetworkConfig('devnet');
console.log(devnet.rpcUrl); // http://165.227.225.79:8080
console.log(devnet.supportsPrivacy); // true
console.log(devnet.isTestnet); // true
// Get mainnet config
const mainnet = getNetworkConfig('mainnet');
console.log(mainnet.rpcUrl); // http://46.101.86.250:8080
console.log(mainnet.supportsPrivacy); // true| Network | Privacy (FHE) | Explorer | |---------|:---:|---| | Mainnet Alpha | Yes | octrascan.io | | Devnet | Yes | devnet.octrascan.io |
NetworkInfo Type
interface NetworkInfo {
id: string;
name: string;
rpcUrl: string;
explorerUrl?: string; // Transaction explorer base URL
explorerAddressUrl?: string; // Address explorer base URL
indexerUrl?: string; // Indexer/API base URL
supportsPrivacy: boolean; // FHE encrypted balance support
color: string; // Brand color hex
isTestnet: boolean;
}Desktop & Mobile Support
The SDK automatically detects when your DApp is running inside:
- 0xio Desktop's built-in browser — Your DApp is loaded in an iframe. The SDK detects
window.parent !== windowand relays all requests to the desktop wallet via the iframe bridge. - 0xio App's built-in browser — Your DApp is loaded in a WebView. The SDK communicates with the mobile wallet via the existing WebView bridge.
- 0xio Wallet Extension — Standard browser extension communication (unchanged).
No code changes are needed for DApp developers. Just use the SDK as normal and it will auto-detect the environment and choose the correct transport.
Wallet Adapters
The SDK ships with a pluggable adapter system so multiple wallets can be supported without changing core code.
Auto-detect
import { detectWalletAdapter, ZeroXIOWallet } from '@0xio/sdk';
const adapter = detectWalletAdapter();
if (!adapter) throw new Error('No supported wallet found');
const wallet = new ZeroXIOWallet({ appName: 'My DApp', adapter });Pass an adapter explicitly
import { ZeroXIOWallet, ZeroXIOAdapter } from '@0xio/sdk';
const wallet = new ZeroXIOWallet({ appName: 'My DApp', adapter: ZeroXIOAdapter });Add support for another wallet
- Copy
src/supports/template.ts→src/supports/my-wallet.ts - Fill in the four constants (
REQUEST_SOURCE,RESPONSE_SOURCE,WINDOW_KEY,READY_EVENT) and the message-shape mapping - Register it in
src/supports/index.tsREGISTERED_ADAPTERS - Export it from
src/index.tsif you want it in the public API
See DOCUMENTATION.md → Wallet Adapter System for the full interface spec.
Requirements
- 0xio Wallet Extension v2.0.1 or higher (Mainnet Alpha)
- 0xio Wallet Extension v2.2.1 or higher (Devnet — required for contract calls and privacy features)
- 0xio Desktop v1.0+ (for iframe bridge support)
- 0xio App v1.0+ (for WebView bridge support)
- Modern browser (Chrome, Firefox, Edge, Brave)
Documentation
See DOCUMENTATION.md for complete API reference.
License
MIT License. Copyright 2026 0xio Labs.
