Skip to main content
The 0xio Wallet SDK enables seamless integration between your decentralized application and the 0xio Wallet across all platforms: Extension, Desktop (iframe), and Mobile (WebView): with full support for Octra’s FHE privacy features.

Installation

Install via npm or yarn:
Package Details:
  • Version: 2.8.1
  • Min Extension (Mainnet): v2.0.1+
  • Min Extension (Devnet): v2.2.1+ (contract calls, privacy features)
  • Private transfers and claims: Extension v2.4.0+
  • Private primitives and framed message signing: Extension v2.5.5+
  • RFC-O-1 support: Extension v2.4.3+ (for the window.octra provider and octra#initialized event)
  • Browser: Chrome 111+ required for the extension transport (uses crypto + structured-clone messaging)
  • License: MIT

Project Setup & Requirements

[!IMPORTANT] The SDK relies on Node.js globals (Buffer, process, crypto) for secure operations. Modern build tools like Vite or Webpack 5+ do not provide these by default. You MUST configure polyfills.

Vite Configuration

  1. Install the polyfill plugin:
  1. Update vite.config.ts:

Webpack 5 Configuration

If using Create React App (CRA) v5 or custom Webpack, install react-app-rewired or customize your config:

Quick Start

The fastest way to get started: createZeroXIOWallet handles initialization and optional auto-connect:
Or with manual control:

Amounts and Units

The wallet and the RFC-O-1 provider count in raw micro-OCT: one OCT is 1000000 units. The SDK never rescales a raw field, so what a dapp sends today keeps working. Where you would rather write OCT, use the OCT field and the SDK converts it with exact decimal arithmetic. Pass one field or the other, never both. A number that cannot be written in six decimals (such as 0.1 + 0.2) is rejected with INVALID_AMOUNT; pass a string instead.

Permissions

Ask for what the dapp uses. The wallet enforces these scope names; the private ones show a warning in the connection dialog. The older SDK names (read_balance, send_transactions, sign_messages, view_private_balance, stealth_claim and the rest) still work. The SDK translates them when it connects, and the names you asked for come back in the granted list as aliases, so a check like permissions.includes('read_balance') keeps working. WALLET_PERMISSIONS, LEGACY_PERMISSION_MAP, toWalletPermissions and withLegacyAliases are exported. The wallet refuses anything that signs, submits or changes state until the page has connected (NOT_CONNECTED), and opens its unlock screen at most once a minute for a page that is not connected (WALLET_LOCKED).

Security (v2.7.x)

The SDK includes several security hardening measures:
  • Origin-validated messaging: postMessage communication validates origins against a strict trusted set. No wildcard origins.
  • Session-nonce binding: every bridge response must carry the session nonce issued at connect time. Same-origin impersonation and forged responses are rejected.
  • No auto-trust for iframes: the SDK does not assume an iframe parent is a wallet. It requires a verified walletReady signal from a trusted origin.
  • Response binding: only responses matching a pending request ID are accepted.
  • No retry on rejection: user-rejected transactions do not trigger automatic retries.
  • Configurable trusted origins: set trustedParentOrigins in the constructor config to restrict which origins are trusted as the parent iframe bridge (disables implicit localhost trust).
  • RPC URL validation: http:// RPC endpoints are rejected on non-testnet networks, blocking a malicious bridge from injecting an insecure endpoint.

Desktop & Mobile Support

The SDK automatically detects the wallet environment and uses the appropriate transport. No code changes are required.

How It Works

  • Auto Frame Detection: When window.parent !== window, the SDK assumes a wallet bridge (Desktop or Mobile) is available and marks the wallet as detected.
  • Dual Posting: postMessageToExtension() posts to both window (for extension content script) and window.parent (for iframe/WebView bridge).
  • Frame-Aware Listener: setupMessageListener() accepts messages from window.parent in addition to same-window messages.
  • walletReady via postMessage: Extension detection recognizes walletReady events sent via postMessage from parent frames.
DApp developers do not need to change any code. Just use the SDK as normal: it auto-detects the environment and selects the correct transport.

Cross-Origin Iframe Bridge (v2.4.4)

Starting in v2.4.4, the SDK supports cross-origin iframe communication for localhost development. This allows DApps running on localhost or 127.0.0.1 during development to communicate with the wallet through an iframe bridge, without requiring the extension content script to be injected into the development server origin. For production frames, pass trustedParentOrigins in the constructor config to allowlist the exact origins you trust:

RFC-O-1 Provider Standard (v2.7.0)

The SDK includes an OctraProviderAdapter that enables any RFC-O-1 compliant wallet (not just 0xio) to work with the SDK. This allows third-party wallets that implement window.octra to be used as a drop-in transport.

How It Works

The SDK’s adapter registry tries transports in priority order: If the 0xio extension is installed, it takes priority. If not, any RFC-O-1 compliant wallet works automatically.

Using a Third-Party Wallet

No code changes are needed. The SDK auto-detects:

Direct Provider Access

For advanced use cases, you can use the provider directly:

RFC-O-1 Error Codes


Core API

Connection Methods

initialize()

Initializes the SDK and checks for extension presence.
  • Returns: Promise<boolean>
  • Throws: ZeroXIOWalletError with code EXTENSION_NOT_FOUND if 0xio is not installed

isReady()

Check if SDK is initialized and extension is available.
  • Returns: boolean

connect(options)

Requests connection to the extension. User must approve in popup.
  • Returns: Promise<ConnectEvent> with address, balance, networkInfo, and permissions
  • Throws: ZeroXIOWalletError with code CONNECTION_REFUSED if user declines

disconnect()

Disconnects from the extension.
  • Returns: Promise<void>

isConnected()

Checks current connection status.
  • Returns: boolean

getConnectionInfo()

Gets the cached connection info.
  • Returns: ConnectionInfo

getConnectionStatus()

Checks connection status with extension (async).
  • Returns: Promise<ConnectionInfo>

getAddress()

Gets the currently connected address.
  • Returns: string | null

getPublicKey()

The connected account’s Ed25519 public key, base64. Served from the session when the wallet reported it at connect, otherwise asked from the wallet. Use it with verifyMessage.
  • Returns: Promise<string>

switchNetwork(networkId)

Ask the wallet to switch its active network from a connected dapp. The wallet asks the user to confirm first (extension 2.5.6 and later, app 1.3.0 and later, desktop 0.4.1 and later); declining rejects with USER_REJECTED. The call is also refused while another request from the page is waiting for approval, so a transaction under review can never move to another network. The page must be connected first.
  • Params: networkId: string, 'mainnet' or 'devnet'
  • Returns: Promise<{ network: string; switched: boolean }>
  • Requires: Extension v2.3.6+, and a connected page

getNetworkId()

Gets the extension’s current active network.
  • Returns: string | null

Balance Methods

getBalance(forceRefresh?)

Fetches current balances from blockchain or cache.
  • Parameters:
    • forceRefresh: If true, queries blockchain; if false, uses cached values
  • Returns: Promise<Balance> with public, private, total (numbers), and currency

Transaction Methods

sendTransaction(txData)

Executes a standard public transfer.
  • Parameters:
  • Returns: Promise<TransactionResult> with hash, accepted, status (and the older txHash, success)

signTransaction(txData) and submitTransaction(signedTx)

Sign without broadcasting, then broadcast later. signTransaction takes the same TransactionData and opens the same approval; submitTransaction sends the signed object to the network. The wallet only relays transactions signed by the connected address.

sendPrivateTransfer(txData)

Executes an FHE-encrypted stealth transfer. The extension handles PVAC ciphertext generation, range proofs, and ECDH key exchange. User must approve via the extension approval popup. Requires private_transfers permission.
Requires Extension v2.4.0+. After the approval the wallet generates FHE proofs, which takes from 17 seconds to a few minutes depending on the engine (Desktop native, then WASM with threads, then single-threaded WASM). The SDK waits up to 10 minutes for this call. The wallet checks the private balance first and refuses, with the reason, when it cannot be spent yet (over the layer cap or held for migration).
  • Parameters:
  • Returns: Promise<TransactionResult> with hash and accepted
  • Note: The amount is encrypted on your device before transmission

getTransactionHistory(page, limit)

Gets transaction history.
  • Parameters: page (default: 1), limit (default: 20)
  • Returns: Promise<TransactionHistory>

Message Signing

signMessage(message)

Signs an arbitrary message with the wallet’s private key using Ed25519. The user will be prompted to approve the signature request in the extension popup.
  • Parameters: message: string - The text message to sign (must be non-empty)
  • Returns: Promise<string> - Base64-encoded Ed25519 signature (64 bytes)
  • Throws:
    • SIGNATURE_FAILED - If signing fails or message is invalid
    • USER_REJECTED - If user rejects the signature request
    • WALLET_LOCKED - If wallet is locked
The 0xio Signed Message standard. The wallet never signs the raw message. It signs a framed payload, "Octra Signed Message:\n" + utf8ByteLength(message) + "\n" + message, as an Ed25519 detached signature over the UTF-8 bytes of that string. This guarantees a signed message can never collide with a transaction pre-image (which is canonical JSON beginning with {). Verify with verifyMessage below, or reconstruct the bytes with getSignedMessageBytes and use any Ed25519 library.
Use Cases:
  • Authentication: Prove wallet ownership for login/API access
  • Attestation: Sign data for on-chain verification
  • Authorization: Approve off-chain actions
  • API Keys: Create authenticated API credentials for Oracle/Indexer
Always include timestamps or nonces in messages to prevent replay attacks. Never sign messages you don’t understand.

signAuthMessage(service, nonce)

Convenience wrapper that builds a domain-separated auth message and signs it. Includes the current origin for anti-phishing.
  • Parameters: service: string (service name), nonce: string (unique challenge)
  • Returns: Promise<string> - Base64-encoded Ed25519 signature
To verify an auth signature, reconstruct the signed string with buildAuthMessage(service, nonce, origin) and pass it to verifyMessage.

verifyMessage(message, signature, publicKey)

Verifies a 0xio message signature using the Web Crypto Ed25519 primitive (zero runtime dependencies). Available on Node 18+, Chrome 137+, Safari 17+, and Firefox 129+.
  • Parameters: message: string, signature: string (base64), publicKey: string (base64)
  • Returns: Promise<boolean>

getSignedMessageBytes(message)

Returns the exact bytes the wallet signs, so you can verify with any Ed25519 library instead of Web Crypto (for example server-side, or on an older runtime).
  • Parameters: message: string
  • Returns: Uint8Array

Smart Contract Methods

callContract(data)

Executes a state-changing smart contract call. The extension builds, signs, and submits the transaction via octra_submit. The user will see an approval popup.
  • Parameters:
  • Returns: Promise<TransactionResult> with txHash, success, finality
AML contract calls use flat arguments: params: [arg1, arg2], not params: [[arg1, arg2]]. The extension handles nonce, signing, and broadcasting automatically.

contractCallView(data)

Read-only contract query. Does not require wallet unlock, signing, or user approval. Only requires initialize() (not connect()). For a page that is not connected the wallet leaves the caller empty unless you pass caller.
  • Parameters:
  • Returns: Promise<any>, the raw result from the contract method

getContractStorage(contract, key)

Read a single key from contract storage. No wallet unlock or approval needed.
  • Parameters: contract: string (contract address), key: string (storage key)
  • Returns: Promise<string | null>, the storage value, or null if not found

sendContractTransactionSequence(data)

Several contract calls under one approval, submitted in order with consecutive nonces. The approval window lists every step with its contract, attached value and fee.
  • Returns: Promise<unknown> with results[], one hash per step

request(method, params) and rpcCall(method, params)

request sends any wallet method through the bridge, for primitives that have no typed helper yet. rpcCall reaches the wallet’s read-only node RPC allow-list (octra_balance, octra_transaction, contract_call and similar); writes are refused with METHOD_NOT_ALLOWED.

Privacy Operations

encryptBalance(amount)

Not served by the 0xio extension: the wallet answers NOT_AVAILABLE, and users encrypt from the extension’s Privacy screen. Kept for wallets that may implement it. Moves funds from Public to Private balance (shielding). The extension generates the FHE cipher, bound proof, and submits the transaction.
  • Parameters: amount: number | string (OCT amount to shield)
  • Returns: Promise<TransactionResult> with txHash, success, finality
Amounts on encryptBalance, decryptBalance, sendPrivateTransfer, and callContract accept string | number. A numeric amount that can’t be represented exactly in micro-OCT (6 decimals) throws INVALID_AMOUNT; pass a string (e.g. "0.300000") for exact control.

decryptBalance(amount)

Not served by the 0xio extension (see encryptBalance). Moves funds from Private to Public balance (unshielding). Includes range proof generation (17-300s depending on engine).
  • Parameters: amount: number | string (OCT amount to unshield)
  • Returns: Promise<TransactionResult> with txHash, success, finality
Approvals wait up to 3 minutes; sendPrivateTransfer waits up to 10 minutes because proof generation follows the approval.

getPrivateBalanceInfo()

Gets detailed private balance information.
  • Returns: Promise<PrivateBalanceInfo>

getPendingPrivateTransfers()

Gets pending incoming private transfers.
  • Returns: Promise<PendingPrivateTransfer[]>. The 0xio extension fills id and a raw micro-OCT amount; the other fields depend on the wallet.
  • Requires: private_balance_read

claimPrivateTransfer(transferId)

Claims a pending private transfer. The wallet opens an approval window showing the fee; a claim is a signed, fee-paying transaction.
  • Parameters: transferId: string
  • Returns: Promise<TransactionResult>
  • Requires: private_claims (or private_transfers)

Private Primitives (2.8.0)

Building blocks for private contracts. Keys never leave the wallet: the dapp receives ciphertexts, proofs and hashes. None of these opens a popup except registerPrivateViewKey; proofs take from seconds to a couple of minutes, and the SDK waits up to 3 minutes.

Network Methods

getNetworkInfo()

Gets current network information.
  • Returns: Promise<NetworkInfo>

Event Listeners

The SDK extends EventEmitter for real-time state updates.

Account Changed

Your site was moved to another wallet. A connection belongs to the wallet it was approved for: when the user switches wallets, 0xio asks whether to move connected sites to the new one, and only a site that is moved receives this event. A site that is not moved stays on its wallet, and while another wallet is active its wallet requests are refused with NOT_CONNECTED and getConnectionStatus reports it as not connected. It works again as soon as the user switches back or connects it again.

Network Changed

User switched networks (mainnet/testnet).

Balance Changed

Balance updated after transaction confirmation.

Connect

Wallet connected successfully.

Disconnect

User disconnected from dApp or locked wallet.

Transaction Confirmed

Transaction was confirmed on-chain.

Transaction Failed

The wallet could not broadcast a queued transaction.

Extension Lock Events

Removing Listeners

Cleanup

Clean up SDK resources when done.

Error Handling

The SDK provides typed error codes for graceful error handling.

Error Codes

Framework Integration

React Hook

Vue.js Composition API

TypeScript Definitions

The SDK exports comprehensive TypeScript types:

Utility Functions

The SDK exports useful utility functions:

SDK Compatibility

Best Practices

Connection

  • Always check isConnected() before operations
  • Handle USER_REJECTED errors gracefully
  • Provide clear install prompts if extension not found
  • Request minimum necessary permissions
  • Use getConnectionStatus() to restore existing connections

Transactions

  • Validate addresses with isValidAddress() before sending
  • Display full transaction details to users
  • Handle all error cases explicitly
  • Show loading states during confirmation
  • Provide transaction hash for tracking

Message Signing

  • Always include timestamps or nonces to prevent replay attacks
  • Display the full message to users before signing
  • Use the public_transactions scope for authentication flows
  • Verify signatures server-side using Ed25519
  • Never sign messages that look like transaction data

Privacy

  • Clearly indicate when using FHE features
  • Explain gas cost differences
  • Let users choose public vs private
  • Don’t store sensitive data

Performance

  • Cache balance data appropriately
  • Use forceRefresh sparingly
  • Batch multiple operations when possible
  • Clean up event listeners on unmount with cleanup()
For production dApps, implement comprehensive error handling and user feedback for all wallet operations.