# Public vs Private Balances Source: https://docs.0xio.xyz/concepts/balances Compare public and private balances in 0xio, how each balance type works on Octra, and when to use FHE-encrypted balances for privacy. Every 0xio wallet holds two distinct balance types on the same Octra address, giving you full control over your financial privacy. * **Public balance (OCT):** a normal, transparent on-chain balance. * **Private balance (pOCT):** an FHE-encrypted balance whose amount is hidden from everyone except you. ## Public Balance Your **Public Balance** (OCT) is visible to everyone on the blockchain. ### Characteristics * **Transparency:** Visible to anyone viewing the blockchain * **Speed:** Faster transaction processing (no proof generation required) * **Compatibility:** Works with all protocols and services ### When to Use * Standard interactions with public DeFi protocols * Sending to exchanges that don't support FHE * When transparency is beneficial or required * Situations where speed and lower cost are priorities ## Private Balance Your **Private Balance** (pOCT) is encrypted with Octra's FHE layer and only visible to you. ### Characteristics * **Privacy:** Amount encrypted on-chain; completely confidential * **Security:** Complete financial privacy * **Validation:** Transfers are validated with zero-knowledge range proofs without revealing amounts * **Cost:** Privacy operations carry a larger fee because the network must process ciphertexts and verify proofs ### When to Use * Confidential transfers and payments * Private trading and swaps * Salary payments and business transactions * Protecting wealth information from public view > [!NOTE] > > Your 0xio wallet displays both balances simultaneously. You move funds between them at any time using **Encrypt** (public → private) and **Decrypt** (private → public), and receive incoming private transfers with **Claim**. ## Moving Between Balances ### Encrypting Funds (Public → Private) This "shields" your funds by moving OCT into your encrypted pOCT balance so the amount is no longer visible to blockchain observers. **Process:** 1. Open the Encrypt action 2. Enter the amount to shield 3. Review the fee 4. Confirm the transaction 5. Wait for blockchain confirmation **Technical note:** Your device generates the FHE ciphertext and the supporting range proof locally before broadcasting. Proof generation can take anywhere from seconds (native desktop) to over a minute (mobile), depending on platform. ### Decrypting Funds (Private → Public) This "unshields" funds by moving pOCT back into your public OCT balance, making the amount visible on the blockchain again. A zero proof attests that the remaining encrypted balance stays consistent. **Process:** 1. Open the Decrypt action 2. Enter the amount to unshield 3. Review the fee 4. Confirm the transaction ### Claiming Incoming Private Transfers When someone sends you a private (stealth) transfer, the funds arrive as a pending encrypted output rather than landing in your balance automatically. **Claim** scans incoming outputs for those addressed to you and adds them to your private balance. ## Network Fees Octra denominates fees in **ou**, the network's base fee unit (1 OCT = 1,000,000 ou). * Standard public transfers carry a small, flat fee. * Stealth and other privacy operations require a minimum fee of **1,000,000 ou (1 OCT)** because the network must process ciphertexts and verify zero-knowledge proofs. Exact fees are estimated by the wallet at the time of each transaction. # Cryptographic Specifications Source: https://docs.0xio.xyz/concepts/cryptography Technical reference for 0xio cryptographic primitives including Ed25519 signatures, AES-GCM vault encryption, PBKDF2, and SHA-256 hashing. 0xio uses standard, widely reviewed cryptographic algorithms. ## Signing Algorithm **Ed25519** (Twisted Edwards Curve) * High-speed signature generation and verification * 256-bit security level * Implemented via TweetNaCl.js for browser compatibility * Resistant to side-channel attacks ## Hashing * **SHA-256:** Primary hashing algorithm for address generation * **HMAC-SHA512:** Used in key derivation processes ## Symmetric Encryption **AES-256-GCM** for local vault protection * Authenticated encryption: guarantees both confidentiality and integrity * Protects your private keys at rest on the device * The vault key is derived from your password with **PBKDF2-HMAC-SHA256** * In the extension the vault is sealed under a random vault key, which is stored only wrapped by the key derived from your password. While unlocked, the session holds that vault key, never your password, and changing the password re-wraps the vault key without touching the vault The PBKDF2 iteration count is tuned per platform to balance security against device performance: | Platform | Vault KDF iterations | | - | - | | Extension | 900,000 | | Desktop | 600,000 | | Mobile (PIN) | 100,000 | ## Key Derivation ### Mnemonic Standard **BIP-39** recovery phrases (English wordlist) * 0xio generates **12-word** phrases (128-bit entropy) * Imports of longer phrases (e.g. 24-word, 256-bit) are also accepted * Dictionary: the 2048-word official BIP-39 English wordlist ### Seed Generation **PBKDF2-HMAC-SHA512** with 2048 iterations * Converts the mnemonic to a 64-byte cryptographic seed * Salt: the string `mnemonic` plus an optional passphrase (per BIP-39) ### Master Root Key Derivation All 0xio platforms (extension, mobile, desktop) use the same canonical key derivation: ``` master_key = HMAC-SHA512("Octra seed", seed)[0:32] ``` * **No HD path:** Octra uses the master root key directly; there is no BIP-44 derivation path * **Deterministic:** The same seed phrase always produces the same master key * **Cross-platform:** Importing the same seed phrase produces the same address on all 0xio products (extension, mobile, desktop) * **Secure:** HMAC-SHA512 provides cryptographic strength; the first 32 bytes become the Ed25519 seed (the next 32 bytes form the chain code) ### EVM Key Derivation (Extension) With EVM on, the browser extension also derives a secp256k1 key from the same BIP-39 seed along the standard path `m/44'/60'/0'/0/i`, where `i` is the account index of the matching Octra account. The 0x address (EIP-55 checksummed) is the one other EVM wallets derive from that phrase. Only the address is stored; the key is derived for a single signature, checked against the address and wiped, together with the master key and every intermediate node. A wallet imported from an Octra private key has no EVM key. ## Address Format 0xio generates Octra addresses through a multi-step process: **1. Generate Ed25519 Public Key** Derive the 32-byte public key from the private key using Ed25519 **2. Hash Public Key** Compute the SHA-256 hash of the raw public key bytes **3. Base58 Encoding** Encode the 32-byte hash using Base58 (Bitcoin alphabet) **4. Add Prefix** Prefix with `oct` for Octra Network identification **Result:** A 47-character string (`oct` + 44 Base58 characters). During generation, 0xio "grinds" (regenerating any key whose encoded hash is shorter than 44 characters), so every address is exactly 47 characters. ## Security Architecture | Component | Implementation | | - | - | | **Private Key Storage** | AES-256-GCM encrypted vault on device | | **Session Management** | Auto-lock with configurable timeout | | **Password Protection** | PBKDF2-HMAC-SHA256 with user-defined password | | **Telemetry** | No tracking or analytics. Desktop and mobile send crash reports without keys or phrases | | **Network Communication** | HTTPS only | > [!NOTE] > > What each client sends, and to whom, is set out in the [Privacy Policy](https://0xio.xyz/privacy). ## Privacy Cryptography (PVAC) Octra's private balances are powered by **PVAC**, an additively-homomorphic encryption scheme. All PVAC cryptography runs locally on your device through **pvac-rs**, a purpose-built Rust library built on the `curve25519-dalek` Ristretto255 group. ### Core Operations 1. **FHE Encrypt/Decrypt:** Additively-homomorphic encryption of token amounts, so ciphertexts can be added and subtracted on-chain (`ct_add` / `ct_sub`) without ever decrypting 2. **Range Proofs:** Bulletproofs-style zero-knowledge proofs (64-bit) that an encrypted amount is non-negative and within bounds, without revealing the value 3. **Pedersen Commitments:** Binding commitments to amounts using Ristretto255 points, used for transaction integrity 4. **Bound & Zero Proofs:** Used when decrypting and when validating balance updates to prove a ciphertext encrypts zero (or a bounded value) 5. **Stealth Transfers:** An X25519 ECDH shared secret derives a per-output stealth tag and claim keys, so private sends are unlinkable and the recipient scans for outputs addressed to them ### Stealth Transfer Detail A stealth transfer derives its routing and claim material entirely from an ECDH shared secret: * **Shared secret:** `SHA-256(X25519(sender_ephemeral_sk, recipient_view_pub))` * **Stealth tag:** first 16 bytes of `SHA-256(shared_secret ‖ "OCTRA_STEALTH_TAG_V1")`, which the recipient looks for when scanning on-chain outputs * **Amount:** encrypted to the recipient with AES-256-GCM keyed by the shared secret * **Claim keys:** further domain-separated SHA-256 derivations bind the claim to the recipient's address ### pvac-rs The **pvac-rs** library compiles to multiple targets to run natively on each platform: | Platform | Target | | - | - | | Extension | WebAssembly (with multithreading via `wasm-bindgen-rayon`) | | Desktop | Native Rust binary | | iOS | Static library (`aarch64`) | | Android | Shared library (`arm64`, `armv7`, `x86_64`) | Range-proof generation time varies widely by platform, from seconds on native desktop builds to over a minute on mobile. **Key dependencies:** `curve25519-dalek` (Ristretto255 group arithmetic and scalars) and `sha2` / `sha3` for hashing. The browser/mobile JavaScript layer separately uses `@noble/hashes` and `tweetnacl`. # Cross-Chain Privacy Layer Source: https://docs.0xio.xyz/concepts/privacy-layer How 0xio uses Octra as a privacy backend for ETH, SOL, and any chain: swap to OCT, encrypt via FHE, transfer privately, swap back. > [!NOTE] > > **Status: Research & Design Phase** > This feature is planned for future development. The architecture builds on Octra's existing FHE (PVAC) primitives and 0xio's cross-chain swap infrastructure. ## The Vision **Use Octra as a privacy middleware layer for any blockchain.** Users swap ETH, SOL, or USDC into OCT via the 0xio DEX, encrypt the OCT balance using Octra's FHE, transfer privately, then the recipient decrypts and swaps back to any token on any chain. OCT is the **privacy denomination**: all assets flow through it. No new wrapped tokens needed. ``` ETH User → DEX swap to OCT → encrypt → private transfer → decrypt → DEX swap to ETH/SOL/USDC ``` ## How It Differs from Umbra ### Umbra on Ethereum Stealth addresses (ECDH). Every payment goes to a one-time address that only the recipient can link to their identity. Simple but **amounts are still visible** on-chain. ### Umbra on Solana Full privacy via Arcium MPC: encrypted balances, mixer pool with ZK proofs, relayer network. Privacy on **both** the source and destination chain. End-to-end encryption. ### 0xio Privacy Layer FHE-encrypted transfers on Octra as a **middle hop**. Privacy exists only on the Octra layer; the deposit and withdrawal on ETH/SOL are public. | Aspect | Umbra (ETH) | Umbra (SOL) | 0xio (octra) | | - | - | - | - | | Source chain | Public (stealth addr) | Encrypted | **Public** | | Transfer layer | Same chain | Same chain | **Encrypted (Octra FHE)** | | Destination chain | Public (stealth addr) | Encrypted | **Public** | | Amount hidden | No | Yes | **Yes (on Octra only)** | | Cross-chain | No | No | **Yes** | | Tech | ECDH | MPC + ZK | **FHE** | ## Two Privacy Modes ### Mode 1: Basic (Octra-Only Privacy) The simplest version uses only the existing 0xio DEX + Octra FHE. Deposits and withdrawals are public, but the transfer itself is private. ``` [PUBLIC] User swaps 1 ETH → OCT via DEX ← visible on Etherscan [PRIVATE] OCT encrypted on Octra via FHE ← amount hidden [PRIVATE] Transfer encrypted OCT to recipient ← sender, receiver, amount all hidden [PRIVATE] Recipient holds encrypted OCT ← invisible [PUBLIC] Recipient swaps OCT → SOL via DEX ← visible on Solana ``` **What's private**: who sent to whom, how much, when the recipient cashes out. **What's NOT private**: the deposit and withdrawal are visible on ETH/SOL. Timing + amount correlation can link them. > [!WARNING] > > Basic mode is not end-to-end private. For maximum privacy, use Advanced mode or hold encrypted OCT for extended periods and withdraw in pieces to different addresses. ### Mode 2: Advanced (End-to-End Privacy) Uses mature, live privacy tech on **every chain** in the pipeline. Each hop is shielded by a different technology optimized for that chain. ``` [PRIVATE] User shields ETH into RAILGUN pool ← sender unlinked from deposit [PRIVATE] Unshield to 0xio bridge deposit ← looks like generic RAILGUN withdrawal [PRIVATE] Bridge swaps to OCT on Octra ← encrypted by FHE [PRIVATE] Transfer encrypted OCT to recipient ← amount + parties hidden [PRIVATE] Recipient decrypts, swaps OCT out ← exit via stealth addr or Umbra [PRIVATE] Funds arrive at fresh stealth address ← recipient fully unlinked ``` **Every hop is private.** No public link between sender and receiver on any chain. ### The Privacy Tech Stack (All Live, SDKs Ready) | Hop | Chain | Technology | Status | What it hides | | - | - | - | - | - | | **Deposit** | Ethereum | RAILGUN v3 shielded pool | Live (\$4.5B volume) | Sender identity (deposit goes through RAILGUN's anonymity set) | | **Deposit alt** | Ethereum | 0xbow Privacy Pools | Live (Vitalik-backed) | Sender + compliance proof (prove funds aren't sanctioned) | | **Transfer** | Octra | FHE (PVAC) | Live | Sender, receiver, amount (all encrypted) | | **Withdraw** | Ethereum | ERC-5564 stealth addresses (Umbra v2) | Live (77K+ addrs) | Recipient identity (one-time unlinkable address) | | **Withdraw** | Solana | Umbra Privacy SDK (Arcium MPC) | Live since March 2026 | Sender, recipient, amount (fully shielded) | | **Gas** | All | Relayer network | Planned | Funding pattern (gas deducted from balance, not paid separately) | ### How Each Technology Works > [accordion] RAILGUN (ETH Deposits) > > RAILGUN is a shielded pool on Ethereum with \$4.5B cumulative volume. Users "shield" tokens into the pool, breaking the on-chain link to their address. From inside the pool, they can transfer to any address privately. 0xio uses RAILGUN for deposits: user shields ETH → unshields to the bridge contract. The bridge deposit looks like a generic RAILGUN withdrawal, not traceable to the original user. > > SDK: `@railgun-community/wallet` (npm, TypeScript, browser + mobile compatible) > [accordion] 0xbow Privacy Pools (ETH Deposits with Compliance) > > Privacy Pools (backed by Vitalik, live since March 2025) add compliance to privacy. Users can prove their withdrawal belongs to a "clean" association set without revealing their full history. This lets regulated users use privacy features while proving they're not laundering funds. > > Useful for institutional users who need privacy + compliance simultaneously. > [accordion] ERC-5564 Stealth Addresses (ETH Withdrawals) > > Stealth addresses (ERC-5564 + ERC-6538) let the solver derive a one-time address for the recipient. Funds arrive at a fresh address that only the recipient can link to their identity. 77,000+ stealth addresses generated on Ethereum mainnet. Umbra v2 (launching summer 2026) is the primary implementation. > > The stealth address gas problem (fresh address has no ETH for gas) is solved by EIP-7702 delegation or RAILGUN relayers. > [accordion] Umbra Privacy SDK (Solana Withdrawals) > > Umbra on Solana (powered by Arcium MPC, public launch March 2026) provides full privacy: sender, recipient, AND amount are shielded. The TypeScript SDK handles all crypto internally. The solver deposits withdrawal funds into Umbra's shielded pool, transfers to the recipient, who withdraws whenever they want. > > SDK: Umbra Privacy TypeScript SDK (mainnet + devnet, browser + Node.js) > [accordion] Solana Confidential Transfers (Complementary) > > Solana's native Token-2022 extension hides transfer amounts and balances using homomorphic encryption + ZK proofs. Already used by PayPal's PYUSD. Addresses remain public, so use in combination with Umbra for full coverage. Useful as a fallback when Umbra is unavailable. ## Privacy Enhancement Layers Beyond the core tech stack, these mechanisms strengthen anonymity: ### 1. Amount Obfuscation (Partial Withdrawals) User deposits 1 ETH → receives encrypted OCT → withdraws as 0.3 ETH + 0.4 SOL + 0.25 USDC at different times to different addresses on different chains. Each withdrawal goes to a fresh stealth address. The FHE encrypted balance supports partial withdrawals naturally. ### 2. Batched Withdrawals The solver batches multiple users' withdrawals into single transactions. Instead of "Address B withdrew 1 ETH," it's "The solver sent 47 ETH to 30 addresses." Individual amounts are obscured in the batch. ### 3. Time Delay (Anonymity Set Growth) The longer users hold encrypted OCT, the larger the anonymity set. If 100 users deposited today and 100 withdrew tomorrow, no observer can link specific deposits to withdrawals. The encrypted OCT balance acts as a privacy buffer. ### 4. Relayer Network (No Gas Linkage) The relayer submits withdrawal transactions on behalf of the user. The gas cost is **deducted from the encrypted OCT balance itself**; the user never pays gas from the recipient address. This means the recipient address has zero prior on-chain activity, breaking the funding-pattern fingerprint. Example: User withdraws 200 OCT as ETH. Relayer fee is \~5 OCT. User receives 195 OCT worth of ETH. The relayer is compensated from the withdrawal amount, not from a separate gas payment. ### 5. Volume Privacy strength scales with usage. More users holding encrypted OCT on Octra → harder to correlate any single deposit to a withdrawal. Unlike mixer pools (Tornado Cash), there is no shared pool: each user has their own encrypted balance on their own Octra address. The anonymity comes from the volume of encrypted balances existing on-chain simultaneously. ## Why OCT as the Privacy Denomination Instead of creating octaWETH, octaWSOL, octaUSDC (separate wrapped tokens on Octra), all assets swap through OCT: | Approach | Pros | Cons | | - | - | - | | **Per-token wrapping** (octaWETH, etc.) | Same asset in/out, no price exposure | Fragmented anonymity sets, new bridge contracts per asset | | **OCT as denomination** | Single asset = more encrypted balances on-chain, uses existing DEX, simpler infrastructure | Price exposure during hold period, extra swap fees | We chose OCT because: * **Larger anonymity set**: all privacy users hold encrypted OCT on Octra (more encrypted balances = harder to correlate) * **Existing infrastructure**: DEX swap (live), encrypted OCT balances (live), private transfers (live) * **No new contracts**: no octaWETH mint/burn, no per-asset bridge extensions * **OCT utility**: every privacy transfer drives OCT demand The trade-off is price exposure: if OCT price drops while the user holds encrypted OCT, they lose value. For short hold times (minutes to hours), this is negligible. For longer holds, users accept the risk for privacy. ## Why FHE Instead of ZK or MPC | Approach | Examples | How it works | Limitation | | - | - | - | - | | **Stealth addresses** | Umbra (ETH) | One-time addresses via ECDH | Amounts visible | | **ZK proofs** | Railgun, Aztec | Prove facts without revealing data | Custom circuits per operation | | **MPC** | Umbra (SOL), Secret Network | Multi-party encrypted computation | Threshold trust assumptions | | **FHE** | **Octra / 0xio** | Compute directly on encrypted data | Performance overhead | Octra's FHE is uniquely powerful because: * **Encrypted arithmetic**: `ct_add` and `ct_sub` operate directly on ciphertexts * **No custom circuits**: Unlike ZK, no per-operation circuit design needed * **Private swaps possible**: AMM logic can run on encrypted order data (future) * **Any amount**: No fixed denominations like Tornado Cash, and ciphertexts support partial spends ## What Already Exists vs. What's Needed | Component | Status | Notes | | - | - | - | | ETH ↔ OCT swap | **Live** (devnet) | EthEscrowV2 + SwapVault v7 | | Encrypted OCT balances | **Live** (mainnet) | PVAC encrypt/decrypt | | Private OCT transfers | **Live** (mainnet) | ct_sub/ct_add on chain | | Cross-chain solver | **Live** (devnet) | Detects swaps, fills automatically | | TWAP oracle pricing | **Live** | 5-min time-weighted average | | SOL ↔ OCT swap | **Planned** | Needs Solana escrow + solver | | "Private Send" UI | **Needed** | One-click deposit → encrypt → transfer | | Relayer network | **Needed** | Gasless withdrawals for address unlinkability | | Batched withdrawals | **Needed** | Solver aggregates multiple withdrawals | | Compliance framework | **Needed** | Viewing key selective disclosure | ## The Product: "Private Send" A single feature in the 0xio wallet/extension: **1. User clicks 'Private Send'** Inputs: amount (ETH/SOL/USDC), recipient's Octra address **2. Auto-swap to OCT** DEX solver swaps the source token to OCT on Octra (\~30s) **3. Auto-encrypt** OCT balance encrypted via PVAC on Octra (instant) **4. Private transfer** Encrypted OCT sent to recipient on Octra (\~10s). Amount hidden from everyone. **5. Recipient holds encrypted OCT** Recipient sees encrypted balance in their 0xio wallet. Can hold as long as they want. **6. Recipient cashes out, all at once or in pieces** When ready, withdraw any portion to any address on any chain. Hold the rest. Steps 2-4 are automated by the solver. The user experience is: "Send 1 ETH privately" → done. ### Private Balance: Partial Withdrawals The recipient's encrypted OCT balance works like a private bank account. They can withdraw any amount at any time to any address on any chain: ``` ┌─────────────────────────────────────────────┐ │ Private Balance: ████████ OCT (encrypted) │ │ │ │ [Withdraw] │ │ Amount: 200 OCT │ │ To: 0xA1b2... on Ethereum │ │ Receive as: ETH │ │ Relayer fee: ~5 OCT │ │ You receive: ~195 OCT worth of ETH │ │ │ │ Remaining: ████████ OCT (encrypted) │ │ │ │ Recent withdrawals: │ │ • 200 OCT → 0xA1b2... (ETH), 2h ago │ │ • 150 OCT → 7xKp3... (SOL), yesterday │ │ • 300 OCT → 0xC4d5... (USDC), 3d ago │ └─────────────────────────────────────────────┘ ``` Each withdrawal is a separate DEX swap at a different time to a different address. This is the **core privacy mechanism**. An observer sees unrelated transactions on different chains with different amounts and timing. The link between them is hidden inside Octra's FHE layer. > [!TIP] > > **Maximum privacy**: Withdraw in small, irregular amounts over days/weeks to fresh addresses on different chains. The encrypted balance on Octra acts as a privacy buffer: the longer you hold and the more you split withdrawals, the harder correlation becomes. ## Development Phases ### Phase 1: Basic Privacy (Octra-only) * "Private Send" UI in 0xio wallet/extension * Auto-chain: DEX swap → encrypt → transfer * Partial withdrawals to any chain/address * Relayer fee deducted from encrypted balance * Uses existing DEX + PVAC infrastructure, with **no new integrations** ### Phase 2: ETH-Side Privacy * Integrate **RAILGUN SDK** (`@railgun-community/wallet`) for shielded deposits * User shields ETH → unshields to bridge → breaks sender link * Integrate **ERC-5564 stealth addresses** for withdrawals via Umbra v2 * Solver sends to one-time stealth address (recipient unlinked) * EIP-7702 delegation to solve stealth address gas problem ### Phase 3: SOL-Side Privacy * Integrate **Umbra Privacy TypeScript SDK** for Solana withdrawals * Solver deposits into Umbra shielded pool → recipient withdraws privately * Sender, recipient, amount all hidden on Solana side * Complement with Solana Confidential Transfers (Token-2022) for amount hiding ### Phase 4: Full Stack * **0xbow Privacy Pools** integration for compliance-friendly deposits * Relayer network decentralization (multiple relayers compete) * Batched withdrawal aggregation in solver * Anonymity set monitoring dashboard * Selective disclosure via PVAC viewing keys * Noir ZK circuits for custom compliance proofs * Privacy audit by external security firm ## Comparison with Competitors | Feature | Tornado Cash | Railgun | Umbra (ETH) | Umbra (SOL) | **0xio Basic** | **0xio Advanced** | | - | - | - | - | - | - | - | | Privacy model | Mixer pool | ZK shielded pool | Stealth addresses | MPC + ZK | FHE only | **FHE + RAILGUN + Umbra + stealth** | | Amount privacy | Fixed denominations | Yes | No | Yes | Yes (Octra only) | **Yes (all hops)** | | Cross-chain | No | No (per-chain) | EVM only | Solana only | Any chain with DEX | **Any chain with DEX** | | End-to-end private | Yes (same chain) | Yes (same chain) | Partially | Yes | Octra hop only | **Yes (all chains)** | | Private swaps | No | Yes (v3) | No | No | Planned | **Planned (FHE AMM)** | | Compliance | None (sanctioned) | Relayer whitelist | None | Auditor program | Viewing keys | **Viewing keys + Privacy Pools** | | Status | Sanctioned | Live | Live | Live | Phase 1 | **Phase 2-4** | ## Performance Estimates | Step | Latency | Notes | | - | - | - | | DEX swap (ETH→OCT) | \~30s | Solver fills on Octra | | Encrypt OCT | \<1s | Client-side PVAC, cached | | Private transfer | \~10s | FHE ciphertext processing | | Decrypt OCT | \<30s | Client-side with cached context | | DEX swap (OCT→ETH) | \~30s | Solver fills on Sepolia | | **Total (no hold)** | **\~90s** | End-to-end with minimal hold | | **Recommended hold** | **Hours to days** | For meaningful anonymity set | ## FAQ > [accordion] Is this like Tornado Cash? > > Similar concept (privacy via intermediary), different implementation. Tornado Cash used fixed-denomination mixer pools on a single chain with no compliance features, and was sanctioned. 0xio uses FHE for any-amount transfers across multiple chains, with viewing key disclosure for compliance. The privacy model is also different: Tornado hid deposits in a pool, while 0xio uses homomorphic encryption on the Octra chain. > [accordion] Can someone trace my deposit to my withdrawal? > > The Octra transfer itself is fully private (FHE encrypted). However, the deposit on ETH and withdrawal on ETH/SOL are public. If you deposit 1 ETH and immediately withdraw 1 ETH worth of OCT, timing and amount correlation could link them. For better privacy: hold encrypted OCT for longer, withdraw in different amounts, use multiple withdrawal addresses. > [accordion] Why not full end-to-end encryption like Umbra on Solana? > > Umbra on Solana built a full MPC privacy infrastructure on a single chain. We're cross-chain: we don't control the source/destination chains (Ethereum, Solana). Their deposits and withdrawals are inherently public. Our FHE privacy lives on Octra as a middle layer. Full end-to-end would require privacy infrastructure on every supported chain, which defeats the purpose of using Octra as a unified backend. > [accordion] What about price risk while holding OCT? > > Users are exposed to OCT price movement while holding encrypted OCT. For short holds (minutes), this is negligible. For longer holds (days), users accept the trade-off between privacy and price stability. Future iterations may support encrypted stablecoin-denominated balances on Octra to eliminate this risk. > [accordion] Is this legal? > > Privacy is legal. Money laundering is not. The 0xio privacy layer includes selective disclosure (viewing keys shared with auditors) and relayer compliance, following models established by Railgun and Umbra. Users can prove transaction history to authorized parties without making it public. Unlike Tornado Cash, which had no compliance mechanism, the design prioritizes regulatory compatibility. # Build with AI Source: https://docs.0xio.xyz/developers/build-with-ai Connect Claude Code, Cursor or VS Code to the 0xio docs, give it the SDK rules and start from a working dapp. ## Docs MCP server These docs are an MCP server at `https://docs.0xio.xyz/mcp` (streamable HTTP). Your assistant can search them and read any page while it writes code. ```bash Claude Code theme={null} claude mcp add --transport http 0xio-docs https://docs.0xio.xyz/mcp ``` ```json title="Cursor (.cursor/mcp.json)" theme={null} { "mcpServers": { "0xio-docs": { "url": "https://docs.0xio.xyz/mcp" } } } ``` ```json title="VS Code (.vscode/mcp.json)" theme={null} { "servers": { "0xio-docs": { "type": "http", "url": "https://docs.0xio.xyz/mcp" } } } ``` * **Claude Code:** add `--scope project` to share it through the repo's `.mcp.json`, or `--scope user` for every project. * **Cursor:** put the same file at `~/.cursor/mcp.json` for every project. ## Plain text docs For assistants without MCP: * [llms.txt](https://docs.0xio.xyz/llms.txt): an index of every page * [llms-full.txt](https://docs.0xio.xyz/llms-full.txt): every page in one file * Any page as Markdown: add `.md` to its URL, for example [request-lifecycle.md](https://docs.0xio.xyz/developers/request-lifecycle.md) ## SDK rules `@0xio/sdk` 2.12.1 and newer ships `AGENTS.md`: the rules an assistant follows when it writes code against the SDK (amounts, request ids, never resending, watch-only, network scoping, private transfers, errors). After `npm install @0xio/sdk` it is at `node_modules/@0xio/sdk/AGENTS.md`. Claude Code, Cursor and VS Code read an `AGENTS.md` at the project root. Point yours to the SDK's: ```markdown AGENTS.md theme={null} Before writing wallet code, read node_modules/@0xio/sdk/AGENTS.md. ``` In Claude Code, a line `@node_modules/@0xio/sdk/AGENTS.md` imports the whole file. ## Starter ```bash theme={null} npm create @0xio/dapp my-dapp ``` A Vite, React and TypeScript dapp that detects the wallet, connects, shows the address and balance per network, sends OCT with a request id and a confirmed wait, follows a send the SDK lost track of instead of sending again, and handles watch-only wallets. It comes with an `AGENTS.md` that loads the SDK rules and the docs MCP server set up for all three editors. ## First prompt Paste this into your assistant from the project folder: ```text theme={null} Read node_modules/@0xio/sdk/AGENTS.md and follow it. Use the 0xio-docs MCP server for anything it does not cover. Add a page that lets a connected user call the deposit method on contract
with an OCT amount, waits for confirmation, shows the request status and never resends. ``` # Names Source: https://docs.0xio.xyz/developers/names Show a .oct name for an address and resolve a name to an address with @0xio/sdk 2.12.2. Since SDK 2.12.2 a dapp can read the Octra Name System with two calls. `lookupAddress` gives the name to show for an address, such as `0xio.oct`. `resolveName` gives what a name points to. Both are reads: no review, no signature, no fee. The Octra Name System is live on devnet. On a network without it, both calls resolve with `null`, so a dapp written today keeps working when a network gains names. ## Before you start Ask for `contract_views` when you connect. The wallet refuses the reads without it. ```typescript theme={null} import { ZeroXIOWallet } from '@0xio/sdk'; const wallet = new ZeroXIOWallet({ appName: 'My DApp' }); await wallet.initialize(); const { address } = await wallet.connect({ permissions: ['accounts', 'contract_views'], }); ``` If your page sets a Content Security Policy, add `https://raw.githubusercontent.com` to `connect-src`. The SDK reads the name contract's address from there (see [Where the contract comes from](#where-the-contract-comes-from)). ## Show a name on the connected button `lookupAddress()` resolves with the primary name of the connected address, or `null` when it has none. ```typescript theme={null} const short = (a: string) => `${a.slice(0, 6)}...${a.slice(-4)}`; async function label(): Promise { const { address } = wallet.getConnectionInfo(); if (!address) return 'Connect'; const name = await wallet.lookupAddress().catch(() => null); return name ?? short(address); } button.textContent = await label(); wallet.on('accountChanged', async () => (button.textContent = await label())); wallet.on('networkChanged', async () => (button.textContent = await label())); ``` A dapp can show the name or the address, whichever it prefers. If it shows the name, keep the address reachable in a tooltip or a second line. | Call | Resolves with | | - | - | | `lookupAddress()` | the connected address's primary name, or `null` | | `lookupAddress(address)` | that address's primary name, or `null` | | `lookupAddress(address, networkId)` | the same on a named network, without a connection | A primary name is set by its owner. The name service only reports one that is active, is held by that address and points back to it, so no second check is needed. An address whose owner pointed a name at it but did not make it primary has no name to show. ## Resolve a name to an address `resolveName(name)` takes the name with or without `.oct`. ```typescript theme={null} const hit = await wallet.resolveName('0xio.oct'); if (hit === null) { // this network has no name service } else if (hit.kind === 'wallet') { await wallet.sendTransaction({ to: hit.wallet, amountOct: '1' }); } else if (hit.kind === 'circle') { // the name points to a Circle: hit.circle is its id } else { // the name points to nothing: not registered, expired, or no target set } ``` | `kind` | Meaning | Set field | | - | - | - | | `wallet` | the name points to a wallet | `wallet` (the address) | | `circle` | the name points to a Circle whose link is confirmed | `circle` (the Circle id) | | `none` | nothing to send to or open | | Text that is not a valid name rejects with `INVALID_PARAMS`. A reply the SDK cannot read rejects with `NETWORK_ERROR`; it is never guessed. ## Rules for sending to a name A name decides where money goes, so treat it as a label for an address and nothing more. * Send to the address. Pass `hit.wallet` to `sendTransaction`, never the name. * Show the address to the user before they confirm. The wallet's review shows it again. * Resolve again right before sending. If the address differs from the one the user saw, stop and show the new one. * Do not keep a resolution longer than a minute, or a looked up name longer than ten minutes. A name can be transferred or pointed elsewhere at any time. * A name is not proof of who is connected. Use `signMessage` for that. ## Name rules `normalizeOnsName(text)` returns the bare name or `null`. `isOnsName(text)` is true only for text that ends in `.oct` and passes the rules, which is the test to use on a recipient field. * lower case letters `a` to `z`, digits and hyphens * 1 to 32 characters * no hyphen at the start or the end ```typescript theme={null} import { isOnsName, normalizeOnsName } from '@0xio/sdk'; isOnsName('0xio.oct'); // true isOnsName('0xio'); // false: no ending normalizeOnsName('0XIO.oct'); // '0xio' normalizeOnsName('a_b.oct'); // null ``` ## Where the contract comes from The name contract's address is not built into the SDK. It is read per network from the name list in the [token lists repository](https://github.com/0xio-xyz/Token-lists) (`ONS_LIST_URL`), kept for ten minutes, and the last list read is used when the list cannot be reached. Before the first read, the SDK asks the contract what it is. It must answer as the Octra Name System with an interface version this SDK supports. A contract that does not is treated as no name service, so `lookupAddress` and `resolveName` resolve with `null`. `onsContract(networkId)` resolves with the listed address or `null`. Never pass a name contract in from a page, a link or a QR code. ## Reference | Export | What it is | | - | - | | `wallet.lookupAddress(address?, networkId?)` | primary name of an address, or `null` | | `wallet.resolveName(name, networkId?)` | `{ name, kind, wallet, circle }`, or `null` | | `onsContract(networkId)` | the listed name contract (async), or `null` | | `normalizeOnsName(text)` | bare name, or `null` | | `isOnsName(text)` | `true` for valid text ending in `.oct` | | `ONS_SUFFIX` | `'.oct'` | | `ONS_LIST_URL` | address of the name list | | `ResolvedName` | type of a `resolveName` result | # Oracle API Source: https://docs.0xio.xyz/developers/oracle-api REST API reference for the 0xio Oracle: OCT and token prices, swap quotes, price history, EVM address data and bridge analytics. Reads need no API key. The **0xio Oracle** serves OCT and token prices, swap quotes, price history, EVM address data for the 0xio wallets, and bridge analytics. The OCT price follows the wOCT/ETH Uniswap v4 pool on Ethereum live, priced in USD from the ETH/USD trades of Coinbase, Kraken, Bitstamp, Binance and Bybit (stablecoin quotes converted at live rates), updated within 250 ms of a trade, and is cross-checked against CoinGecko and DexScreener. Price history is stored in PostgreSQL for charts. ## Base URL ``` https://oracle.0xio.xyz ``` ## Authentication **Reads need no API key.** Every `GET` route works without a key, limited per client IP. A valid key skips the IP limit and counts against its own monthly quota instead (10,000 calls a month unless your plan sets another). A retired or unknown key is treated as no key, so it falls back to the IP limit rather than failing. Get a key from the [Developer Portal](https://devs.0xio.xyz) when you need more than the IP limit allows, and send it in the `x-api-key` header: ```http theme={null} x-api-key: 0xio-<16 hex>-oracle ``` > [!WARNING] > > Keep your API key secret. Use it from your own server; never ship it in client-side code or a public repository. ### Route Access | Endpoint | Access | | - | - | | `GET /health`, `GET /api/health`, `GET /api/metrics` | Public | | `GET /api/price/:symbol`, `GET /api/prices`, `GET /api/price/oct/details`, `GET /api/price/twap` | No key (per-IP limit) or a key | | `GET /api/quote`, `GET /api/reserves`, `GET /api/chart/history` | No key (per-IP limit) or a key | | `GET /api/evm/history`, `GET /api/evm/nfts`, `GET /api/evm/tokens` | No key (per-IP limit) or a key | | `GET /api/bridge/stats`, `GET /api/bridge/events`, `GET /api/bridge/chart` | No key (per-IP limit) or a key | | `POST /api/bridge/event` | Public, rate limited (see [Bridge Analytics](#bridge-analytics)) | | `POST /api/keys`, `GET /api/keys`, `DELETE /api/keys/:id`, `GET /api/usage` | Owner wallet signature | | `DELETE /api/keys` | The key being revoked | | `POST /api/simulate-trade`, `POST /api/liquidity` | Operator key only | | `POST /api/keys/revoke-owner`, `GET /api/keys/usage`, `PATCH /api/keys/quota` | Operator key only | Any other write without a valid key gets `401`. Quotas are per key, per calendar month (UTC), and reset on the 1st. "Operator key" routes are for the operator's own services: any other key gets `403`. ### Scopes A key can be limited to scopes when it is created. Each route group needs its scope: | Scope | Routes | | - | - | | `read:price` | `/api/price/...`, `/api/prices` | | `read:quote` | `/api/quote` | | `read:chart` | `/api/chart/history` | | `read:reserves` | `/api/reserves` | | `read:bridge` | `/api/bridge/...` | | `read:evm` | `/api/evm/...` | An empty or missing scope list means all scopes. *** ## Health Check ### Full Health Check ```http theme={null} GET /health ``` Returns detailed service status including database and cache connectivity. **Response:** ```json theme={null} { "status": "healthy", "timestamp": "2025-01-26T12:00:00+00:00", "version": "2.0.0", "services": { "database": "connected", "redis": "connected" }, "environment": "production" } ``` ### Simple Liveness Probe ```http theme={null} GET /api/health ``` Returns a simple response for load balancer health checks. **Response:** ```json theme={null} { "status": "ok", "timestamp": 1706270400000 } ``` *** ## Prices ### Get Token Price ```http theme={null} GET /api/price/:symbol ``` Get the current spot price for a specific token. **Parameters:** | Parameter | Type | Description | | - | - | - | | `symbol` | path | Token symbol (e.g., `OCT`, `USDC`, `SOL`, `ETH`) | **Example:** ```bash theme={null} curl https://oracle.0xio.xyz/api/price/OCT ``` **Response:** ```json theme={null} { "symbol": "OCT", "price": 0.024816, "source": "aggregated(uniswap+coingecko+dexscreener)" } ``` The `source` field indicates where the price came from: * `aggregated(...)`: OCT and wOCT. The names in brackets are the sources that agreed: `uniswap` (the live pool price) leads while it agrees with the polled `coingecko` and `dexscreener` prices; a source too far from the others is left out * `spot`: ETH (from the live ETH/USD feed) and SOL (Coinbase, then Kraken) * `static`: USDC and USDT, pegged at \$1 ### Get All Prices ```http theme={null} GET /api/prices ``` Get current prices for all supported tokens (OCT, WOCT, USDC, USDT, SOL, ETH). **Example:** ```bash theme={null} curl https://oracle.0xio.xyz/api/prices ``` **Response:** ```json theme={null} { "OCT": 0.024816, "WOCT": 0.024816, "USDC": 1.0, "USDT": 1.0, "SOL": 180.50, "ETH": 3200.00 } ``` ### Get OCT Detailed Market Data ```http theme={null} GET /api/price/oct/details ``` Get full aggregated market data for OCT including 24h change, volume, liquidity, and source breakdown. **Example:** ```bash theme={null} curl https://oracle.0xio.xyz/api/price/oct/details ``` **Response:** ```json theme={null} { "symbol": "OCT", "priceUsd": 0.024816, "priceChange24h": -2.15, "volume24h": 12500.00, "liquidityUsd": 85000.00, "source": "aggregated(uniswap+coingecko+dexscreener)", "confidence": "high", "spreadPct": 0.8, "allSources": { "uniswap": 0.024816, "coingecko": 0.024800, "dexscreener": 0.024832 }, "ethUsd": 1780.50, "timestamp": 1745312400000 } ``` ### Get TWAP Prices ```http theme={null} GET /api/price/twap ``` Get 5-minute time-weighted average prices for OCT and ETH alongside the current spot, plus the OCT spot-vs-TWAP deviation. **Example:** ```bash theme={null} curl https://oracle.0xio.xyz/api/price/twap ``` **Response:** ```json theme={null} { "oct": { "twap_5m": 0.024790, "spot": 0.024816, "deviation_pct": 0.10 }, "eth": { "twap_5m": 1779.20, "spot": 1780.50 }, "window_seconds": 300, "timestamp": 1745312400000 } ``` *** ## Trading ### Get Swap Quote ```http theme={null} GET /api/quote ``` Get a swap quote with expected output amount and price impact. **Query Parameters:** | Parameter | Type | Description | | - | - | - | | `inputMint` | string | Input token symbol | | `outputMint` | string | Output token symbol | | `amount` | string | Input amount (as string) | **Example:** ```bash theme={null} curl "https://oracle.0xio.xyz/api/quote?inputMint=OCT&outputMint=USDC&amount=100" ``` **Response:** ```json theme={null} { "inputToken": "OCT", "outputToken": "USDC", "amountIn": 100, "amountOut": 124.75, "feeAmount": 0.25, "priceImpact": 0.0005, "spotPrice": 1.25, "executionPrice": 1.2475, "route": "Solver-AMM-v1" } ``` ### Get AMM Reserves ```http theme={null} GET /api/reserves ``` Get current liquidity pool reserves. **Example:** ```bash theme={null} curl https://oracle.0xio.xyz/api/reserves ``` **Response:** ```json theme={null} { "reserves": { "OCT": 1000000, "USDC": 1250000, "USDT": 500000 }, "timestamp": 1706270400000 } ``` ### Simulate Trade (Operator) ```http theme={null} POST /api/simulate-trade ``` Used by the solver to reserve liquidity or update local state before on-chain execution. Requires the operator key; any other key gets `403`. **Request Body:** ```json theme={null} { "inputToken": "OCT", "amountIn": 1000, "outputToken": "USDC", "amountOut": 1245.50 } ``` **Response:** ```json theme={null} { "success": true, "newReserves": { "OCT": 1001000, "USDC": 1248754.50 } } ``` ### Update Liquidity (Operator) ```http theme={null} POST /api/liquidity ``` Adjust liquidity reserves, for example after a rebalancing. Requires the operator key; any other key gets `403`. **Request Body:** ```json theme={null} { "reserves": { "OCT": 1000000, "USDC": 1250000 } } ``` **Response:** ```json theme={null} { "success": true, "message": "Liquidity Updated", "currentReserves": { "OCT": 1000000, "USDC": 1250000 }, "newPriceOctUsdt": 1.25 } ``` *** ## Chart Data ### Get Price History ```http theme={null} GET /api/chart/history ``` Get historical price data over a configurable time range. Points are downsampled to fit the range (e.g. 30d → \~1 point per 2h). **Query Parameters:** | Parameter | Type | Default | Description | | - | - | - | - | | `pair` | string | `OCT` | Token symbol | | `range` | string | `1d` | Time window: `1h`, `1d`, `7d` (`1w`), `30d` (`1m`), `1y` (`365d`) | **Example:** ```bash theme={null} curl "https://oracle.0xio.xyz/api/chart/history?pair=OCT&range=30d" ``` **Response:** ```json theme={null} [ { "value": 1.24, "time": 1706270400 }, { "value": 1.25, "time": 1706274000 }, { "value": 1.26, "time": 1706277600 } ] ``` > [!NOTE] > > The `time` field on chart data points is a Unix timestamp in **seconds** (suitable for charting libraries like Lightweight Charts). Other endpoints (`/health`, `/api/price/oct/details`, `/api/reserves`) return timestamps in **milliseconds**. *** ## EVM Data The 0xio wallets read EVM address data through these routes. They are read from Blockscout with the oracle's own key, server side. Each call covers one chain, so a client can query chains in parallel and keep per-chain failures apart. ### EVM History ```http theme={null} GET /api/evm/history?chain=1&address=0x... ``` Latest transactions and ERC-20 transfers of an address. **Query Parameters:** | Parameter | Type | Description | | - | - | - | | `chain` | integer | `1` (Ethereum), `11155111` (Sepolia), `4663` (Robinhood Chain) or `46630` (Robinhood Chain Testnet) | | `address` | string | `0x` and 40 hex characters | **Response:** `{ transactions, token_transfers }`, Blockscout's v2 items trimmed to the fields wallets read, under Blockscout's own names. Every other field is dropped. Cached 60 seconds per chain and address. * **`transactions` items:** `hash`, `timestamp`, `value`, `status` and `method`; `from`, `to` and `created_contract` as `{ hash }` or null; `fee` as `{ value }` (wei) or null; `raw_input`; `decoded_input` as `{ method_call, method_id, parameters: [{ name, type, value }] }` or null. Contract names are not passed on; wallets read them per address with the explorer's verified and scam flags. * **`token_transfers` items:** `transaction_hash`, `timestamp` and `log_index`; `from` and `to` as `{ hash }` or null; `total` as `{ value, decimals }`; `token` as `{ address_hash }`. Token names, symbols and logos are left out, so a wallet names a token only from its own list. * **Caps:** a scalar string is cut at 128 characters and a fee at 80. `raw_input` is kept whole up to 522 characters (a selector and eight words) and cut to its 10 character selector beyond that, and dropped unless it is `0x` and hex. `parameters` keeps the first 16. A parameter `name` or `type` is cut at 64 characters, `method_call` and a parameter `value` at 512 (a nested array or object longer than that becomes its JSON text, cut), and `method_id` at 10. A cut string ends with `…`. ### EVM NFTs ```http theme={null} GET /api/evm/nfts?chain=1&address=0x... ``` ERC-721 and ERC-1155 tokens held by an address. **Query Parameters:** `chain` (a positive integer chain ID) and `address` (`0x` and 40 hex characters). **Response:** ```json theme={null} { "items": [ { "contract": "0x...", "tokenId": "42", "standard": "ERC-721", "amount": "1", "name": "Piece #42", "collection": "Collection", "image": "https://..." } ], "complete": true } ``` * `contract` is lowercase. `tokenId` and `amount` are decimal strings of at most 78 digits. `standard` is `ERC-721` or `ERC-1155`. * `name` (token metadata) and `collection` (token name) are trimmed, stripped of control and bidi characters and capped at 80 characters, or null. * `image` is an `https://` or `ipfs://` URL of at most 1024 characters, or null. * Tokens Blockscout flags as scam or spam are dropped. * **Paging:** follows Blockscout's pages up to 200 items (5 pages, 20 seconds in total). `complete` is false when more remain or a later page failed. Cached 60 seconds per chain and address (10 seconds for a result cut short by a failed page). * **Errors:** `400` on a bad chain or address, or a chain Blockscout does not serve; `503` when Blockscout fails. ### EVM Tokens ```http theme={null} GET /api/evm/tokens?chain=1&address=0x... ``` ERC-20 tokens held by an address, each with a spam verdict. **Query Parameters:** `chain` (a positive integer chain ID) and `address` (`0x` and 40 hex characters). **Response:** `{ items: [{ contract, symbol, name, decimals, balance, price, verified, spam, spamReason, logo }], complete }`, in Blockscout's order (highest USD value first). * `contract` is lowercase. `decimals` is an integer from 0 to 36 (other items are dropped). `balance` is a decimal string of at most 78 digits, and zero balances are skipped. * `symbol` (at most 11 characters) and `name` (at most 40) are trimmed and stripped of control, bidi and zero-width characters, or null when empty or longer. * `price` is Blockscout's USD exchange rate when finite and above 0, else null. * `logo` is kept only as an `https://` URL of at most 512 printable ASCII characters on `raw.githubusercontent.com`, `assets.coingecko.com` or `coin-images.coingecko.com`; null otherwise, and always null on a spam item. Never anything the token contract itself supplies. * `verified` says whether the contract source is verified. It is looked up only where it decides the verdict (unpriced tokens no other rule caught), else null. * `spam` is true when any rule below holds; `spamReason` is the first that matched, else null: 1. `flagged`: Blockscout marks it as scam or spam. 2. `link`: the name or symbol holds `http`, `www.` or a domain (a dot after a letter or digit, then 2 to 10 letters), such as `t.me` or `x.com`. 3. `bait`: the name or symbol holds claim, reward(s), airdrop, visit, free, voucher, bonus, gift, redeem or eligible as a word (camelCase split; airdrop, voucher and eligible anywhere). 4. `impersonation`: the symbol, with look-alike Cyrillic, Greek and fullwidth letters mapped to Latin and case and spaces ignored, is ETH, WETH, USDC, USDT, DAI or WBTC on a chain where that token is listed (1, 4663, 8453, 42161, 10, 137) at a contract that is not the canonical one. OCT and WOCT apply on every chain; the only canonical one is wOCT on Ethereum (`0x4647e1fe715c9e23959022c2416c71867f5a6e80`). 5. `unverified_unpriced`: the contract is not verified and has no price. Rules 2 and 3 also see look-alike letters as Latin, and apply only to tokens with no market price, so a real token named after its site is not flagged. * **Paging:** follows Blockscout's pages up to 200 items (5 pages, 20 seconds in total). `complete` is false when more remain or a later page failed. Cached 60 seconds per chain and address, 10 seconds when a page failed or verification lookups were left for the next call. * **Errors:** `400` on a bad chain or address, or a chain Blockscout does not serve; `503` when Blockscout fails or its plan does not cover the chain. *** ## Bridge Analytics The [bridge site](https://bridge.0xio.xyz) and the 0xio Extension report each step of a transfer to `POST /api/bridge/event`. The read routes count a row only after the oracle has found that transfer on chain. ### Report a Bridge Event ```http theme={null} POST /api/bridge/event ``` Public, no key. The body is JSON of at most 4 KB, and unknown fields are refused. ```json theme={null} { "event_type": "lock", "direction": "o2e", "octra_addr": "oct...", "eth_addr": "0x...", "amount_raw": "1000000", "amount_display": "1", "tx_hash": "...", "wallet_type": "0xio", "source": "web" } ``` | Field | Rule | | - | - | | `event_type` and `direction` | `lock` with `o2e` (to Ethereum; `tx_hash` is the Octra hash: 64 hex, no prefix), `claim` with `o2e`, `burn` with `e2o` (to Octra) and `any_swap` with `a2o` (`tx_hash` is the Relay request ID). The last three take `0x` and 64 hex. Any other type, or a direction that does not match the type, is refused | | `octra_addr` | `oct` and 44 Base58 characters (43 accepted). Needed for `lock`, `burn` and `any_swap` | | `eth_addr` | `0x` and 40 hex, not the zero address. Needed for every type (`claim`: the recipient) | | `amount_raw` | Whole micro OCT, digits only, above 0 and at most 1 billion OCT | | `amount_display` | Optional. The stored display is always derived from `amount_raw` | | `wallet_type` | Null or one of `0xio`, `metamask`, `rabby`, `walletconnect`, `coinbase`, `rainbow`, `trust`, `phantom`, `okx`, `brave`, `injected` | | `source` | Optional. `web` (the bridge site) or `extension` (the 0xio Extension); absent means `web`. Self-reported and not checked on chain; the first report of a transfer keeps its source | **Response:** `{ "status": "ok" }`, or `{ "status": "duplicate" }` when the row is already known. `400` with a short `error` on any rule above; `429` past the rate limit. * **Limits:** 20 posts a minute per client IP and 300 a minute in total. * **Dedupe:** an event whose `(event_type, tx_hash)` already has a verified row, or a pending row with the same addresses and amount, is not stored again. At most 3 rows with different payloads may wait under one hash, and only one verified row is allowed per `(event_type, tx_hash)`, so a fake posted first with a real hash cannot block or double the real event. ### Verification Every row starts unverified and is left out of every read. A background task checks rows newest first: * **lock:** the Octra contract receipt shows a successful `lock_to_eth` on the bridge vault whose `Locked` event has `octra_addr` as sender, the same amount and `eth_addr` as recipient. * **claim:** the Ethereum receipt succeeded, was sent to the bridge contract, is 12 blocks deep and holds a `Minted` event for the amount whose recipient (or the transaction's sender) is `eth_addr`. * **burn:** the same receipt checks and a `BurnToOctra` event for the amount from `eth_addr` to `octra_addr`. * **any_swap:** Relay reports the request as settled, and one of its Ethereum transactions sends wOCT to `octra_addr` through the bridge for the amount (within 1%). * A row not on chain yet is checked again after 1, 2, 4 minutes and so on, at most 12 hours apart. * Rows are never deleted. A row that breaks the rules, is contradicted by the chain twice, is under 7 days old and still not found after 8 checks, or whose hash another verified row already holds, is quarantined and read by no route. A row older than 7 days that is not found after 3 checks stays unverified instead. ### Bridge Stats ```http theme={null} GET /api/bridge/stats GET /api/bridge/events?limit=50&offset=0 GET /api/bridge/chart ``` * **`/api/bridge/stats`:** totals such as `total_events`, `total_locks`, `total_burns`, `locks_24h`, `burns_24h`, `volume_locked_oct`, `volume_locked_24h_oct`, `volume_burned_oct`, `volume_burned_24h_oct`, `unique_octra_wallets` and `unique_eth_wallets`, plus `by_source` with the same totals for `web` (the bridge site) and `extension` (the 0xio Extension). Both keys are always present, and wallet counts are per source, so the two can overlap. The top-level `source` field is a fixed `"bridge_frontend_only"`, kept for older readers: it only says these are client-reported transfers rather than full on-chain totals, and does not name a client; use `by_source` for the split. `note` describes what is counted. Cached 60 seconds. * **`/api/bridge/events`:** `{ events, count }`, newest first. `limit` defaults to 50 (at most 500). * **`/api/bridge/chart`:** `{ data }`, daily rows with `ts`, `direction`, `event_type`, `amount_oct` and `source`. Cached 120 seconds. All three read rows found on chain, plus rows recorded before validation existed that were not quarantined. Volumes are in OCT, from `amount_raw`. *** ## Metrics ```http theme={null} GET /api/metrics ``` Public. How live the OCT price is, for status displays, without price values: price updates in total and in the last minute, ETH/USD ticks, the venues feeding ETH/USD, whether the pool's swaps arrive live, the sources in use, the confidence and how long ago the price last changed. *** ## API Key Management ### List Your Keys ```http theme={null} GET /api/keys ``` List all API keys associated with your wallet address. **Requires wallet signature for privacy.** **Query Parameters:** | Parameter | Type | Required | Description | | - | - | - | - | | `owner` | string | Yes | Your wallet address | | `timestamp` | number | Yes | Unix timestamp | | `signature` | string | Yes | Base64-encoded signature of `0xio-list-keys:{owner}:{timestamp}` | | `public_key` | string | Yes | Base64-encoded Ed25519 public key | **Example:** ```bash theme={null} curl "https://oracle.0xio.xyz/api/keys?owner=oct...×tamp=170...&signature=...&public_key=..." ``` **Response:** ```json theme={null} [ { "id": 42, "key_masked": "0xio-<12 hex>...", "label": "Production App", "created_at": "2025-01-15 10:30:00", "monthly_quota": 10000, "usage": 1834, "scopes": ["read:price", "read:quote"] } ] ``` ### Generate New Key ```http theme={null} POST /api/keys ``` Generate a new API key. Requires a wallet signature, and the owner must be on the portal whitelist. Keys look like `0xio-<16 hex>-oracle`. An owner can create at most 5 keys an hour (`429` past that). **Request Body:** ```json theme={null} { "owner": "oct...", "public_key": "base64_encoded_public_key", "label": "My App Production", "scopes": ["read:price", "read:quote"], "signature": "base64_encoded_signature", "timestamp": 1706270400 } ``` | Field | Type | Required | Description | | - | - | - | - | | `owner` | string | Yes | Your wallet address (`oct...`) | | `public_key` | string | Yes | The Ed25519 public key, Base64 | | `label` | string | No | Descriptive label, at most 64 printable characters | | `scopes` | string\[] | No | Limit the key to these [scopes](#scopes); empty or missing means all | | `signature` | string | Yes | Base64-encoded ed25519 signature | | `timestamp` | number | Yes | Unix timestamp (must be within 5 minutes) | **Signature Message Format:** ``` 0xio-api-key:{owner}:{timestamp} ``` Example: `0xio-api-key:oct...:1706270400` **Response:** ```json theme={null} { "status": "created", "id": 42, "key": "0xio-<16 hex>-oracle" } ``` > [!WARNING] > > Store your API key securely! It will only be shown once. > [!TIP] > > Use the [Developer Portal](https://devs.0xio.xyz) to create keys easily - it handles wallet signing automatically. ### Revoke Key by ID ```http theme={null} DELETE /api/keys/:id?owner=...&public_key=...&signature=...×tamp=... ``` Revoke one of your keys by the `id` from create or list. Signed by the owner wallet, so a lost or over-quota key can still be revoked. **Signature Message Format:** ``` 0xio-revoke:{owner}:{id}:{timestamp} ``` **Response:** ```json theme={null} { "status": "revoked", "id": 42 } ``` Returns `404` when the id does not exist or belongs to another wallet, `401` on a bad signature and `400` on an expired or malformed one. ### Revoke Key with the Key Itself ```http theme={null} DELETE /api/keys ``` Legacy revoke with the full key in the header. Kept for compatibility; prefer `DELETE /api/keys/:id`, which also works for a key that is over its quota. **Headers:** ```http theme={null} x-api-key: KEY_TO_REVOKE ``` **Response:** ```json theme={null} { "status": "deleted" } ``` ### Usage Report ```http theme={null} GET /api/usage?owner=...&public_key=...&signature=...×tamp=...&days=30&logs=20 ``` Your keys' usage over the last `days` (1 to 365, default 30) with the latest `logs` requests (1 to 500, default 20). Signed by the owner wallet over `0xio-usage:{owner}:{timestamp}`. The key routes (`POST /api/keys`, `GET /api/keys`, `DELETE /api/keys/:id`) and `GET /api/usage` allow 60 calls a minute per client IP. *** ## Error Responses | HTTP Status | Description | | - | - | | 400 | Bad Request: invalid parameters or signature payload | | 401 | Unauthorized: a write without a valid key, or an invalid signature | | 403 | Forbidden: address not whitelisted for key creation, a scope the key lacks, or an operator-only route | | 404 | Not Found: token or resource not found, or the price is still initializing | | 429 | Too Many Requests: the per-IP limit for calls without a key, a key over its monthly quota, or a route's own rate limit | | 500 | Internal Server Error | | 502 | Bad Gateway: upstream price source error | | 503 | Service Unavailable: oracle data not yet initialized | Error responses from the routes use the shape `{ "error": "message" }`; a route's own rate limit adds `retry_after` in seconds. A `401`, `403` or `429` from the key check itself (no key over the IP limit, a missing scope, a key over its quota) carries no body. *** ## Code Examples ### JavaScript/TypeScript ```typescript theme={null} const ORACLE_URL = 'https://oracle.0xio.xyz'; // No key needed for reads. On your own server, pass a key to skip the per-IP limit: // fetch(url, { headers: { 'x-api-key': process.env.ORACLE_API_KEY } }) async function getQuote(input: string, output: string, amount: number) { const response = await fetch( `${ORACLE_URL}/api/quote?inputMint=${input}&outputMint=${output}&amount=${amount}` ); if (!response.ok) { throw new Error(`Oracle error: ${response.status}`); } return response.json(); } // Usage const quote = await getQuote('OCT', 'USDC', 100); console.log(`Expected output: ${quote.amountOut} USDC`); console.log(`Price impact: ${(quote.priceImpact * 100).toFixed(2)}%`); ``` ### Python ```python theme={null} import requests ORACLE_URL = 'https://oracle.0xio.xyz' def get_price(symbol: str) -> dict: # No key needed; add headers={'x-api-key': ...} on a server to skip the per-IP limit response = requests.get(f'{ORACLE_URL}/api/price/{symbol}') response.raise_for_status() return response.json() # Usage price = get_price('OCT') print(f"OCT Price: ${price['price']} (source: {price['source']})") ``` ### Rust ```rust theme={null} use serde::Deserialize; const ORACLE_URL: &str = "https://oracle.0xio.xyz"; #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct QuoteResponse { input_token: String, output_token: String, amount_in: f64, amount_out: f64, price_impact: f64, } async fn get_quote(input: &str, output: &str, amount: f64) -> Result { let client = reqwest::Client::new(); client .get(format!("{}/api/quote", ORACLE_URL)) .query(&[ ("inputMint", input), ("outputMint", output), ("amount", &amount.to_string()), ]) .send() .await? .json() .await } ``` # PVAC SDK (@0xio/pvac) Source: https://docs.0xio.xyz/developers/pvac-guide FHE privacy primitives for building privacy-aware DApps on Octra ## Overview `@0xio/pvac` (v1.0.2) provides standalone FHE (Fully Homomorphic Encryption) primitives for DApps that need to work with encrypted balances on Octra Network. It runs entirely client-side via WASM: no wallet extension dependency for cryptographic operations. The primitives are built on the **Ristretto255** group (curve25519-dalek): exponential-ElGamal ciphers, Bulletproofs-style range proofs, and Pedersen commitments. > [!NOTE] > > PVAC's confidentiality rests on the discrete-log hardness assumption (Ristretto255). It is **not** lattice-based and is **not** post-quantum / quantum-safe. Use `@0xio/pvac` for FHE crypto, and `@0xio/sdk` for wallet signing. They work together: | Package | Purpose | | - | - | | `@0xio/pvac` | Encrypt, decrypt, proofs, cipher math | | `@0xio/sdk` | Connect wallet, sign transactions, broadcast | ## Installation ```bash theme={null} npm install @0xio/pvac ``` ## Quick Start ```typescript theme={null} import { PvacContext, initPvac } from '@0xio/pvac'; // 1. Initialize WASM module const wasm = await initPvac(); // 2. Create context from wallet seed (seed must be exactly 32 bytes) const seed = new Uint8Array(32); // derive from wallet key const ctx = await PvacContext.create(seed, wasm); // For parallel range proofs, opt into the rayon thread pool: // const ctx = await PvacContext.create(seed, wasm, { preWarm: true, threads: 8 }); // 3. Encrypt an amount const cipher = ctx.encrypt(100_000_000n); // 100 OCT in microOCT // 4. Create proofs const blinding = crypto.getRandomValues(new Uint8Array(32)); const boundProof = ctx.makeBoundProof(cipher, 100_000_000n, blinding); const commitment = ctx.pedersenCommit(100_000_000n, blinding); ``` ## API Reference ### Encryption | Method | Description | | - | - | | `encrypt(value)` | Encrypt an amount (bigint) | | `encryptSeeded(value, seed)` | Encrypt with deterministic seed | | `encryptZero()` | Encrypt zero (for account initialization) | | `decrypt(cipher)` | Decrypt a cipher to bigint | ### Cipher Arithmetic | Method | Description | | - | - | | `ctAdd(a, b)` | Add two ciphers: `enc(a) + enc(b) = enc(a+b)` | | `ctSub(a, b)` | Subtract: `enc(a) - enc(b) = enc(a-b)` | ### Proofs | Method | Description | Time | | - | - | - | | `makeBoundProof(cipher, value, blinding)` | Prove cipher encrypts specific value | \~100ms | | `makeRangeProof(cipher, value)` | Prove decrypted value ≥ 0 | 30-60s | | `makeRangeProofParallel(cipher, value)` | Parallel range proof (rayon): requires `threads` in `create()` | 15-30s | | `pedersenCommit(value, blinding)` | Create Pedersen commitment | \~1ms | | `verifyRangeProof(cipher, proof)` | Verify a range proof | \~100ms | | `verifyBoundProof(cipher, proof, commitment)` | Verify a bound proof | \~50ms | ## Private DEX Example ```typescript theme={null} import { PvacContext, initPvac } from '@0xio/pvac'; import { ZeroXIOWallet } from '@0xio/sdk'; // Setup const wasm = await initPvac(); const ctx = await PvacContext.create(seed, wasm); const wallet = new ZeroXIOWallet({ appName: 'My DEX' }); await wallet.initialize(); await wallet.connect(); // Open private account on pool contract const pubkey = ctx.serializePubkey(); const zeroCipher = ctx.encryptZero(); const blinding = crypto.getRandomValues(new Uint8Array(32)); const zeroProof = ctx.makeBoundProof(zeroCipher, 0n, blinding); const rangeProof = ctx.makeRangeProof(zeroCipher, 0n); await wallet.callContract({ contract: 'octYourPoolAddress...', method: 'open_private_account', params: [ btoa(String.fromCharCode(...pubkey)), btoa(String.fromCharCode(...zeroCipher)), btoa(String.fromCharCode(...zeroProof)), btoa(String.fromCharCode(...rangeProof)), ], amount: 0, }); ``` ### Subtracting a Public Amount To subtract a known amount from an encrypted balance (e.g., after a swap): ```typescript theme={null} // Subtract 50 OCT from encrypted balance const amountCipher = ctx.encrypt(50_000_000n); const newBalance = ctx.ctSub(currentCipher, amountCipher); // Prove remaining balance ≥ 0 const proof = ctx.makeRangeProof(newBalance, expectedNewValue); ``` This is mathematically identical to `fhe_sub_const`. Both produce `enc(balance - amount)`. ## Network Proof Rules Each network states which proof rules it accepts. 0xio wallets read them before any private action and again right before signing. * **Where:** `octra_pvacMigrationStatus` for the address. `proof_math` is `"wide"` or `"prior"` and sets the challenge arithmetic. `proof_key` (true or false) says whether proofs are bound to the registered encryption key. * **Fallback:** when the answer has no `proof_math`, only the devnet runtime the Octra reference client lists (read from `octra_runtimeVersion`) counts, and it means wide arithmetic without key binding. * **No rules:** a network that states neither gets no private operation from the wallet. The user sees that private actions are paused, and nothing is sent. * **Rules changed:** when the rules read right before signing differ from the ones the proofs were built under, nothing is sent and the action is prepared again. * **Key binding:** on a `proof_key` network every key switch carries `owner_cipher` and `owner_proof`, a proof that the sender owns the key being registered. * **Migration:** when the network opens its cipher proof route, a balance held under an older key migrates with a `cipher_proof` payload instead of the older migration format. A proof built under one setting fails under the other. If your dApp builds proofs itself, check the same status first: a proof that does not match the network's rules is refused. ## Platform Support | Platform | Package | Notes | | - | - | - | | Browser / Web DApp | `@0xio/pvac` | Full support via WASM | | Browser Extension | `@0xio/pvac` | Same WASM, runs in page context | | React Native | `pvac-rs` FFI | Native ARM binary | | Desktop (Tauri) | `pvac-rs` crate | Native Rust | | Node.js | Not supported | No WASM threading | ## Approval Popup When your DApp calls `wallet.callContract()`, the 0xio extension shows an approval popup: * **Amount** = native value sent to the contract. `amount` is raw micro-OCT (1 OCT = 1000000) and is passed to the wallet as is; `amountOct` takes OCT and is converted by the SDK. Set `amount: 0` if none. * **Fee** = OU (operational units / gas). Shown separately below the amount. If you see a non-zero amount and expect 0, check your `amount` parameter. # Request lifecycle Source: https://docs.0xio.xyz/developers/request-lifecycle Request ids, statuses, confirmed waits and wallet detection in @0xio/sdk 2.12.0. Since SDK 2.12.0 every call that opens a review carries a request id. Your site can store it, follow the request, find it again after a reload, and wait for the chain without running its own RPC client. Nothing in the lifecycle ever signs or sends a second time. Wallets that speak wallet protocol 2 keep each request's status for 24 hours. With an older wallet the SDK keeps its own record for the life of the page, and every call still works. ## Detect before Connect `detectWallet()` (or `wallet.detect()`) tells you what the page can offer before any click. It needs no `initialize()`, answers in about 300 ms and never shows a browser prompt. ```typescript theme={null} import { ZeroXIOWallet } from '@0xio/sdk'; const wallet = new ZeroXIOWallet({ appName: 'My DApp' }); const found = await wallet.detect(); if (found.next === 'open-in-app') showOpenInAppButton(found.openInAppUrl); else if (found.next === 'install') showInstallLinks(); else showConnectButton(); ``` | Field | Values | | - | - | | `injected` | `{ kind, version, protocol }` of the wallet in the page, or `null` | | `desktop` | `available`, `unknown-until-connect`, `not-running`, `blocked` or `not-allowed` | | `mobile` | `true` in a phone or tablet browser | | `openInAppUrl` | A link that opens this page in the 0xio app, set on a phone with nothing injected | | `next` | `connect`, `install` or `open-in-app` | * **`unknown-until-connect`** means Chrome would ask for local network access before any page can reach 0xio Desktop. Asking would show that prompt to every visitor, so the SDK waits for a click. Show a Connect button. * **`blocked`** means the visitor denied local network access for your site. **`not-allowed`** covers `desktop: false`, a page that is not a secure context, another wallet's adapter and phones. * `isMobileBrowser()` is the check `detect()` uses; iPads that report a Mac count as mobile. ### Which wallet answered After `initialize()`, `wallet.walletInfo` describes the wallet behind the transport: ```typescript theme={null} wallet.walletInfo; // { kind: 'extension', version: '3.0.6', protocol: 2, capabilities: ['request_status', ...] } ``` `kind` is `extension`, `desktop`, `app-expo`, `app-ios` or `app-android`. The 0xio app's browser speaks the extension channel, so `wallet.transport` still reads `extension` there; `walletInfo.kind` tells them apart. The SDK switches features on `capabilities`, never on `version`. ### Opening your site in the 0xio app On a phone without a wallet, offer a link into the 0xio app: ```typescript theme={null} wallet.openInAppUrl(); // https://0xio.xyz/open?url= wallet.openInAppUrl(url, { scheme: true }); // oxio://open?url=, for QR codes inside the app ``` Only https pages without credentials in the URL get a link; anything else returns `null`. The SDK never navigates by itself: put the link on a button. The app shows your site's origin and asks before it opens it, and your site still has to call `connect()`. ## Request ids The SDK makes a request id for every call that opens a review or signs. Make your own with `newRequestId()` when you want to store it before the call: ```typescript theme={null} const requestId = wallet.newRequestId(); localStorage.setItem('deposit', requestId); const result = await wallet.callContract( { contract: POOL, method: 'deposit', params: [], amountOct: '10' }, { requestId, wait: 'confirmed', onStatus: (s) => setLabel(s.status) } ); ``` * A request id is 32 lowercase hex characters. Anything else is refused with `INVALID_PARAMS`. * Calling again with the same id never opens a second review. It joins the call in flight or returns the stored outcome. * The same id with another method, other params or another network is refused with `REQUEST_ID_REUSED`. * A failed, rejected or expired request never runs again under its id. A new id is a new review the user sees. Every review call takes `{ requestId, timeoutMs, onStatus }`. Writes also take `wait`. ## Following a request | Method | What it does | | - | - | | `requestStatus(id)` | The request's current status. Works after a reload with a protocol 2 wallet | | `waitForRequest(id, { until })` | Resolves when the request is final (or on chain with `until: 'submitted'`), whatever the outcome | | `cancelRequest(id)` | Closes your own review that was not approved yet; an approved request is unchanged | | `pendingRequest()` | The review of this page that blocks a new one, or `null` | | `wallet.on('requestUpdate', fn)` | Every status change of a request this page sent | ```typescript theme={null} const stored = localStorage.getItem('deposit'); if (stored) { const status = await wallet.requestStatus(stored); if (status.status === 'submitted') await wallet.waitForRequest(stored); } ``` ## Statuses | Status | Meaning | Next | | - | - | - | | `pending` | The wallet has the request; it waits behind another review or prepares one | `locked`, `review_open`, `submitted`, `completed`, `failed`, `rejected` | | `locked` | Waiting for the user to unlock | `review_open`, `rejected`, `expired`, `failed` | | `review_open` | The review is on screen | `signing`, `completed`, `rejected`, `expired`, `failed` | | `signing` | Approved; signing, proofs and submit | `submitted`, `failed` | | `submitted` | The node accepted it; `hash` is the node's | `confirmed`, `failed` | | `confirmed` | The chain executed it | final | | `completed` | Finished without a transaction (connect, network switch, message signature) | final | | `failed` | `error.stage` says where: `review`, `submit` or `execution` | final | | `rejected` | The user declined, closed the review, or the page closed first | final | | `expired` | The wallet's 5 minute review window ran out | final | | `unknown` | Nobody knows this id (never seen, another site's id, or older than 24 hours) | | * A final status never changes. * A site that is not connected never learns the wallet is locked: its reads answer the same, its review requests get `NOT_CONNECTED` with no prompt, and its connect reads `review_open` with no update at unlock. It sees no address or network. * Connect from a connected site and a switch to the current network go `pending` to `completed` with no review. * A page that closes before the user answers closes its review. A request the user already approved finishes, and the reloaded page can read the outcome. ## Waiting for the chain `wait: 'submitted'` (the default) resolves when the node accepted the transaction, as before. `wait: 'confirmed'` resolves when the chain executed it: ```typescript theme={null} const result = await wallet.callContract(call, { wait: 'confirmed' }); result.requestStatus; // 'confirmed' result.epoch; result.receipt?.events; // [{ event, values }] ``` * Writes resolve with `requestId` and `requestStatus` on top of their usual result. The wallet's own `status` field is left as it sent it. * Contract calls and deploys carry `receipt`: `success`, `events` and `error`. Octra's receipt has no return value, so a contract that needs to report a result should emit it as an event. * `receipt` is `null` with `receiptUnavailable: 'refused'` when the wallet's node refuses receipts (the public scanner does), or `'error'` when the read failed. * A chain rejection or drop rejects with `EXECUTION_FAILED`, with `details.hash` and `details.reason`. * Protocol 2 wallets watch the chain for you. With an older wallet the SDK reads `octra_transaction` through the wallet's read relay, every 4 seconds for a minute and then every 10 seconds, for at most 10 minutes. If the wallet refuses those reads, the call rejects with `UNSUPPORTED_METHOD` and the hash. ## Timeouts A timeout ends only the SDK's wait. It never cancels the review and never sends anything again. * A review call whose wait runs out rejects with `STATUS_UNKNOWN`, with `details.requestId` and `details.lastStatus`. The user may still approve it. Ask `requestStatus()` or `waitForRequest()`; never treat it as a failure to retry. * With a protocol 2 wallet the wait restarts on every status change, so a user who reads the review for several minutes does not time your site out. * `NETWORK_ERROR` means the link to the wallet failed. On a review call it carries the request id and `details.status: 'unknown'`, because the wallet may still finish the request. * `timeoutMs` on a call sets its wait. The default is 3 minutes, 10 minutes for private transfers and on 0xio Desktop. > [!WARNING] > > `STATUS_UNKNOWN` never means "send again". A second call with a new request id is a second transaction the user has to approve. ## Errors | Code | When | | - | - | | `STATUS_UNKNOWN` | The SDK stopped waiting; the wallet may still finish the request | | `REQUEST_ID_REUSED` | The request id belongs to another request of your site | | `EXECUTION_FAILED` | `wait: 'confirmed'` and the chain rejected or dropped the transaction | | `PVAC_KEY_MISMATCH` | `registerPrivateViewKey`: another private key is registered for the account | | `REGISTRATION_PENDING` | `registerPrivateViewKey`: this wallet's registration is not confirmed yet | | `RATE_LIMIT_EXCEEDED` | See `details.reason` below | Every error of a review call carries `error.requestId`. `RATE_LIMIT_EXCEEDED` names its cause in `details.reason`: | `details.reason` | Cause | | - | - | | `review_pending` | Another review of this page is open; `details.requestId` and `details.status` name it. `switchNetwork` is refused the same way | | `requests_per_second` | More than 20 requests a second from the page | | `open_requests` | Your site already has the most requests the wallet holds open (8, or 3 on 0xio Desktop) | ## Pre-signed transactions `submitTransaction(signedTx)` broadcasts a transaction from `signTransaction()` without a second review: the `signTransaction` review already showed exactly what was signed. It goes `pending` to `submitted`. A transaction not signed by the connected wallet is refused with `INVALID_PARAMS`. ## Registering a private receive key `registerPrivateViewKey({ address })` makes the connected wallet able to receive private transfers (its view key on chain) and claim them (a confirmed private key). | Account | What happens | Result | | - | - | - | | Already registered | Nothing is signed, no review; `pending` then `completed` | `{ status: 'registered', view_pubkey, pvac: true }` | | Not registered | One review with the live fee, then a key switch: `submitted` with a hash, then `confirmed` | `{ hash, requestStatus }` | | Another address | Refused before any review | `INVALID_PARAMS` | | Another private key registered for the account | Refused before any review, because a key switch could strand its encrypted balance | `PVAC_KEY_MISMATCH` | | A registration from this wallet not yet confirmed | Refused before any review | `REGISTRATION_PENDING` | | Balance below the fee | Refused before any review | `INSUFFICIENT_BALANCE` | There is no free registration. The node derives the view key from the public key a transaction carries, so a fresh account that never sent anything needs that one transaction. ## Which wallets speak protocol 2 | Wallet | Protocol 2 from | | - | - | | 0xio extension | 3.0.6 | | 0xio Desktop | 0.6.2 | | 0xio app | 1.3.5 | Older wallets keep working through the SDK's fallbacks: `walletInfo.protocol` reads `1`, `requestStatus()` answers from the SDK's own record until a reload, and `wait: 'confirmed'` reads the chain through the wallet. Building a wallet? See [Wallet protocol 2](/developers/wallet-protocol). # SDK Integration Guide Source: https://docs.0xio.xyz/developers/sdk-guide Integrate the 0xio Wallet SDK to connect dApps to the browser extension, desktop and mobile apps with FHE privacy support, plus the extension's EVM provider. The **0xio Wallet SDK** connects your dApp to the 0xio Wallet on every platform (the browser extension, the desktop app's built-in browser and the mobile app's built-in browser), with support for Octra's FHE privacy features. For EVM sites, see [EVM Provider](#evm-provider). Building with an AI assistant: [Build with AI](/developers/build-with-ai). ## Installation Install via npm or yarn: ```bash npm theme={null} npm install @0xio/sdk ``` ```bash yarn theme={null} yarn add @0xio/sdk ``` ```bash pnpm theme={null} pnpm add @0xio/sdk ``` **Package Details:** * **Version:** 2.12.2 * **Names (`lookupAddress`, `resolveName`):** see [Names](/developers/names); needs the `contract_views` permission * **Request lifecycle (request ids, status, confirmed waits):** see [Request lifecycle](/developers/request-lifecycle); full support from extension 3.0.6, 0xio Desktop 0.6.2 and 0xio app 1.3.5 * **Watch-only signal (`canSign`, `WATCH_ONLY`):** Extension v3.0.3+ * **Contract deployment (`deployContract`):** Extension v3.0.0+ * **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: ```bash theme={null} npm install vite-plugin-node-polyfills ``` 2. Update `vite.config.ts`: ```typescript theme={null} import { defineConfig } from 'vite'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; export default defineConfig({ plugins: [ nodePolyfills({ include: ['buffer', 'process', 'util', 'stream', 'events'], globals: { Buffer: true, global: true, process: true, }, }), ], }); ``` ### Webpack 5 Configuration If using Create React App (CRA) v5 or custom Webpack, install `react-app-rewired` or customize your config: ```javascript theme={null} // webpack.config.js const webpack = require('webpack'); module.exports = { // ... resolve: { fallback: { "crypto": require.resolve("crypto-browserify"), "stream": require.resolve("stream-browserify"), "buffer": require.resolve("buffer/"), "process": require.resolve("process/browser"), } }, plugins: [ new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], process: 'process/browser', }), ] }; ``` ## Quick Start The fastest way to get started: `createZeroXIOWallet` handles initialization and optional auto-connect: ```typescript theme={null} import { createZeroXIOWallet } from '@0xio/sdk'; // One-liner: create, initialize, and auto-connect const wallet = await createZeroXIOWallet({ appName: 'My Privacy dApp', appDescription: 'An encrypted DeFi protocol on Octra', autoConnect: true, // Auto-reconnects if user previously approved }); const balance = await wallet.getBalance(true); console.log('Public:', balance.public, 'OCT'); console.log('Private:', balance.private, 'OCT'); ``` Or with manual control: ```typescript theme={null} import { ZeroXIOWallet } from '@0xio/sdk'; const wallet = new ZeroXIOWallet({ appName: 'My Privacy dApp', appDescription: 'An encrypted DeFi protocol on Octra', }); // Initialize: detects 0xio extension or any RFC-O-1 wallet const isAvailable = await wallet.initialize(); if (!isAvailable) { console.log("No compatible wallet detected."); return; } // Connect: the user approves in the wallet popup const result = await wallet.connect({ permissions: ['accounts', 'public_transactions'] }); console.log('Connected to:', result.address); console.log('Network:', result.networkInfo.name); ``` ## 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. | Method | Raw field (sent unchanged) | OCT field (converted by the SDK) | | - | - | - | | `sendTransaction`, `signTransaction` | `amount` | `amountOct` | | `callContract` | `amount` | `amountOct` | | `sendPrivateTransfer` | `amountRaw` | `amount` | | `getBalance` | | returns OCT | | `getTransactionHistory` | rows carry raw micro-OCT, as the node returns them | | 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. ```typescript theme={null} await wallet.sendTransaction({ to, amountOct: '10.5' }); // 10.5 OCT await wallet.sendTransaction({ to, amount: '10500000' }); // the same, in raw units ``` ## Permissions Ask for what the dapp uses. The wallet enforces these scope names; the private ones show a warning in the connection dialog. | Scope | Grants | | - | - | | `accounts` | address, balance, public key | | `public_transactions` | sends and message signing | | `contract_calls` | state-changing contract calls | | `contract_views` | read-only contract calls | | `private_balance_read` | private balance, decrypting values, pending transfers | | `private_proofs` | encrypting values, zero and range proofs | | `private_transfers` | sending private transfers | | `private_claims` | claiming private transfers | 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`). While the wallet is locked, `getBalance` and the private balance reads also return `WALLET_LOCKED` (extension 3.0.0 and newer). ## What the Review Shows The user approves exactly what is signed. The wallet reduces each request to the fields it signs, and the review shows those same fields, so a request cannot show one thing and sign another. * **One value per field.** A request may name its recipient as `to`, `to_` or `contract`, its method as `method` or `encrypted_data`, and its arguments as `params` or `message`. When two of them are set and differ, the wallet refuses the request with `INVALID_PARAMS` before any review opens. Send each value once. * **Arguments are signed as given.** A contract call signs its arguments as JSON array text. An array in `params` is written out as JSON. When the request carries the text itself, the extension signs it byte for byte, so integers above 2^53 keep every digit and the review shows them exactly. A JavaScript number above 2^53 has already lost digits before it reaches the wallet, so send such a call as text: ```typescript theme={null} await wallet.request('call_contract', { contract: 'oct26LiaGUz78Jnby2TWEHcmboKHyupnu1NX1G3Evz28YHm', method: 'set_limit', message: '[18446744073709551615, true]', // signed exactly as written }); ``` * **Sequences show every step.** `sendContractTransactionSequence` lists each step with its contract, method, exact arguments and attached amount. * **Fees.** The wallet uses the fee you pass only when it is a whole number at or above the network's live minimum; otherwise it shows and signs the live recommended fee. With no live fee the request is refused before the review. Private operations have floors (5,000 OU, claims 3,000 OU) and fees stop at 10,000,000 OU. See [Network Fees](/wallet/sending#network-fees). * **Resends.** When the node answers that a transaction was not accepted for now, the wallet sends the same signed bytes again, up to 3 tries. A used nonce goes back to the user for a new review. Nothing is re-signed without the user. See [What Happens After You Send](/wallet/sending#what-happens-after-you-send). * **Hash.** The hash returned is computed by the wallet the same way the network computes it, so you can look it up even when the node's reply was lost. 0xio Desktop adds a few limits of its own: `signTransaction` signs standard transfers only, messages and memos over 4,000 characters are refused (never cut), and contract `params` must be an array. Each is `INVALID_PARAMS` before any review. ## 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. | Platform | Transport | Description | | - | - | - | | **0xio Wallet Extension** | `window.postMessage` | Standard browser extension communication | | **RFC-O-1 Third-Party Wallet** | `window.octra.request()` | Any wallet implementing the RFC-O-1 standard | | **0xio Desktop** | `window.parent.postMessage` (iframe) | DApp loaded in Desktop's built-in BrowserScreen | | **0xio App** | `window.parent.postMessage` (WebView) | DApp loaded in App's built-in browser | | **0xio Desktop, no extension** | WebSocket to `ws://127.0.0.1:19345` | Used only when no extension answers; approvals in the desktop menu bar (SDK 2.11.0) | ### 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. > [!TIP] > > 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. ### 0xio Desktop without the extension (2.11.0) A visitor who has 0xio Desktop but not the extension can use your site as is. `initialize()` looks for the extension first, for about 300 ms (`detectTimeoutMs`), and the extension always wins. When none answers, the SDK reaches 0xio Desktop on this computer and `wallet.transport` reads `'desktop'` (otherwise `'extension'`). ```typescript theme={null} const wallet = new ZeroXIOWallet({ appName: 'My DApp' }); await wallet.initialize(); console.log(wallet.transport); // 'extension', 'desktop', or null until connect() console.log(wallet.desktopAvailableOnConnect); // true: connect() will try 0xio Desktop // Opt out for this site: new ZeroXIOWallet({ appName: 'My DApp', desktop: false }); ``` What the visitor sees: * **A Chrome prompt the first time, after Connect.** Chrome asks once to let the site "access other apps and services on this device". The SDK never triggers it on page load: when Chrome would ask, `initialize()` resolves with `transport` `null` and `desktopAvailableOnConnect` `true`, and the next `connect()` opens the desktop. Keep the Connect button enabled in that state. The SDK emits `connectProgress` with `phase: 'local-network-prompt'` while the prompt is open. A block ends the attempt at once with `EXTENSION_NOT_FOUND` and `details.desktop: 'blocked'`, a desktop that is not running with `'not-running'`; only an ignored prompt waits the full 20 seconds. * **Approvals in the menu bar.** Connect, signing, sends and network switches are reviewed in the 0xio Desktop menu bar (Reject or the action), not in a popup. The SDK waits up to ten minutes for an answer. Rules and differences: * Tried only on `https:` pages that are a secure context, or on `http://localhost`, `127.0.0.1` and `[::1]` during development; never with `desktop: false` or an adapter for another wallet. * `getBalance()` reports `private: 0`; read private balances with `getPrivateBalance()`. * Private methods run only for the wallet open in the desktop and while it is on the site's network; otherwise they fail with `WALLET_CHANGED` or `NOT_AVAILABLE`. * `getPendingPrivateTransfers()` fails with `UNSUPPORTED_METHOD`; use `getPrivateBalance({ include_claimable: true })` and `claimPrivateTransfer()`. * At most 3 requests from a site wait at once, and 60 a minute. * If the desktop quits, the SDK emits `disconnect` with reason `network_error`; the next call reconnects. Call `getConnectionStatus()` or `connect()` to restore the session. * **Site token (2.12.0).** Any local program can send your site's Origin, so an approved `connect()` gives the page a site token. The SDK keeps it in the page's `localStorage` (`0xio:desktop-site-token`), shows it on every new link, and never hands it to your code. Without it (cleared storage, a private window) the visitor approves `connect()` again. `disconnect()` forgets it. ### 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: ```typescript theme={null} const wallet = new ZeroXIOWallet({ appName: 'My DApp', trustedParentOrigins: ['https://desktop.0xio.xyz'], }); ``` ### In the 0xio Mobile App The app's built-in browser injects the same providers (`window.wallet0xio` and `window.octra`) and answers the SDK over its WebView bridge, so the calls below work unchanged. What the user sees for each request: | Request | What the app shows | How it is confirmed | | - | - | - | | `connect()` | Your site's listed name and host (never the page title), the wallet with **Change**, what the site can see (address and balance), what it can ask (signatures the user reviews), any private permissions you asked for, and that it can never move funds on its own | **Slide to connect** | | `signMessage()` | The whole message in a scrolling box and "Signing moves no funds and costs no fee." | **Slide to sign** | | `sendTransaction()`, `callContract()`, sequences | A receipt: the action, amount with USD, recipient, numbered steps for a sequence, fee, network and wallet, plus the memo | **Slide to approve** | | `signTransaction()` | Titled "Sign a transaction", with a warning that the site can send what is signed. It is never broadcast by the wallet | **Slide to approve** | | `submitTransaction()` | No second review: the `signTransaction()` review already showed what was signed. Refused unless signed by the connected wallet | none | | `switchNetwork()` | A confirm sheet; only mainnet and devnet, and only for a connected site | Switch network or Cancel | * **https only.** The wallet answers only pages served over https. A plain http page loads but cannot connect. Development builds of the app also accept `http://localhost`. * **One request at a time.** A second request while one is open is refused; wait for the first to settle. * **Biometrics or PIN after the slide.** With the user's **Ask for PIN before signing** setting on (the default), Face ID or the PIN follows the slide. Allow for a few seconds of user time on every call. * **Bound to a wallet and network.** A request is held to the wallet and network it was reviewed under. If the user switches either before it is signed, nothing is signed or sent and your call rejects. Listen for `accountChanged` and `networkChanged` and re-read state. * **Connections lapse.** A connection is held per wallet and network and expires after 24 hours; call `connect()` again when `isConnected()` is false. * **Raw RPC cannot submit.** Pass-through RPC methods that submit or send are refused; use `sendTransaction`, `callContract` or `submitTransaction` so the user sees an approval. * **Lookalike warning.** If your host is spelt close to an app listed in the 0xio directory, or uses another alphabet, the user sees a warning before your site can connect, sign, send or switch network, and your request rejects if they close it. Serve your dApp from its listed host to avoid it. ### WalletConnect (0xio Mobile) A dApp on another device can reach the mobile wallet through WalletConnect v2 (v1 links are refused). The user scans your code from the scan icon on Home, or pastes the connection link. * **Connect:** the sheet shows your site, the wallet and the network, each with **Change**, and warns when you asked for a different network than the one chosen. The user confirms with **Slide to connect**. Watch-only wallets cannot connect. * **Site identity:** the app names your site by the origin WalletConnect's Verify service confirms, only when Verify reports it valid. Otherwise it shows the name your metadata gives, tagged **Unverified** (or **Lookalike** when it is spelt like a listed app). A domain Verify reports invalid, or one flagged as a scam, is refused before any sheet. Serve your dApp from the domain registered for your WalletConnect project so it shows as verified. * **Arguments:** `octra_contractCall` sheets show the arguments exactly as signed. A request that expires on your side while the transaction is being sent is still answered with its real result. * **Session scope:** the session is bound to the wallet and network chosen at connect. Choosing them does not switch the app; if the app is on another wallet or network later, requests fail and the user is asked to switch back or reconnect. * **Methods:** `octra_sendTransaction`, `octra_contractCall` and `octra_signMessage` open the same sheets as the in-app browser, with the same slide; `octra_getAddress` and `octra_getBalance` are answered without a prompt. * **Timing:** connect requests that arrive after the user leaves the WalletConnect screen are refused. Requests from a session already connected are answered anywhere in the app once it is unlocked. * **Disconnect:** the user can end a session from the WalletConnect screen or from Connected sites; it is removed for that wallet and network. ## 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: | Priority | Adapter | Detects | | - | - | - | | 1 | `ZeroXIOAdapter` | `window.wallet0xio` or `window.ZeroXIOWallet` (0xio extension) | | 2 | `OctraProviderAdapter` | `window.octra.isOctra` (any RFC-O-1 wallet) | 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: ```typescript theme={null} import { ZeroXIOWallet } from '@0xio/sdk'; const wallet = new ZeroXIOWallet({ appName: 'My DApp' }); await wallet.initialize(); // Finds window.octra from any compliant wallet await wallet.connect(); // Uses RFC-O-1 request() under the hood ``` ### Direct Provider Access For advanced use cases, you can use the provider directly: ```typescript theme={null} import { OctraProviderAdapter } from '@0xio/sdk'; // Check if any RFC-O-1 wallet is available if (OctraProviderAdapter.detect()) { const accounts = await window.octra.request({ method: 'octra_requestAccounts' }); const balance = await window.octra.request({ method: 'octra_getEncryptedBalance' }); } ``` ### RFC-O-1 Error Codes | Code | Meaning | SDK Error Code | | - | - | - | | 4001 | User rejected | `USER_REJECTED` | | 4100 | Unauthorized | `PERMISSION_DENIED`, or `WATCH_ONLY` when the error `data.code` is `WATCH_ONLY` | | 4200 | Unsupported method | `UNKNOWN_ERROR` | | 4900 | Disconnected | `CONNECTION_REFUSED` | | 4901 | Network unavailable | `NETWORK_ERROR` | *** ## EVM Provider The SDK covers Octra. For Ethereum and other EVM networks, the 0xio browser extension (3.0 and later) is a standard EIP-1193 provider, so existing EVM libraries work with it unchanged. Users manage these connections as described in [Connecting to Sites](/wallet/connecting-sites). ### Discovery * The extension announces itself through **EIP-6963** with the name **0xio Wallet** and the rdns `xyz.0xio.wallet`. * It defines `window.ethereum` only when the page has none, so it never replaces another wallet. ```typescript theme={null} window.addEventListener('eip6963:announceProvider', (event: any) => { if (event.detail.info.rdns === 'xyz.0xio.wallet') { const provider = event.detail.provider; // use provider.request({ method, params }) } }); window.dispatchEvent(new Event('eip6963:requestProvider')); ``` ### Methods | Kind | Methods | | - | - | | Open a review | `eth_requestAccounts`, `wallet_requestPermissions`, `eth_sendTransaction`, `personal_sign`, `eth_signTypedData_v4`, `wallet_switchEthereumChain`, `wallet_addEthereumChain`, `wallet_watchAsset` | | Answered by the wallet | `eth_chainId`, `net_version`, `eth_accounts`, `wallet_getPermissions`, `wallet_getCapabilities`, `web3_sha3`, `net_listening` | | Forwarded to the site's network | `eth_call`, `eth_getBalance`, `eth_estimateGas`, receipts, logs, blocks, filters, `eth_syncing` and the other read methods | | Refused (`4200`) | `eth_sign`, `eth_signTransaction`, `eth_sendRawTransaction`, older typed-data versions, `wallet_sendCalls` and any other unknown method | ### Behavior to Expect * **One address:** `eth_accounts` answers only the address the site uses, even when several of the user's wallets are connected to it. A request naming another address is refused. While the wallet is locked it answers `[]`, and the same account comes back after unlock without a prompt. * **Events:** `accountsChanged` when the site moves to another of its wallets, `chainChanged` when the user moves it to another network. Events go only to the tabs and frames that used the provider. * **Fees:** transactions are signed with the wallet's live fee. The fee your site sends is used only when the network gives none. * **Approvals:** the user can lower the amount of an `approve` or `increaseAllowance` on the review, or pick Unlimited. Never assume the signed amount equals the one you sent. * **Contract creation:** a transaction with no `to` opens a deploy review with the new contract's address; signing is refused if the nonce moved after the review. * **Adding a network:** a chain the user already has is never changed by `wallet_addEthereumChain`; the request only asks to switch. A chain on the other side of the user's Mainnet or Testnet setting asks to switch all of 0xio first. * **Repeats and limits:** the same connect, switch or add request sent again while its review is open waits on that review. Reads are rate limited per page and per site and answer `-32005` beyond the limit. * **Reload:** when the extension reloads, pending requests reject with `4900` and the provider emits `disconnect` once. *** ## Request lifecycle (2.12.0) Every review call takes `{ requestId, timeoutMs, onStatus }`, and every write also `wait: 'submitted' | 'confirmed'`. A timeout rejects with `STATUS_UNKNOWN` and never cancels the review; it never means "send again". ```typescript theme={null} const requestId = wallet.newRequestId(); const result = await wallet.sendTransaction( { to: 'oct1qv...', amountOct: '1' }, { requestId, wait: 'confirmed', onStatus: (s) => console.log(s.status) } ); console.log(result.requestStatus, result.hash); ``` Detection, statuses, receipts, reloads and error reasons: [Request lifecycle](/developers/request-lifecycle). ## Names (2.12.2) `lookupAddress()` gives the primary name of the connected address in the Octra Name System, or `null`, so a connected button can show `0xio.oct` or the address. `resolveName(name)` gives what a name points to. Both are reads. ```typescript theme={null} const name = await wallet.lookupAddress(); button.textContent = name ?? `${address.slice(0, 6)}...${address.slice(-4)}`; ``` Sending to a name, the name rules and where the name contract comes from: [Names](/developers/names). ## Core API ### Connection Methods #### `initialize()` Initializes the SDK and checks for extension presence. ```typescript theme={null} const success = await wallet.initialize(); if (success) { console.log('SDK ready!'); } ``` * **Returns:** `Promise` * **Throws:** `ZeroXIOWalletError` with code `EXTENSION_NOT_FOUND` if 0xio is not installed #### `isReady()` Check if SDK is initialized and extension is available. ```typescript theme={null} if (wallet.isReady()) { // Safe to make requests } ``` * **Returns:** `boolean` #### `connect(options)` Requests connection to the extension. User must approve in popup. ```typescript theme={null} const result = await wallet.connect({ permissions: [ 'accounts', // address, balance, public key 'public_transactions', // sends and message signing 'private_balance_read' // private balance (optional, shows a warning) ] }); console.log('Connected:', result.address); console.log('Balance:', result.balance.total, 'OCT'); console.log('Network:', result.networkInfo.name); ``` * **Returns:** `Promise` with address, balance, networkInfo, and permissions * **Throws:** `ZeroXIOWalletError` with code `CONNECTION_REFUSED` if user declines #### `disconnect()` Disconnects from the extension. ```typescript theme={null} await wallet.disconnect(); ``` * **Returns:** `Promise` #### `isConnected()` Checks current connection status. ```typescript theme={null} const connected = wallet.isConnected(); ``` * **Returns:** `boolean` #### `getConnectionInfo()` Gets the cached connection info. ```typescript theme={null} const info = wallet.getConnectionInfo(); console.log(info.address); console.log(info.balance); console.log(info.connectedAt); ``` * **Returns:** `ConnectionInfo` #### `getConnectionStatus()` Checks connection status with extension (async). ```typescript theme={null} const status = await wallet.getConnectionStatus(); if (status.isConnected) { console.log('Already connected to:', status.address); } ``` * **Returns:** `Promise` #### `getAddress()` Gets the currently connected address. ```typescript theme={null} const address = wallet.getAddress(); // Returns: "oct1abc123..." or null if not connected ``` * **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`. ```typescript theme={null} const publicKey = await wallet.getPublicKey(); ``` * **Returns:** `Promise` * **Errors:** `WATCH_ONLY` for a watch-only wallet (it has no key) #### `canSign()` Whether the connected wallet can sign (v2.10.0). A watch-only wallet connects and reads balances and history, but holds no key. ```typescript theme={null} const canSign = wallet.canSign(); // true, false or undefined ``` * **Returns:** `boolean | undefined` * `true`: the wallet signs. * `false`: watch-only. Every signing call fails with `WATCH_ONLY`. * `undefined`: not connected, or an extension older than v3.0.3 that does not report it. The same value is `canSign` on `ConnectionInfo`, on the `connect` event and on `accountChanged`, so it follows a wallet switch. See [Watch-only wallets](#watch-only-wallets). #### `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. ```typescript theme={null} // Detect wrong network const networkId = wallet.getNetworkId(); // 'mainnet' or 'devnet' if (networkId !== 'devnet') { try { const result = await wallet.switchNetwork('devnet'); console.log('Switched:', result.switched); // true once the user confirmed } catch (e) { // USER_REJECTED: the user kept the current network } } ``` * **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. ```typescript theme={null} const networkId = wallet.getNetworkId(); // Returns: 'mainnet', 'devnet', or null ``` * **Returns:** `string | null` ### Balance Methods #### `getBalance(forceRefresh?)` Fetches current balances from blockchain or cache. ```typescript theme={null} const balance = await wallet.getBalance(true); // Force refresh console.log(balance.public); // 1250.5 (number) console.log(balance.private); // 3420.25 (number) console.log(balance.total); // 4670.75 (number) console.log(balance.currency); // "OCT" ``` * **Parameters:** * `forceRefresh`: If true, queries blockchain; if false, uses cached values * **Returns:** `Promise` with public, private, total (numbers), and currency ```typescript theme={null} interface Balance { public: number; // Public balance in OCT private: number; // Private (FHE) balance in OCT total: number; // Combined balance currency: 'OCT'; // Currency symbol } ``` ### Transaction Methods #### `sendTransaction(txData)` Executes a standard public transfer. ```typescript theme={null} const result = await wallet.sendTransaction({ to: 'oct1qv...', amountOct: '10.5', // 10.5 OCT, converted exactly by the SDK message: 'Payment for services' // Optional }); console.log('Transaction Hash:', result.hash); console.log('Accepted:', result.accepted); ``` * **Parameters:** ```typescript theme={null} interface TransactionData { to: string; // Recipient address amount?: string | number; // Raw micro-OCT, sent to the wallet as is amountOct?: string | number; // Amount in OCT, converted by the SDK. One of the two. message?: string; // Optional transaction memo feeLevel?: 1 | 3; // 1 = standard, 3 = priority isPrivate?: boolean; // Use private transfer } ``` * **Returns:** `Promise` 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 without a second review, because the `signTransaction` review already showed what was signed. A transaction not signed by the connected wallet is refused with `INVALID_PARAMS`. ```typescript theme={null} const { signedTx } = await wallet.signTransaction({ to: 'oct1qv...', amountOct: '1' }); const result = await wallet.submitTransaction(signedTx); ``` #### `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. ```typescript theme={null} const result = await wallet.sendPrivateTransfer({ to: 'oct1x...', amount: 20 // OCT }); console.log('TX Hash:', result.hash); ``` > [!NOTE] > > **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:** ```typescript theme={null} interface PrivateTransferData { to: string; // Recipient address amount: number; // Amount in OCT amountRaw?: string; // Exact amount in raw micro-OCT; when set, this is what the wallet uses message?: string; // Not carried by the wallet; private transfers have no memo on-chain } ``` * **Returns:** `Promise` with `hash` and `accepted` * **Note:** The amount is encrypted on your device before transmission #### `getTransactionHistory(page, limit)` Gets transaction history. ```typescript theme={null} const history = await wallet.getTransactionHistory(1, 20); history.transactions.forEach(tx => { console.log(`${tx.from} to ${tx.to}: ${Number(tx.amount) / 1_000_000} OCT`); // rows carry raw micro-OCT }); console.log('Total:', history.totalCount); console.log('Has more:', history.hasMore); ``` * **Parameters:** `page` (default: 1), `limit` (default: 20) * **Returns:** `Promise` ### 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. ```typescript theme={null} // Simple message signing const signature = await wallet.signMessage('Hello, 0xio!'); console.log('Signature:', signature); // Authentication challenge with timestamp const timestamp = Date.now(); const challenge = `Sign in to MyDApp\nTimestamp: ${timestamp}\nAddress: ${wallet.getAddress()}`; const authSignature = await wallet.signMessage(challenge); // API key creation (for 0xio Oracle/Indexer) const owner = wallet.getAddress(); const ts = Math.floor(Date.now() / 1000); const apiKeyMessage = `0xio-api-key:${owner}:${ts}`; const signature = await wallet.signMessage(apiKeyMessage); ``` * **Parameters:** `message: string` - The text message to sign (must be non-empty) * **Returns:** `Promise` - 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 > [!NOTE] > > **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 > [!WARNING] > > 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. ```typescript theme={null} const signature = await wallet.signAuthMessage('MyDApp', 'abc123'); // Signs: "0xio auth\nService: MyDApp\nNonce: abc123\nOrigin: https://mydapp.com" ``` * **Parameters:** `service: string` (service name), `nonce: string` (unique challenge) * **Returns:** `Promise` - 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+. ```typescript theme={null} import { verifyMessage } from '@0xio/sdk'; const publicKey = await wallet.getPublicKey(); // base64 const valid = await verifyMessage('Hello, 0xio!', signature, publicKey); ``` * **Parameters:** `message: string`, `signature: string` (base64), `publicKey: string` (base64) * **Returns:** `Promise` #### `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). ```typescript theme={null} import { getSignedMessageBytes } from '@0xio/sdk'; import nacl from 'tweetnacl'; const ok = nacl.sign.detached.verify( getSignedMessageBytes(message), base64ToBytes(signature), base64ToBytes(publicKey), ); ``` * **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. ```typescript theme={null} import { ContractCallData } from '@0xio/sdk'; const result = await wallet.callContract({ contract: 'oct26LiaGUz78Jnby2TWEHcmboKHyupnu1NX1G3Evz28YHm', method: 'swap', params: [100000, true, 90000], // Flat primitives, not array-wrapped amountOct: '0', // Value to attach in OCT (optional, default none); or amount in raw micro-OCT ou: '10000', // Operational units (optional, default '10000') }); console.log('TX Hash:', result.txHash); console.log('Success:', result.success); ``` * **Parameters:** ```typescript theme={null} type ContractParams = ReadonlyArray; interface ContractCallData { contract: string; // Contract address (oct-prefixed, 47 chars) method: string; // AML method name params: ContractParams; // Method arguments, flat primitives amount?: string | number; // Value to attach in raw micro-OCT, sent as is (default '0') amountOct?: string | number; // Value to attach in OCT, converted by the SDK. One of the two. ou?: string | number; // Operational units (default '10000') } ``` * **Returns:** `Promise` with `txHash`, `success`, `finality` > [!WARNING] > > AML contract calls use **flat arguments**: `params: [arg1, arg2]`, not `params: [[arg1, arg2]]`. The extension handles nonce, signing, and broadcasting automatically. The review shows the method with its arguments exactly as they are signed. For integers above 2^53, see [What the Review Shows](#what-the-review-shows). #### `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`. ```typescript theme={null} // Query contract state const price = await wallet.contractCallView({ contract: 'oct26LiaGUz78Jnby2TWEHcmboKHyupnu1NX1G3Evz28YHm', method: 'get_active_price', params: [], }); console.log('Active price:', price); // Query with parameters const reserves = await wallet.contractCallView({ contract: 'oct26LiaGUz78Jnby2TWEHcmboKHyupnu1NX1G3Evz28YHm', method: 'get_bin_reserves', params: [8388608], caller: 'oct1youraddress...', // Optional caller context }); console.log('Reserves:', reserves); ``` * **Parameters:** ```typescript theme={null} interface ContractViewCallData { contract: string; // Contract address (oct-prefixed, 47 chars) method: string; // View method name params: ContractParams; // Method arguments, flat primitives caller?: string; // Optional caller address } ``` * **Returns:** `Promise`, the raw result from the contract method #### `getContractStorage(contract, key)` Read a single key from contract storage. No wallet unlock or approval needed. ```typescript theme={null} const totalSupply = await wallet.getContractStorage( 'oct7qrpW3T8mXYbZfAZrkLQKGAtD6ZswCf9Px5jPXnfmtiK', 'total_supply' ); console.log('Total supply:', totalSupply); // Returns null if key doesn't exist const missing = await wallet.getContractStorage(contractAddr, 'nonexistent'); console.log(missing); // null ``` * **Parameters:** `contract: string` (contract address), `key: string` (storage key) * **Returns:** `Promise`, 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, method, exact arguments, attached value and fee. A step whose fields disagree (see [What the Review Shows](#what-the-review-shows)) refuses the whole sequence with `INVALID_PARAMS`, naming the step. ```typescript theme={null} const result = await wallet.sendContractTransactionSequence({ transactions: [ { contract: 'oct26Lia...', method: 'approve', params: [poolAddress, 1000000] }, { contract: 'oct26Lia...', method: 'swap', params: [1000000, true, 900000] }, ], }); ``` * **Returns:** `Promise` with `results[]`, one hash per step #### `deployContract(data)` Deploys a compiled Octra contract from the connected wallet. Needs extension 3.0.0 or later. The review shows the code size, the contract address the node predicts, the constructor params, the network and the fee; the wallet cannot be switched in that review, because the address depends on the deployer. ```typescript theme={null} const { txHash, contractAddress } = await wallet.deployContract({ bytecode: compiled.deploy_payload, // base64 from octra_compileAml with program=true params: ['0xio Token', 'OXT', 1000000], // optional constructor params (JSON) }); ``` * **Parameters:** `bytecode: string` (strict base64, at most 512 KB decoded), `params?: string | unknown[]` (valid JSON, at most 64 KB), `ou?: string` (a whole number from 1 to 50,000,000) * **Fee:** without `ou` the wallet uses the node's recommended deploy fee, never below 200,000 OU plus 1,000 OU per KiB of code * **Returns:** `Promise<{ txHash, contractAddress, success }>` * **Errors:** `DEPLOY_CHANGED` when another transaction moved the nonce after the review and the address would differ (nothing is sent); `DEPLOY_FAILED` when the node refuses it #### `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`. ```typescript theme={null} const account = await wallet.rpcCall('octra_balance', [wallet.getAddress()]); ``` ### 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. ```typescript theme={null} const result = await wallet.encryptBalance(5.0); console.log('TX Hash:', result.txHash); console.log('Success:', result.success); ``` * **Parameters:** `amount: number | string` (OCT amount to shield) * **Returns:** `Promise` with `txHash`, `success`, `finality` > [!NOTE] > > 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). ```typescript theme={null} const result = await wallet.decryptBalance(10.0); console.log('TX Hash:', result.txHash); console.log('Success:', result.success); ``` * **Parameters:** `amount: number | string` (OCT amount to unshield) * **Returns:** `Promise` with `txHash`, `success`, `finality` > [!NOTE] > > Approvals wait up to 3 minutes; `sendPrivateTransfer` waits up to 10 minutes because proof generation follows the approval. #### `getPrivateBalanceInfo()` Gets detailed private balance information. ```typescript theme={null} const info = await wallet.getPrivateBalanceInfo(); console.log('Has private balance:', info.hasPrivateBalance); console.log('Can decrypt:', info.canDecrypt); ``` * **Returns:** `Promise` #### `getPendingPrivateTransfers()` Gets pending incoming private transfers. ```typescript theme={null} const pending = await wallet.getPendingPrivateTransfers(); pending.forEach(transfer => { console.log(`Output ${transfer.id}: ${Number(transfer.amount) / 1_000_000} OCT`); }); ``` * **Returns:** `Promise`. 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. ```typescript theme={null} const result = await wallet.claimPrivateTransfer('transfer_123'); console.log('Claimed! TX:', result.hash); ``` * **Parameters:** `transferId: string` * **Returns:** `Promise` * **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 review except `registerPrivateViewKey` for an account that is not registered yet; proofs take from seconds to a couple of minutes, and the SDK waits up to 3 minutes. | Method | Scope | Returns | | - | - | - | | `getPrivateCapabilities()` | none | what the wallet supports, to render the right UI or fail closed | | `encryptValue({ value_raw })` | `private_proofs` | `{ cipher, commitment, encoding }` | | `decryptValue({ cipher })` | `private_balance_read` | `{ value_raw, display, decimals }` | | `makeZeroProof({ cipher, value_raw })` | `private_proofs` | `{ proof, commitment, blinding, encoding }` | | `makeRangeProof({ cipher, value_raw })` | `private_proofs` | `{ proof, encoding }` | | `getPrivateBalance()` | `private_balance_read` | `{ address, private_oct_raw, private_oct, tokens }` | | `registerPrivateViewKey({ address })` | `private_transfers` | `{ status: 'registered', view_pubkey, pvac: true }` at once when registered; otherwise one review with the fee and a key switch transaction. See [the contract](/developers/request-lifecycle#registering-a-private-receive-key) | Proofs follow the proof rules the network states (see [Network Proof Rules](/developers/pvac-guide#network-proof-rules)). When the network states none, or the wallet cannot reach it to check, every private method and private transfer is refused and nothing is sent. Public calls are not affected. ```typescript theme={null} const caps = await wallet.getPrivateCapabilities(); if (caps.supports.encrypt_value) { const { cipher } = await wallet.encryptValue({ value_raw: '1000000' }); // 1 OCT const { proof, commitment, blinding } = await wallet.makeZeroProof({ cipher, value_raw: '1000000' }); } ``` ### Network Methods #### `getNetworkInfo()` Gets current network information. ```typescript theme={null} const network = await wallet.getNetworkInfo(); console.log('Network:', network.name); console.log('RPC:', network.rpcUrl); console.log('Explorer:', network.explorerUrl); console.log('Is Testnet:', network.isTestnet); ``` * **Returns:** `Promise` ## 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. ```typescript theme={null} wallet.on('accountChanged', (event) => { console.log('New Address:', event.data.newAddress); console.log('Previous Address:', event.previousAddress); console.log('Balance:', event.data.balance); // Re-fetch user data, update UI updateUserInterface(event.data.newAddress); }); ``` ### Network Changed User switched networks (mainnet/testnet). ```typescript theme={null} wallet.on('networkChanged', (event) => { console.log('Previous:', event.previousNetwork?.name); console.log('New:', event.data.newNetwork.name); // Update RPC endpoint, refresh data reconnectToNetwork(event.data.newNetwork); }); ``` ### Balance Changed Balance updated after transaction confirmation. ```typescript theme={null} wallet.on('balanceChanged', (event) => { console.log('Address:', event.data.address); console.log('Previous:', event.data.previousBalance?.total); console.log('New:', event.data.newBalance.total); // Update UI with new amounts updateBalanceDisplay(event.data.newBalance); }); ``` ### Connect Wallet connected successfully. ```typescript theme={null} wallet.on('connect', (event) => { console.log('Connected to:', event.data.address); console.log('Balance:', event.data.balance.total); console.log('Permissions:', event.data.permissions); }); ``` ### Disconnect User disconnected from dApp or locked wallet. ```typescript theme={null} wallet.on('disconnect', (event) => { console.log('Disconnect Reason:', event.data.reason); // Possible reasons: 'user_action', 'extension_locked', 'network_error' // Also fires when the wallet ends the session for your site (2.11.0). // Clear user session, show connect button clearUserSession(); showConnectModal(); }); ``` ### Request Update (2.12.0) Every status change of a request this page sent. Same meaning in every wallet. ```typescript theme={null} wallet.on('requestUpdate', (event) => { console.log(event.data.requestId, event.data.status); }); ``` ### Connect Progress (2.12.0) The browser's local network prompt is open while `connect()` tries 0xio Desktop. ```typescript theme={null} wallet.on('connectProgress', (event) => { if (event.data.phase === 'local-network-prompt') showPromptHint(); }); ``` ### Transaction Confirmed Accepted by the node in the extension and 0xio Desktop, final on chain in the 0xio app. For one meaning everywhere, use `requestUpdate` or `wait: 'confirmed'`. ```typescript theme={null} wallet.on('transactionConfirmed', (event) => { console.log('TX Hash:', event.txHash); console.log('Confirmations:', event.confirmations); }); ``` ### Transaction Failed The wallet could not broadcast a queued transaction. ```typescript theme={null} wallet.on('transactionFailed', (event) => { console.log('TX ID:', event.data.txId, 'reason:', event.data.error); }); ``` ### Extension Lock Events ```typescript theme={null} wallet.on('extensionLocked', () => { console.log('Wallet locked - user needs to unlock'); }); wallet.on('extensionUnlocked', () => { console.log('Wallet unlocked'); }); ``` ### Removing Listeners ```typescript theme={null} // Remove specific listener wallet.off('accountChanged', handlerFunction); // Remove all listeners for an event wallet.removeAllListeners('balanceChanged'); // Remove all listeners wallet.removeAllListeners(); ``` ### Cleanup Clean up SDK resources when done. ```typescript theme={null} wallet.cleanup(); ``` ## Error Handling The SDK provides typed error codes for graceful error handling. ```typescript theme={null} import { ZeroXIOWalletError, ErrorCode } from '@0xio/sdk'; try { await wallet.sendTransaction({ to: 'oct1...', amountOct: '100' }); } catch (error) { if (error instanceof ZeroXIOWalletError) { switch (error.code) { case ErrorCode.NOT_CONNECTED: console.log('Connect first'); break; case ErrorCode.USER_REJECTED: console.log('User cancelled the transaction'); showNotification('Transaction cancelled'); break; case ErrorCode.INSUFFICIENT_BALANCE: console.log('Not enough balance'); showNotification('Insufficient funds'); break; case ErrorCode.EXTENSION_NOT_FOUND: console.log('0xio Extension not installed'); showInstallPrompt(); break; case ErrorCode.STATUS_UNKNOWN: // The wallet may still finish it: follow it, never send it again await wallet.waitForRequest(error.requestId!); break; case ErrorCode.NETWORK_ERROR: // A review call carries its request id: the wallet may still finish it if (error.requestId) await wallet.waitForRequest(error.requestId); else showRetryOption(); break; case ErrorCode.INVALID_ADDRESS: console.log('Invalid recipient address'); showError('Please check the address'); break; case ErrorCode.WALLET_LOCKED: console.log('Wallet is locked'); showError('Please unlock your wallet'); break; case ErrorCode.PERMISSION_DENIED: console.log('Permission not granted'); break; case ErrorCode.TRANSACTION_FAILED: console.log('Transaction failed:', error.message); break; case ErrorCode.SIGNATURE_FAILED: console.log('Signing failed:', error.message); showError('Failed to sign message'); break; default: console.error('Unexpected error:', error.message); } // Access error details if (error.details) { console.log('Details:', error.details); } } else { console.error('Unknown error:', error); } } ``` ### Watch-only wallets A watch-only wallet can connect, so a dapp should show a read-only state instead of buttons that will fail. * `canSign` is `false`: show the read-only state. `sendTransaction`, `signMessage`, `signAuthMessage`, `signTransaction`, `callContract`, `deployContract`, `sendContractTransactionSequence`, `sendPrivateTransfer`, `claimPrivateTransfer`, the private primitives and `getPublicKey` reject with `WATCH_ONLY`. * `canSign` is `undefined`: the extension is older than v3.0.3. Treat a missing `publicKey` as a hint of watch-only; those versions refuse signing with `PERMISSION_DENIED`. ```typescript theme={null} import { ZeroXIOWallet, ZeroXIOWalletError, ErrorCode } from '@0xio/sdk'; function showSignState(canSign: boolean | undefined) { const readOnly = canSign === false || (canSign === undefined && !wallet.getConnectionInfo().publicKey); sendButton.disabled = readOnly; readOnlyNote.hidden = !readOnly; // "This wallet is watch-only. Switch wallets in 0xio to send." } await wallet.connect(); showSignState(wallet.canSign()); wallet.on('accountChanged', (event) => showSignState(event.data.canSign)); try { await wallet.sendTransaction({ to: recipient, amountOct: '1' }); } catch (error) { if (error instanceof ZeroXIOWalletError && error.code === ErrorCode.WATCH_ONLY) { showSignState(false); } } ``` ### Error Codes | Error Code | Description | | - | - | | `WATCH_ONLY` | The wallet is watch-only and cannot sign (v2.10.0, extension v3.0.3+) | | `USER_REJECTED` | User declined transaction or connection | | `INSUFFICIENT_BALANCE` | Not enough funds for transaction + gas | | `EXTENSION_NOT_FOUND` | 0xio Wallet Extension not installed | | `NETWORK_ERROR` | RPC connection or blockchain issue | | `INVALID_ADDRESS` | Malformed or invalid Octra address | | `INVALID_AMOUNT` | Negative amount, both amount fields given, or a number that can't be represented exactly in micro-OCT (6 decimals); pass a string like `"0.300000"` for exact control | | `DEPLOY_CHANGED` | The deployer's nonce moved after the review, so the contract address would differ; nothing was sent | | `DEPLOY_FAILED` | The node refused the deploy | | `PERMISSION_DENIED` | Requested permission not granted | | `TRANSACTION_FAILED` | Transaction reverted or rejected | | `SIGNATURE_FAILED` | Message signing failed or invalid input | | `WALLET_LOCKED` | Wallet is locked, user needs to unlock | | `CONNECTION_REFUSED` | Connection was refused | | `RATE_LIMIT_EXCEEDED` | Too many requests; `details.reason` is `review_pending`, `requests_per_second` or `open_requests` | | `STATUS_UNKNOWN` | The SDK stopped waiting; the wallet may still finish the request. Never resend (2.12.0) | | `REQUEST_ID_REUSED` | The request id belongs to another request of your site (2.12.0) | | `EXECUTION_FAILED` | `wait: 'confirmed'` and the chain rejected or dropped the transaction (2.12.0) | | `PVAC_KEY_MISMATCH` | `registerPrivateViewKey`: another private key is registered for the account (2.12.0) | | `REGISTRATION_PENDING` | `registerPrivateViewKey`: this wallet's registration is not confirmed yet (2.12.0) | | `MALFORMED_TRANSACTION` | Transaction is malformed | | `SELF_TRANSFER` | Cannot send to yourself | | `SENDER_NOT_FOUND` | Sender address not found | | `INVALID_SIGNATURE` | Invalid transaction signature | | `DUPLICATE_TRANSACTION` | Duplicate transaction detected | | `NONCE_TOO_FAR` | Transaction nonce too far ahead | | `INTERNAL_ERROR` | Internal server error | | `NOT_CONNECTED` | The page has not connected; call `connect()` first | | `INVALID_PARAMS` | The wallet rejected the request parameters, for example two fields that name different recipients, methods or arguments | | `METHOD_NOT_ALLOWED` | `rpcCall` with a method outside the read-only allow-list | | `NOT_AVAILABLE` | Not served by the wallet over the bridge (`encryptBalance`, `decryptBalance`), or a private method while 0xio Desktop is on another network | | `PRIVATE_PROOF_FAILED` | A private primitive failed inside the wallet | | `PRIVATE_TRANSFER_FAILED` | The private transfer was refused or failed | | `CONTRACT_CALL_FAILED` | The contract call failed | | `SIGN_FAILED` | Transaction signing failed | | `RECIPIENT_NOT_REGISTERED` | The recipient has no private view key on-chain | | `UNSUPPORTED_METHOD` | The wallet has no answer for this method (0xio Desktop: `getPendingPrivateTransfers`) | | `NETWORK_CHANGED` | The site's network or wallet moved while the request waited for review; nothing was sent | | `WALLET_CHANGED` | A private method while 0xio Desktop has another wallet open than the one this site uses | | `UNKNOWN_ERROR` | Unexpected error occurred | ## Framework Integration ### React Hook ```typescript theme={null} import { useEffect, useState, useCallback } from 'react'; import { ZeroXIOWallet, Balance, ConnectEvent } from '@0xio/sdk'; // Singleton instance to prevent multiple listeners let walletInstance: ZeroXIOWallet | null = null; export function useWallet() { const [wallet, setWallet] = useState(walletInstance); const [address, setAddress] = useState(null); const [balance, setBalance] = useState(null); const [isConnected, setIsConnected] = useState(false); const [isReady, setIsReady] = useState(false); useEffect(() => { let mounted = true; async function init() { // Return existing instance if ready if (walletInstance) { if (mounted) { setWallet(walletInstance); setIsReady(true); walletInstance.getConnectionStatus().then(status => { if (status.isConnected && mounted) { setAddress(status.address || null); setIsConnected(true); } }); } return; } // Create new instance const sdk = new ZeroXIOWallet({ appName: 'My DApp', debug: true }); try { // Initialize (waits for extension) const ready = await sdk.initialize(); if (ready && mounted) { walletInstance = sdk; setWallet(sdk); setIsReady(true); // Check existing connection const status = await sdk.getConnectionStatus(); if (status.isConnected) { setAddress(status.address || null); setIsConnected(true); } } } catch (e) { console.error("SDK Init failed:", e); } } init(); // Setup listeners if wallet exists if (wallet) { const handleConnect = (event: ConnectEvent) => { if (!mounted) return; setAddress(event.data.address); setBalance(event.data.balance); setIsConnected(true); }; const handleDisconnect = () => { if (!mounted) return; setAddress(null); setBalance(null); setIsConnected(false); }; const handleAccountChanged = (event: any) => { if (!mounted) return; setAddress(event.data.newAddress); if (event.data.balance) setBalance(event.data.balance); }; wallet.on('connect', handleConnect); wallet.on('disconnect', handleDisconnect); wallet.on('accountChanged', handleAccountChanged); return () => { wallet.off('connect', handleConnect); wallet.off('disconnect', handleDisconnect); wallet.off('accountChanged', handleAccountChanged); }; } }, [wallet]); const connect = useCallback(async () => { if (!wallet) throw new Error("Wallet SDK not initialized"); try { const result = await wallet.connect({ permissions: ['accounts', 'public_transactions'] }); setAddress(result.address); setBalance(result.balance); setIsConnected(true); } catch (error) { console.error('Connection failed:', error); throw error; } }, [wallet]); // ... other methods (disconnect, signMessage, etc) return { wallet, address, balance, isConnected, isReady, connect }; } ``` ### Vue.js Composition API ```typescript theme={null} import { ref, onMounted, onUnmounted } from 'vue'; import { ZeroXIOWallet, Balance } from '@0xio/sdk'; export function useWallet() { const wallet = new ZeroXIOWallet({ appName: 'My DApp' }); const address = ref(null); const balance = ref(null); const isConnected = ref(false); const isReady = ref(false); onMounted(async () => { await wallet.initialize(); isReady.value = true; // Check for existing connection wallet.getConnectionStatus(); wallet.on('connect', (event) => { address.value = event.data.address; balance.value = event.data.balance; isConnected.value = true; }); wallet.on('disconnect', () => { address.value = null; balance.value = null; isConnected.value = false; }); wallet.on('balanceChanged', (event) => { balance.value = event.data.newBalance; }); }); onUnmounted(() => { wallet.removeAllListeners(); }); const connect = async () => { try { const result = await wallet.connect({ permissions: ['accounts', 'public_transactions'] }); address.value = result.address; balance.value = result.balance; isConnected.value = true; } catch (error) { console.error('Connection failed:', error); throw error; } }; const disconnect = async () => { await wallet.disconnect(); }; const signMessage = async (message: string) => { if (!isConnected.value) throw new Error('Not connected'); return await wallet.signMessage(message); }; return { wallet, address, balance, isConnected, isReady, connect, disconnect, signMessage }; } ``` ## TypeScript Definitions The SDK exports comprehensive TypeScript types: ```typescript theme={null} import type { // Core types WalletAddress, Balance, NetworkInfo, ConnectionInfo, ConnectOptions, SDKConfig, // Transaction types TransactionData, SignedTransaction, TransactionResult, TransactionFinality, // 'pending' | 'confirmed' | 'rejected' | 'dropped' TransactionHistory, Transaction, // Event types WalletEventType, WalletEvent, ConnectEvent, DisconnectEvent, AccountChangedEvent, BalanceChangedEvent, NetworkChangedEvent, TransactionConfirmedEvent, ErrorEvent, // Contract types ContractCallData, ContractViewCallData, ContractParams, // Private balance types PrivateBalanceInfo, PrivateTransferData, PendingPrivateTransfer, // Permission types Permission } from '@0xio/sdk'; // Key type definitions interface Balance { public: number; // Public balance in OCT private: number; // Private (FHE) balance in OCT total: number; // Combined balance currency: 'OCT'; // Currency symbol } interface TransactionData { to: string; // Recipient address amount?: string | number; // Raw micro-OCT, sent to the wallet as is amountOct?: string | number; // Amount in OCT, converted by the SDK. One of the two. message?: string; // Optional memo feeLevel?: 1 | 3; // 1 = standard, 3 = priority isPrivate?: boolean; // Use private transfer } interface NetworkInfo { id: string; // 'mainnet' | 'devnet' | 'custom' name: string; // Display name rpcUrl: string; // RPC endpoint 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; // Whether this is a test network } // Contract parameter types type ContractParams = ReadonlyArray; type Permission = // Wallet scope names | 'accounts' | 'public_transactions' | 'contract_calls' | 'contract_views' | 'private_balance_read' | 'private_proofs' | 'private_transfers' | 'private_claims' // Older names, translated at connect | 'read_address' | 'read_balance' | 'read_public_key' | 'send_transactions' | 'sign_messages' | 'view_private_balance' | 'view_encrypted_balance' | 'encrypt_balance' | 'decrypt_balance' | 'stealth_scan' | 'stealth_claim'; ``` ## Utility Functions The SDK exports useful utility functions: ```typescript theme={null} import { // Validation isValidAddress, isValidAmount, isValidMessage, isValidFeeLevel, isValidNetworkId, // Formatting formatOCT, formatAddress, formatTimestamp, formatTxHash, // Conversion toMicroOCT, fromMicroOCT, octToMicro, // exact string conversion, at most 6 decimals // Permissions toWalletPermissions, withLegacyAliases, // Address derivation deriveOctraAddress, // async: base64 Ed25519 pubkey to an "oct..." address // Error utilities createErrorMessage, isErrorType, // Async utilities delay, retry, withTimeout, // Browser utilities isBrowser, checkBrowserSupport, // Development utilities generateMockData, createLogger } from '@0xio/sdk'; // Examples if (isValidAddress('oct1...')) { console.log('Valid address'); } const formatted = formatOCT(123.456789, 2); // "123.46" const shortAddr = formatAddress('oct1abc...xyz'); // "oct1ab...xyz" const micro = toMicroOCT(1.5); // "1500000" const exact = octToMicro('10.5'); // "10500000" const oct = fromMicroOCT(1500000); // 1.5 // Derive an Octra address from a base64 Ed25519 public key const addr = await deriveOctraAddress(publicKeyBase64); // "oct..." await delay(1000); // Wait 1 second ``` ## SDK Compatibility ```typescript theme={null} import { SDK_VERSION, MIN_EXTENSION_VERSION, MIN_EXTENSION_VERSION_DEVNET, SUPPORTED_EXTENSION_VERSIONS, checkSDKCompatibility } from '@0xio/sdk'; console.log('SDK Version:', SDK_VERSION); // "2.12.0" console.log('Min Extension (Mainnet):', MIN_EXTENSION_VERSION); // "2.0.1" console.log('Min Extension (Devnet):', MIN_EXTENSION_VERSION_DEVNET); // "2.2.1" console.log('Supported:', SUPPORTED_EXTENSION_VERSIONS); // ">=2.0.1" // Check browser compatibility const { compatible, issues, recommendations } = checkSDKCompatibility(); if (!compatible) { console.log('Issues:', issues); console.log('Recommendations:', recommendations); } ``` ## 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 * Check `canSign()` after connecting and on `accountChanged`; show a read-only state for a watch-only wallet ### 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()` > [!TIP] > > For production dApps, implement comprehensive error handling and user feedback for all wallet operations. # Wallet protocol 2 Source: https://docs.0xio.xyz/developers/wallet-protocol What a wallet implements to answer @0xio/sdk 2.12.0, and the conformance suite that checks it. For teams building a wallet that sites reach through `@0xio/sdk`. Every 0xio wallet (extension, 0xio Desktop, the 0xio app) runs the same conformance suite against these rules. ## Messages | | Extension and app (postMessage) | 0xio Desktop (local WebSocket) | | - | - | - | | Request | `{ id, method, params, timestamp, v: 2, rid? }` | `site_request { id, network, rid?, request }` | | Hello | `wallet_hello { sdk, protocols: [1, 2] }`, answered `{ protocol: 2, kind, version, capabilities }` | `protocol` and `capabilities` in the `site_hello` reply | | Status | `request_status { rid }` | `site_status { rid }` | | Cancel | `cancel_request { rid }` | `site_cancel { id?, rid }` | | Event | `requestUpdate` with the request's status | `site_event` kind `request` | | Presence | Answer `0xio:requestWallet` with a `0xio:announceWallet` event: `{ protocol, kind, version, name }` | none | * `rid` is 32 lowercase hex and rides only on methods that open a review or sign. Key records by the page's origin as the browser reports it, never by params. * An answer to a request with a `rid` carries it. An error answer carries `rid` and `status` at the top level and again in `error.details` as `{ requestId, status }`. * The announce and `wallet_hello` carry no user state: no address, network, lock state or connection. * "Same request" is sha256 of the canonical JSON of `{ method, network, params }`. `network` is the frame's network on 0xio Desktop and `null` on postMessage channels. ## Rules * **Idempotency.** The same request under a known `rid` joins it or gets the stored outcome; another request under it is `REQUEST_ID_REUSED`. A final failure never runs again. * **Statuses.** Only the allowed moves; a final status never changes; records live 24 hours. * **Scope.** Events go only to the tab and frame that sent the request. A site that is not connected sees `review_open` for `locked`, and no address or network. * **Timeouts.** A page that stops waiting never cancels the review. The review window is 5 minutes, then `expired` with `USER_REJECTED`. * **Page close.** An unanswered review closes with its page; an approved request finishes and keeps its outcome. * **Limits.** Over 20 requests a second, answer `RATE_LIMITED` for each extra one, never drop it. Over the open request cap (8, or a lower cap you declare), answer `RATE_LIMIT_EXCEEDED`. * **Pre-signed.** `broadcast_only` from the connected wallet is not reviewed again; from another wallet, `INVALID_PARAMS`. * **Registration.** `register_private_view_key` follows the [contract on the request lifecycle page](/developers/request-lifecycle#registering-a-private-receive-key). ## Conformance suite The suite lives in the SDK repository under [`conformance/`](https://github.com/0xio-xyz/0xio-sdk/tree/main/conformance). It speaks only the wire, so a wallet passes only by following the rules itself. * `harness.ts`: the adapter your test harness implements (open a page, send, receive events, close the page, approve or reject the review, and optional clock, chain, lock and account hooks). * `suite.ts`: the cases. Register them with `defineConformanceSuite({ name, features, create })` under Jest or Vitest. * `vectors.json`: request id format, digest bytes, transitions, masking and limits for `cargo test`, XCTest and JUnit. * `reference/`: a reference wallet that passes every case, to read as the executable spec. The suite's README has the exact steps to run it from a wallet repository. # The Octra Network Source: https://docs.0xio.xyz/introduction/octra-network Overview of the Octra Network, a Layer-1 blockchain that uses Hypergraph Fully Homomorphic Encryption to keep balances and state private. Octra is a **privacy-preserving Layer-1 blockchain** built from the ground up to execute **encrypted computation on-chain**. Unlike traditional blockchains, where data is either fully public or hidden using external techniques (ZK proofs, MPC, or trusted hardware), Octra allows: * Transactions * Smart contract logic * Onchain state to remain **encrypted at all times**, while still being executed and verified by the network. > [!NOTE] > > **In short:** Octra computes on data it cannot see. 0xio is designed specifically to expose this capability safely to end users. ## Fully Homomorphic Encryption (FHE) Fully Homomorphic Encryption (FHE) is a cryptographic primitive that allows arbitrary computation to be performed directly on encrypted data, without decryption. On Octra, this enables: * Validators to process transactions without seeing balances or inputs * Smart contracts to execute logic over private state * Verifiable outputs with no data leakage * User data privacy enforced by cryptography, not trust This is not privacy through obfuscation or offchain assumptions. It is **privacy enforced at the execution layer**. ## Hypergraph FHE (HFHE) Traditional FHE is prohibitively slow. Octra addresses this with **HFHE (Hypergraph Fully Homomorphic Encryption)**. HFHE differs fundamentally from lattice-based FHE approaches by: * Mapping encrypted data onto **hypergraph incidence structures** * Keeping encryption noise **localized**, not global * Enabling massive parallel execution and bootstrapping * Remaining CPU-friendly without specialized hardware ### What This Unlocks * Parallel encrypted logic gate evaluation (AND / OR / XOR) * Sub-10ms encrypted bootstrapping * Practical encrypted throughput at Layer-1 scale 0xio abstracts these mechanics so users and developers benefit from encrypted execution **without interacting with cryptography directly**. ## Validator Work: Real Computation, Not Waste Octra does not rely on hash-based mining. Validators are rewarded for performing the **actual encrypted computation** required by the network: * Executing encrypted transactions * Running encrypted application logic * Maintaining encrypted state * Participating in validation and key management tasks There is no expendable work. > [!NOTE] > > **Execution itself is the work being secured.** ## Circles: Encrypted Execution Environments Octra introduces **Circles**, isolated, on-chain execution environments that function like encrypted backend services. Within a Circle, developers can: * Deploy complex application logic * Run code written in **Rust, C++, or WASM** * Store and compute over encrypted state * Control access without exposing raw data Circles are best understood as: **Private, verifiable, on-chain compute containers** They scale independently, remain isolated, and inherit encrypted execution by default. ## Networks Octra currently operates two public networks: | Network | Purpose | FHE Support | Explorer | | - | - | :-: | - | | **Mainnet** | Production network | Yes | [octrascan.io](https://octrascan.io) | | **Devnet** | Development and testing | Yes | [devnet.octrascan.io](https://devnet.octrascan.io) | **Devnet** is the primary testing ground for FHE-powered features: encrypted balances, private transfers, smart contract development, and the full encrypt/decrypt/claim flow. All 0xio products (extension, mobile app, desktop) support switching between networks. ## How 0xio Fits In 0xio sits **above** Octra's execution layer. * Octra provides encrypted computation * 0xio provides usable interfaces, wallets, SDKs, and applications The 0xio product suite includes: * **[Browser Extension](/products/extensions)**: Primary gateway for web-based interaction * **[Mobile Wallet](/products/mobile)**: Privacy on the go with biometric security * **[Desktop Wallet](/products/desktop)**: Native PVAC engine that accelerates FHE operations * **[DEX](/products/dex)**: Cross-chain swaps and same-chain CLMM pools * **[Bridge](/products/bridge)**: Trustless OCT to wOCT bridge between Octra and Ethereum * **[SDK](/developers/sdk-guide)**: TypeScript SDK for DApp integration Users interact with 0xio. 0xio securely interfaces with Octra. # Official Links Source: https://docs.0xio.xyz/introduction/official-links Official 0xio resources including the website, documentation, ecosystem page, privacy policy, and verified community channels for support. ## Website & Documentation > [card] [0xio Website](https://0xio.xyz) > > Official website and landing page > [card] [Documentation](https://docs.0xio.xyz) > > Official documentation (you are here) > [card] [Ecosystem](https://0xio.xyz/ecosystem) > > Explore the 0xio ecosystem > [card] [Privacy Policy](https://0xio.xyz/privacy) > > Our privacy policy and data practices *** ## Products & Downloads > [card] [Install 0xio](https://0xio.xyz/install) > > Every client in one place: browser extension, desktop (macOS and Windows), mobile (iOS and Android) and the SDK > [card] [Browser Extension](https://chromewebstore.google.com/detail/0xio-wallet/anknhjilldkeelailocijnfibefmepcc) > > Install 0xio Wallet for Chrome, Brave, Edge, Opera, and Firefox (the Firefox version is in review on Mozilla Add-ons) > [card] [Telegram Bot](https://t.me/NullXio_bot) > > @NullXio_bot - Wallet monitoring and notifications > [card] [0xio DEX](https://dex.0xio.xyz) > > Cross-chain swaps and same-chain CLMM pools > [card] [0xio Bridge](https://bridge.0xio.xyz) > > Bridge between Octra and Ethereum, with stats at bridge.0xio.xyz/stats > [card] [Atlas](https://atlas.0xio.xyz) > > Blockchain visualization and analytics *** ## Infrastructure > [card] [RPC Proxy](https://rpc-proxy.0xio.xyz) > > rpc-proxy.0xio.xyz: optional Octra RPC proxy with history compression, used only when you turn it on > [card] Push Server > > Internal push notification server for mobile wallet alerts > [card] [Indexer](https://indexer.0xio.xyz) > > indexer.0xio.xyz: Chain indexing service for the 0xio ecosystem *** ## Developer Resources > [card] [GitHub](https://github.com/0xio-xyz) > > Official GitHub organization > [card] [0xio SDK](https://www.npmjs.com/package/@0xio/sdk) > > @0xio/sdk on npm > [card] [0xio PVAC](https://www.npmjs.com/package/@0xio/pvac) > > @0xio/pvac: WASM privacy primitives on npm > [card] [Token Lists](https://github.com/0xio-xyz/token-lists) > > Community-maintained token list repository *** ## Social & Community > [card] [X (Twitter)](https://x.com/0xio_xyz) > > @0xio_xyz - Follow for updates and announcements *** ## Contact > [card] General Inquiries > > [team@0xio.xyz](mailto:team@0xio.xyz) > [card] Founder > > [gery@0xio.xyz](mailto:gery@0xio.xyz) > [!WARNING] > > **Security Notice:** Only trust links from this official documentation. 0xio Labs members will never ask for your recovery phrase, private keys, or passwords. *** ## Quick Reference | Resource | Link | | - | - | | Install | [0xio.xyz/install](https://0xio.xyz/install) | | Website | [0xio.xyz](https://0xio.xyz) | | Documentation | [docs.0xio.xyz](https://docs.0xio.xyz) | | Browser Extension | [Chrome Web Store](https://chromewebstore.google.com/detail/0xio-wallet/anknhjilldkeelailocijnfibefmepcc) | | Telegram Bot | [@NullXio_bot](https://t.me/NullXio_bot) | | 0xio DEX | [dex.0xio.xyz](https://dex.0xio.xyz) | | 0xio Bridge | [bridge.0xio.xyz](https://bridge.0xio.xyz) | | Bridge Stats | [bridge.0xio.xyz/stats](https://bridge.0xio.xyz/stats) | | 0xio Oracle | [oracle.0xio.xyz](https://oracle.0xio.xyz) | | 0xio Indexer | [indexer.0xio.xyz](https://indexer.0xio.xyz) | | Atlas | [atlas.0xio.xyz](https://atlas.0xio.xyz) | | SDK | [@0xio/sdk](https://www.npmjs.com/package/@0xio/sdk) | | PVAC | [@0xio/pvac](https://www.npmjs.com/package/@0xio/pvac) | | GitHub | [github.com/0xio-xyz](https://github.com/0xio-xyz) | | Twitter/X | [@0xio_xyz](https://x.com/0xio_xyz) | | Ecosystem | [0xio.xyz/ecosystem](https://0xio.xyz/ecosystem) | | Privacy Policy | [0xio.xyz/privacy](https://0xio.xyz/privacy) | | Token Lists | [github.com/0xio-xyz/token-lists](https://github.com/0xio-xyz/token-lists) | | RPC Proxy | [rpc-proxy.0xio.xyz](https://rpc-proxy.0xio.xyz) (optional) | | General Email | [team@0xio.xyz](mailto:team@0xio.xyz) | # What is 0xio? Source: https://docs.0xio.xyz/introduction/overview Learn about 0xio, the user-facing application layer for Octra Network that brings encrypted balances, private transfers, and FHE tools to users. **0xio** is the **user-facing application layer** built for the **Octra Network**, designed to make encrypted blockchain computation usable, safe, and intuitive. While Octra provides the underlying Layer-1 blockchain and cryptographic infrastructure, **0xio delivers the wallets, SDKs, and applications** that allow users and developers to interact with Octra's fully encrypted execution model without needing to understand or implement cryptography themselves. ## What is 0xio? 0xio is a **non-custodial Web3 ecosystem** built on top of the Octra Network. It provides: * Secure client-side applications (wallets, SDKs, interfaces) * Abstractions over encrypted execution and fees * Tooling for private transfers, private trading, and encrypted application logic All sensitive data is encrypted **locally on the user's device** before ever touching the network. 0xio never has access to user keys, balances, or private transaction data. > [!WARNING] > > **Important:** 0xio is an independent, community-built project. It is not operated by, nor formally affiliated with, Octra Labs. ## Why 0xio? On most blockchains, privacy is optional and usually external: * Balances are public * Transactions are globally visible * Activity can be trivially tracked and correlated 0xio takes a different approach. By building directly on Octra's encrypted execution layer, 0xio enables: * **Private balances whenever you want them** * **Encrypted transactions validated without revealing amounts** * **Applications that operate on private state, not public data** The result is a blockchain experience closer to traditional finance privacy, **but without custodians, intermediaries, or trusted servers.** ## Core Value Proposition ### 1. Privacy by Default 0xio integrates Octra's **Hypergraph Fully Homomorphic Encryption (HFHE)** so balances, transfers, and application logic can remain encrypted throughout their lifecycle. ### 2. Non-Custodial Sovereignty All keys are generated and stored locally. 0xio cannot freeze, access, or recover user funds. ### 3. Client-Side Encryption Sensitive data is encrypted **before** it is broadcast to the network. Validators process encrypted payloads without learning their contents. ## Architecture The 0xio ecosystem spans multiple client platforms unified by a shared SDK layer, backed by infrastructure services, all interfacing with the Octra Network's FHE-enabled blockchain. ### Client Applications > [card] Extension v3.0.4 > > React, Vite and WASM PVAC, with an EVM wallet > [card] Mobile v1.3.3 > > React Native, Expo 57 and native PVAC > [card] Desktop v0.6.0 > > Tauri 2, Rust and native PVAC > [card] DEX > > React + Vite + CLMM + Cross-chain ### Developer SDK > [card] [@0xio/sdk v2.12.1](https://www.npmjs.com/package/@0xio/sdk) > > TypeScript SDK on npm. Connects dApps to 0xio wallets and the Octra Network. Private MessageChannel transport, security-hardened postMessage, pluggable wallet adapters, full type definitions. Privacy primitives ship separately as `@0xio/pvac` (WASM). ### Infrastructure > [card] RPC Proxy > > Optional caching proxy for Octra RPC that compresses heavy history responses. Wallets use it only when you turn it on. No key needed, with a per-IP limit. > [card] [Oracle](/developers/oracle-api) > > OCT and token prices, EVM address data for the wallets and bridge analytics. Reads need no key, with a per-IP limit. > [card] Push Server > > Real-time transaction notifications with signature authentication, replay protection, and per-address rate limiting. > [card] [Install and updates](https://0xio.xyz/install) > > Every client from one page at 0xio.xyz/install. Desktop builds and minisign-verified updates are served through 0xio.xyz. ### Blockchain Layer > [card] Octra Network > > FHE-enabled blockchain with AML smart contracts, BridgeVault for OCT-wOCT bridging, and on-chain contract verification. Supports both Mainnet and Devnet. | Layer | Components | | - | - | | **Client Applications** | Browser Extension (React, Vite, WASM PVAC, EVM wallet), Mobile App (React Native, Expo 57, native PVAC), Desktop App (Tauri 2, Rust, native PVAC), DEX (React, Vite, CLMM and cross-chain swap), Bridge (bridge.0xio.xyz) | | **Developer SDK** | @0xio/sdk v2.12.1 (TypeScript, npm, MessageChannel transport, postMessage security, request lifecycle); @0xio/pvac v1.0.2 WASM privacy primitives | | **Infrastructure** | RPC Proxy (optional, caching, history compression), Oracle (prices, EVM data, bridge analytics), Push Server (signature-authenticated notifications), install page and desktop updates at 0xio.xyz | | **Blockchain** | Octra Network: FHE encrypted balances, AML smart contracts, BridgeVault, wOCT (ERC-20), on-chain proof verification | ## The 0xio Ecosystem > [card] [Browser Extension](/products/extensions) > > Non-custodial wallet for Chrome, Brave, Edge, Opera and Firefox. Public and private OCT, an EVM wallet and a built-in bridge. > [card] [Desktop Wallet](/products/desktop) > > Native Tauri and Rust wallet with a menu bar panel and a native privacy engine for faster private operations. > [card] [Mobile Wallet](/products/mobile) > > Privacy on the go with biometric security and mobile-optimized features. > [card] [Developer SDK](/developers/sdk-guide) > > TypeScript SDK for integrating dApps with 0xio and Octra's FHE capabilities. > [card] [0xio DEX](/products/dex) > > Cross-chain swap protocol and same-chain CLMM pools for the Octra Network. > [card] [0xio Bridge](/products/bridge) > > Bridge to Ethereum, Bridge to Octra and Any chain to OCT, secured by a light client. > [card] [Telegram Bot](/products/telegram-bot) > > Real-time wallet monitoring and notifications via Telegram. # How 0xio and Octra Work Source: https://docs.0xio.xyz/introduction/technology Learn how Octra works with HFHE encryption, how 0xio signs and encrypts transactions locally, and the cryptographic specs behind the wallet. 0xio is built on **Octra**, so it helps to know how Octra works. ## What is Octra? **Octra** is a Layer-1 blockchain network distinguished by its use of **Hypergraph Fully Homomorphic Encryption (HFHE)**. Unlike traditional blockchains where transparency is mandatory, Octra enables privacy while maintaining decentralized verification. ### Key Concepts * **Fully Homomorphic Encryption (FHE):** Traditional encryption requires you to decrypt data to process it (exposing it to potential leaks). FHE allows the network to perform calculations on *encrypted* data without ever decrypting it. The result remains encrypted, and only you (the key holder) can decrypt the final state. * **Compute-Coupled Validation:** Instead of solving arbitrary hash puzzles (like Bitcoin's Proof-of-Work), Octra validators utilize their computing power to process actual FHE tasks. They are rewarded for executing useful encrypted computations that advance the network state. * **Isolated Execution Environments (Circles):** Octra allows developers to run "Circles": independent execution environments that function similarly to private servers on the blockchain, isolating state for performance and privacy. ## How 0xio Fits In 0xio acts as the **Client-Side Encryption & Signing Layer** for Octra. Because Octra relies on FHE, data must be encrypted *before* it leaves your device. If raw data were sent to the network, privacy would be compromised. 1. **Local Encryption & Signing:** When you initiate a transaction, 0xio encrypts the payload locally using your specific encryption keys and Octra's public parameters. It then **signs** the transaction with your private key to prove authority without revealing the data. 2. **Broadcasting:** The encrypted blob (ciphertext) is signed and sent to the Octra network. 3. **Encrypted Execution:** Octra validators process the state change (e.g., updating a balance) on the encrypted data. They verify the transaction's validity mathematically without ever seeing the plaintext amounts. 4. **Decryption:** You use your 0xio wallet to view your new balance. The wallet decrypts the on-chain state locally to reveal the numbers to you. ## 0xio Security Specs 0xio is designed with strict client-side security standards to ensure non-custodial safety. | Feature | Specification | | :- | :- | | **Recovery Phrase** | **BIP-39** 12-word mnemonic. The seed derives a master key via `HMAC-SHA512("Octra seed", seed)` (no BIP-44 path). | | **Signing Algorithm** | **Ed25519** (tweetnacl) for high-speed, secure transaction signatures. Your address is `SHA-256(public key)` encoded as base58 with an `oct` prefix. | | **Vault Encryption** | **AES-256-GCM**. Your keys are encrypted at rest on your device. | | **Key Derivation** | **PBKDF2-HMAC-SHA256** with high iteration counts: 900,000 (extension), 600,000 (desktop), and 100,000 for the mobile PIN. | | **Privacy Engine** | **PVAC** (curve25519-dalek / Ristretto255) for FHE encrypt/decrypt, Bulletproofs range proofs, Pedersen commitments, and stealth transfers via X25519 ECDH. | | **EVM Keys** | The extension derives the EVM key from the same phrase at `m/44'/60'/0'/0/i` (secp256k1) for one signature at a time. | | **Price Feeds** | **0xio Oracle**: the live wOCT/ETH Uniswap v4 pool price in USD, cross-checked against CoinGecko and DexScreener, with anomaly detection and PostgreSQL history. EVM token prices in the extension come from DefiLlama. | | **Telemetry** | No analytics, advertising or tracking scripts. The extension has no crash reporting; the desktop and mobile apps send crash reports without keys, phrases or PINs. See the [Privacy Policy](https://0xio.xyz/privacy). | > [!NOTE] > > 0xio is an independent project built by community developers. It is not owned by, nor formally affiliated with, Octra Labs, ensuring a separation between the network and the toolset. # 0xio Bridge Source: https://docs.0xio.xyz/products/bridge Bridge between Octra and Ethereum: Bridge to Ethereum (OCT to wOCT), Bridge to Octra (wOCT to OCT) and Any chain to OCT, on bridge.0xio.xyz and inside the 0xio extension. > [!NOTE] > > **Status: Live on Mainnet.** Use the bridge at [bridge.0xio.xyz](https://bridge.0xio.xyz) or from **Bridge** inside the [0xio browser extension](/products/extensions). ## Overview The **0xio Bridge** moves OCT between the **Octra Network** and **Ethereum**, where it is held as **wOCT** (an ERC-20 token). 1 OCT always equals 1 wOCT. | Route | From | To | | - | - | - | | **Bridge to Ethereum** | OCT on Octra | wOCT on Ethereum | | **Bridge to Octra** | wOCT on Ethereum | OCT on Octra | | **Any chain to OCT** | A token on any chain Relay supports | OCT on Octra | The bridge uses a **light client**: an Octra light client contract on Ethereum verifies Merkle proofs of Octra transactions, so a claim on Ethereum does not depend on a trusted oracle. ## How It Works ### Bridge to Ethereum (OCT to wOCT) **1. Connect wallets** Connect your 0xio wallet (Octra) and an EVM wallet. In the 0xio extension both are the same wallet. **2. Bridge to Ethereum** Send OCT to the bridge on Octra with your Ethereum address. The bridge holds your OCT until wOCT is claimed. **3. Wait for Octra's proof on Ethereum** The bridge relayer posts Octra epoch roots to the light client on Ethereum. Your transfer becomes claimable once the light client reaches its epoch. There is no fixed waiting time; the bridge shows the live wait. **4. Claim on Ethereum** Claim wOCT on Ethereum with a Merkle proof. Anyone can submit the claim, but only the address you named receives the wOCT. ### Bridge to Octra (wOCT to OCT) **1. Allow wOCT** Allow the bridge contract on Ethereum to use the amount of wOCT you are bridging. This is skipped when the allowance already covers it. **2. Bridge to Octra** wOCT leaves Ethereum with your Octra address as the recipient. **3. Arrives on Octra** The bridge relayer sees the transfer and the same amount of OCT arrives at your Octra address, usually within a minute. The arrival is confirmed on chain, not assumed after a timer. ### Any Chain to OCT Pay with a token on any EVM chain that Relay supports. Relay swaps it into wOCT on Ethereum and bridges it to your Octra address in one fill. No 0xio contract is involved. * Search tokens and chains by name or symbol, or paste a contract address. * Type what you bridge, or the OCT you want to get; the other field shows the live quote. * Before your wallet signs, the order is checked against Relay's own chain list: the OCT goes to your Octra address for the amount shown, the payment stays within what you entered, and refunds come back to you. Anything else is refused. * A 0.25% 0xio fee applies to this route only. It is shown in **Fees and gas** before you confirm. ## Bridge Inside the Extension The 0xio browser extension bridges without leaving the wallet, using the wallet's own Octra and 0x addresses. The bridge works on mainnet only. ![Bridge in the extension with OCT to wOCT, the receiving address and the Claim pill](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/bridge.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=f7fce693dc180331a90a76aa31478486) Bridge in the extension * **Both directions and Any chain to OCT,** each step with its own review. Bridge to Ethereum signs the live Octra network fee shown on the review, and asks again if the network's minimum rises. * **Claim page:** the **Claim** pill in the Bridge header opens your transfers to Ethereum in **Ready**, **In progress** and **Completed**, with a count on each and the wait shown as hours left. The pill counts the transfers ready to claim. ![Claim page with transfers ready to claim on Ethereum](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/bridge-claim.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=30824c974a7668f791808cd3955da573) The Claim page (sample transfers) * **Find my transfers** searches Octra and Ethereum for this wallet's bridge transfers. **Add by hash** takes an Octra or Ethereum transaction hash. The first time Bridge opens on a wallet, its transfers are read back from the chains. * **A transfer to Octra that was allowed but not sent** is offered again from the form. * **Wallets without an EVM key** (imported from an Octra private key) can bridge to Ethereum by typing a 0x address. The claim, Bridge to Octra and Any chain to OCT need an EVM wallet, so they are done on [bridge.0xio.xyz](https://bridge.0xio.xyz) with any EVM wallet; the Claim page says so and links there. * **Watch-only wallets** cannot bridge. * If a send may have reached the node but the reply was lost, the wallet follows it on Octra and asks you to check Claim before trying again, never that it was not sent. ## Bridge Stats [bridge.0xio.xyz/stats](https://bridge.0xio.xyz/stats) shows transfers made on the bridge site and from **Bridge** in the 0xio extension: * **Net to Ethereum:** OCT bridged to Ethereum minus OCT bridged back to Octra, over 7 days, 30 days or all time. * **Weekly counts** of transfers to Ethereum, transfers to Octra and claims on Ethereum. * **Totals:** total and 24 hour volume, transactions and wallets (Octra and Ethereum). * **By client:** Web (the bridge site) and 0xio Extension, each with its total bridged and transactions. * **By route:** Octra to Ethereum, Ethereum to Octra and Any chain to Octra, each with total bridged, the last 24 hours and transactions. * **Recent bridge events** with time (UTC), type, from, to and amount. ![Bridge analytics with the net to Ethereum chart, totals and weekly counts](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/bridge/stats.jpg?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=931818e0844fe5aae6570ce901f7118b) bridge.0xio.xyz/stats The stats count a transfer only after the 0xio oracle has found it on chain. See [Bridge Analytics](/developers/oracle-api#bridge-analytics) for the API. ## Features * **Light client verification:** claims are checked on Ethereum against Octra epoch roots, with no trusted oracle * **Multi-wallet support:** EIP-6963 detection for MetaMask, Rabby, Phantom and other EVM wallets * **WalletConnect:** connect the 0xio mobile app by scanning the QR code * **Resumable transfers:** the current transfer stays at the top of History with its Claim button and resumes after a refresh, so the page can be closed while you wait * **Honest states:** a transfer reads Complete only after the claim receipt or the confirmed arrival on Octra * **Bridge history:** claim actions, recovery from the relayer feed and lookup by hash ## Contract Addresses | Contract | Network | Address | | - | - | - | | BridgeVault | Octra | `oct5MrNfjiXFNRDLwsodn8Zm9hDKNGAYt3eQDCQ52bSpCHq` | | wOCT (ERC-20) | Ethereum | `0x4647e1fE715c9e23959022C2416C71867F5a6E80` | | EthereumBridge | Ethereum | `0xE7eD69b852fd2a1406080B26A37e8E04e7dA4caE` | | OctraLightClient | Ethereum | `0xC01cA57dc7f7C4B6f1B6b87B85D79e5ddf0dF55d` | ## Limits | Parameter | Value | | - | - | | Minimum to Ethereum | 1 OCT | | Rate | 1 OCT = 1 wOCT (always) | | Per-transfer and daily limits | Set on the bridge contract and read before every review. A claim over the daily limit becomes claimable the next day | | Pause | Either side can be paused; the bridge says so and keeps your OCT in your wallet | ## Fees | Route | 0xio fee | | - | - | | Bridge to Ethereum and Bridge to Octra | None (network fees only) | | Any chain to OCT | 0.25%, collected by Relay and shown in Fees and gas before you confirm | ## Architecture ``` User (0xio wallet + EVM wallet) | +-- Octra: BridgeVault (holds OCT bridged to Ethereum) | +-- Ethereum: EthereumBridge + wOCT ERC-20 | +-- OctraLightClient (Merkle proof verification) | +-- Bridge relayer (posts epoch roots, delivers OCT to Octra) ``` ## Services | Service | URL | Role | | - | - | - | | Bridge | [bridge.0xio.xyz](https://bridge.0xio.xyz) | Bridge interface | | Bridge Stats | [bridge.0xio.xyz/stats](https://bridge.0xio.xyz/stats) | Volume, wallets and activity | | Oracle | [oracle.0xio.xyz](https://oracle.0xio.xyz) | Prices and bridge event tracking | ## Known Limitations * **Epoch sync delay:** the light client on Ethereum can fall behind Octra. Until it catches up, new transfers to Ethereum are not claimable yet; the bridge keeps checking. * **Pruned epochs:** old epoch roots are pruned from the light client contract, so claim your wOCT promptly. ## Security * **No trusted oracle:** claims are verified on chain against the light client * **Pause:** the bridge can be paused during emergencies * **Limits:** per-transfer and daily limits slow any drain * **Account change detection:** the site notices wallet account switches so you never send to the wrong address > [!WARNING] > > Always check the full destination address on the review before bridging. The bridge shows the complete address, not a shortened one. # Desktop Wallet Source: https://docs.0xio.xyz/products/desktop The 0xio Desktop wallet for macOS and Windows: a native Octra wallet with a menu bar panel, a native privacy engine that speeds up the browser extension, and contract tools. The **0xio Desktop** is a native desktop application built with [Tauri](https://tauri.app/) (Rust backend and React frontend). It is a full wallet for the Octra Network, a menu bar panel for everyday sends and private operations, and a **native privacy engine** that builds proofs for the browser extension. > [!NOTE] > > **Status: Live (v0.6.0).** Download it from [0xio.xyz/install](https://0xio.xyz/install) for macOS (Apple silicon and Intel, macOS 13 or later, signed and notarized by Apple) or Windows. ## Why Desktop? Browser extensions run in a sandbox with limited access to the CPU and memory, and private proofs are heavy work. The desktop app runs **pvac-rs** natively with every CPU core, so private operations finish faster than in the browser, both in the app and for the extension when the two run together. *** ## Menu Bar Panel Click the 0xio icon in the menu bar for your wallet at a glance. The panel opens over other apps, including full-screen ones, on every Space and on the display you clicked. * **Balance** up front, hidden until you click it, with a live status line for the privacy engine. * **Send**, **Receive** (with a QR code), **Private** and **Lock**, and a wallet switcher. The **•••** menu holds the network, Lock now, Open at login, Open and Quit. * **Private:** encrypt or decrypt, claim private transfers, turn on a private balance, migrate it or update the encryption key, each with the live network fee. * **Locked:** unlock in the panel with Touch ID or your password. * The menu bar icon shows an outline while locked, a turning slash while a private proof runs, and the number of site requests waiting (else the transfers waiting to be claimed). ### Command Line The panel has a command line that reads loose input, for example `send 10 to 0xio privately`, `pay bob half` or `5 to oct8Hk...`. It accepts any word order and small typos, amounts such as max, half, 25% or 1.5k, and recipients by contact name, wallet name or address prefix. A live line says what Return will do. A command only fills the form: every send still opens its review. ![Menu bar panel with the balance hidden and a typed command with its live preview](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/desktop/menu-bar-command-line.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=3adafdcf6d84524a685f1f4a70139a34) The menu bar panel with a command typed ### Themes Choose the panel's look in **Settings > Appearance > Menu bar theme**: Match app, Original, Smoke, Frost, Octra, Clear, Phosphor, Ember or CLI. The **CLI** theme turns the panel into a terminal with no buttons. Type `0xio --help` for the commands (send, receive, qr, encrypt, decrypt, private, claim, migrate, lock, unlock, balance, status, wallets, use, network, theme, open, quit and more). It keeps history and tab completion, shows a review before anything is signed, and signs only after you type `y`. A review lapses when the wallet, network, lock state or fee changes before you answer. ![CLI theme showing a send review that waits for y](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/desktop/menu-bar-cli.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=36c54dbabeb3ee262052c661892c7258) The CLI theme with a send review ### Reviews and Fees * Every send, encrypt, decrypt, setup, migration and key update shows a review with the wallet, network and live network fee, then asks for Touch ID or your password. * The fee on the review is the fee signed, as long as the network still accepts it. You are asked to review again only when the network's minimum fee rises above it. * A fee that cannot be read blocks the action with **Network fee unavailable. Try again.** No default fee is ever signed. * An approved request runs only on the wallet and network its review showed. * A busy node gets the same signed transaction again (3 tries in all). A nonce that is already used sends the transaction back to review; nothing is re-signed on its own. A rejected or too large transaction says so. See [What Happens After You Send](/wallet/sending#what-happens-after-you-send). * A private send is offered only when the private balance is ready. A balance that is due for a migration, needs a key update or setup, or is waiting for the network shows its status, with **Migrate**, **Update** or **Turn on** where the app can do it. ### Sites Through the Extension A website can sign with the desktop wallet instead of the browser. In the extension's connect review, pick **Sign with: 0xio Desktop** and press **Continue**. The extension only carries the site's requests over the paired connection; every one is reviewed in the menu bar and signed by the desktop app. * **Arrival:** when nothing is waiting and the panel is closed, the first request opens the panel on its review. Later requests never take focus; they raise the count on the menu bar icon. * **Notifications:** while the panel is closed, a later request also shows one macOS notification for its site, for example "[https://app.octradex.xyz](https://app.octradex.xyz) wants you to sign a transaction". A site gets one at a time. Click it to open the panel on that request, or on the Requests list when more wait. macOS asks for permission the first time one is due. Turn them off in **Settings > Security & Privacy**, **Notify me about site requests**; if macOS has them off, the row says so with **Open settings**. * **Requests:** two or more waiting show the Requests list, grouped by site with its browser and **Reject all** per site. Repeat connect requests from one site merge into one row (for example 2 tabs). A review has a way back to the list; answering lands on the list, or closes the panel when nothing is left. A click outside closes the panel without answering. * **Every review:** Reject and the verb (Connect, Send, Sign, Switch, Claim, Deploy), the full site address with its scheme, the page around it when the site is in a frame, **Via 0xio extension (Chrome)** or **(Firefox)**, the wallet, and the live network fee for anything that pays one. Touch ID or your password follows for anything that signs. * **Everything signed is shown:** a contract call shows its exact arguments, a deploy its constructor params, and any memo in full. A contract sequence lists each step (contract, method, exact arguments and amount) with the OCT total. A connect review lists every permission the site gets, private ones included. * **Refused before review:** a sign-only request for anything but a standard transfer, a message or memo over 4,000 characters (never cut short) and contract params that are not an array. * **Locked:** the panel asks you to unlock first, then shows the review. * **Networks:** each site keeps its own network. A site's network switch is its own review and moves only that site. * **Limits:** at most 3 requests per site wait at once and a site is rate limited; a request beyond that gets an error at once. After you reject a review, that site's next requests are refused for 5 seconds without a review. A tab that closes or reloads takes its requests with it. Sites connected this way are listed in **Settings > Security & Privacy**, **Connected sites**, per network (Mainnet or Devnet), each with the browser it came through and the wallet it uses. **Disconnect** asks once more in place. Removing the extension in **Paired apps** first says how many sites it forgets and lists them in the panel. ### Sites Straight from the Browser A website can also reach the desktop wallet with no extension at all. When the site offers **0xio Desktop** and you press Connect, it opens a connection to the app on this computer; the browser names the site and the site cannot pretend to be another one. * **Same reviews:** every connect, sign, send and network switch opens in the menu bar exactly like a site through the extension, with **Reject** and the verb, the live fee, then Touch ID or your password. The review shows **Connection: Direct from browser** where a relayed site shows the extension. A locked wallet asks you to unlock first. * **What a site learns:** nothing about you until you approve its connect. After that it sees the wallet it uses, as with the extension. * **Chrome asks first:** the first time a website reaches the app, Chrome (and Edge) shows a prompt that the site wants to access apps and services on this device (**Apps on device**). Allow it for sites you use; Block stops that site from reaching the app. You can change it later in the site's settings in Chrome. * **Limits:** the same as through the extension (3 waiting requests per site, a rate limit), and a cap on how many pages can be connected at once. Closing a tab ends its waiting requests; the site stays connected and picks up again when you come back. * **Turn it off:** **Settings > Security & Privacy**, **Websites can connect without the extension** (on by default). Off refuses websites at once and closes the open ones; sites through the extension keep working. These sites are listed under **Connected sites** with **Direct from browser** and **Disconnect**. ### Lives in the Menu Bar Closing the window (the red button, Cmd+W or Cmd+Q) keeps 0xio Desktop running in the menu bar without a Dock icon. The wallet, auto-lock and proof work carry on. Open the window again from the menu bar, Finder, Spotlight or the Dock. **Quit** in the menu bar menu or panel quits the app. **Open at login** starts it quietly in the menu bar. *** ## Core Wallet Features ### Dashboard & Balances The home screen displays your public and private (FHE-encrypted) balances, recent transactions, and token holdings, the same as the extension and mobile app. * **Token page**: click a token on Home. The price (6 decimals), its change, the range tabs and a chart are on the left; move the pointer over the chart to read the price at that time. The panel on the right holds the balance, Send and Receive, and the token's own activity on this network and address. OCT shows its public and encrypted amounts as two rows under one total, with Encrypt and Decrypt. A token with no price shows its activity on the left and its contract (with Copy) and decimals in the panel; a token you added can be removed there ### Sending & Receiving * **Send screen**: the form on the left and a live panel on the right. Pick a recipient from the address book, your wallets or recent sends (each shows when you last sent), enter the amount with Max and the token, and add a message on public OCT. The panel shows both balances, the live network fee, who the recipient is to you (saved in, sent before, last send) and what is left after the send * **Review in place**: Review send locks the form and turns the panel into the review card (amount, from, to, network fee, total, balance after, message) with **Reject** and **Send**. Reject returns to editing with every value kept; Send reads the fee again, asks for Touch ID or your password, then shows Signing and Broadcasting, and Sent with the hash, View in History and New send * **Standard transfers**: Public on-chain transactions with memo support * **Private transfers**: FHE-encrypted stealth transfers (recipient must have a registered PVAC public key). The proofs run in the shared proof queue with five steps and the elapsed time of each; a private send pays its fee from the public balance, so the total shows a private line and a public line * **Bulk send**: rows in the same frame (add one, paste a list or import a CSV), totals in the panel, then one review listing every send in order and the progress in the same card. Duplicate addresses and a total over the balance are flagged. Private (FHE) bulk sends are available only in the desktop app and run as one job in the proof queue * **QR codes**: Generate and share receive addresses * **Exact token amounts**: token amounts in Send, bulk send and their reviews are exact whole units, never rounded * **OU control**: press the network fee to pick Slow, Normal, Fast or a custom fee. Every preset comes from the node's live quote, and Fast shows only when the node quotes it. Private operations have floors (5,000 ou, claims 3,000 ou); see [Network Fees](/wallet/sending#network-fees) * **Private send readiness**: a private send is offered only when the private balance is ready; otherwise the panel shows what comes first (turn on, refresh the key, migrate, a key update on hold) with its own review ### Privacy Operations Full encrypt, decrypt and claim support, same as the extension. The Privacy screen has a composer on the left with **Encrypt**, **Decrypt** and **Claim** tabs and a live **Proofs** panel on the right. Each operation is reviewed in the composer's place with **Reject** and the verb, then Touch ID or your password; the job then joins the proof queue and the composer is free for the next one. * **Proof queue**: one job at a time, in the order confirmed, so nonces and the balance's layers stay in order. The running job shows its step dots (encrypt has four steps; decrypt, claim and private send five) with the elapsed time of each step, then Up next and what finished today. A failed proof stays on top with its error, **Remove** and **Retry**, and the jobs behind it wait as Paused * **Claim**: waiting transfers oldest first, each with its own Claim. Any of them can be claimed, one per press, because each claim adds two layers; the review shows the sender, the live fee, the private balance and the layers after * **Turn on private balance**, **Refresh key** (at four or more of eight layers), **Migrate balance** and a **key update on hold** each show in the composer with the wallet, network and live fee. A changed fee, wallet or network is refused before any proof work * **Proof rules**: private operations follow the proof rules the network states, read again before each submit. A network that does not state them gets no private operation, and a change in between sends nothing. See [Network Proof Rules](/wallet/privacy-operations#network-proof-rules) **1. Encrypt (Public → Private)** Shield funds using FHE. Your device generates a Pedersen commitment and zero-knowledge bound proof locally. **2. Decrypt (Private → Public)** Unshield funds back to your public balance. **3. Claim** Scan the blockchain for incoming private transfers and decrypt them into your wallet. ### Multi-Wallet Support Create or import multiple wallets using 12/24-word seed phrases. All wallets use the same master root key derivation (`HMAC-SHA512("Octra seed", seed)[0:32]`) as the extension and mobile app, so importing the same seed phrase produces the same address across all 0xio platforms. *** ## NFTs & Marketplace ### NFT Gallery * Every collection the wallet holds shows without an import step, including collections on the 0xio NFT list that the node does not list. **Import** stays for any other contract. * The newest piece leads, then one shelf per collection, newest first. * Hide a collection or a single piece, with Undo for four seconds. Hidden ones wait on the **Hidden** shelf, kept per network and wallet. * A piece opens as a page with its status, who holds it and two actions. ### Living NFTs Living NFTs are on-chain programs that draw their own art. The detail view runs the program in an isolated frame with no access to the wallet. A program that reads the chain may reach only its own network's RPC; gallery thumbnails stay offline. See [NFT Standards](/wallet/nft-standards#living-nfts-euint-labs-geodesics-and-chain-state-collections). ### Xpectrum Marketplace List and manage your NFTs on the Xpectrum marketplace from the wallet: * **List for sale**: two-signature listing flow with on-chain verification * **Change price**: cancels, approves again when needed and lists again, with every signature shown on the review first * **Cancel listing**: delist an NFT at any time *** ## Companion Mode: Accelerating the Extension The desktop app runs a **local WebSocket server** on `ws://127.0.0.1:19345` that the browser extension connects to automatically when both are running. ### How It Works 1. The extension detects the desktop app on the local machine, and both prove the pairing secret to each other (see Pairing with the Extension) 2. Only then the extension sends the PVAC master seed, a one-way hash of the wallet key, and the desktop derives the same PVAC keys from it. The key that signs transactions never leaves the extension 3. For heavy FHE operations, the extension delegates computation to the desktop app 4. Desktop computes using native Rust with all CPU cores, returns the result 5. Extension uses the result as if it computed locally ### What Gets Accelerated Range proofs, bound proofs, commitments and PVAC key work for the extension's encrypt, decrypt, private send and claim all run natively in the desktop app. ### Security * **Loopback only**: The WebSocket server binds to `127.0.0.1`, so no remote connections are accepted * **Paired clients only**: The extension and local programs pair once, and the extension proves its pairing on every connection before any key material moves. A website that connects straight from the browser can only send site requests for its own address, each reviewed in the menu bar; it never reaches proofs, keys or pairing * **Connection limit**: Maximum 8 concurrent connections * **Message size cap**: 16 MiB per message for the extension and paired programs (range proofs can be 1 to 3MB), 1 MiB for web pages * **Handshake timeout**: a connection that does not finish its handshake within 5 seconds is closed, and only finished handshakes count toward the connection limit * **Idle timeout**: 300 seconds * **No signing key**: The desktop can build proofs and read the private balance, but it cannot sign, because it never receives the wallet key (extension 2.5.6 and later) * **Memory safety**: The master seed is zeroized after use on both sides ### Status Indicators * **Extension**: the extension's status bar shows a **0xio Desktop** chip while the desktop app runs its proofs * **Menu bar icon**: the slash turns while a private proof runs * **Engine card**: the menu bar names the extension only for the extension's own proofs *** ## AI Contract Assistant The desktop app includes an AI-powered assistant for smart contract development: * **AML brainstorming**: describe what you want your contract to do and get AML code suggestions * **Code generation**: generate complete contract templates from natural language descriptions * **Error fixing**: paste compiler errors and get suggested fixes *** ## Contract Developer Tools The desktop app includes a full smart contract development suite: compile, deploy, verify, and interact with contracts directly. ### Write & Compile Write contracts in two languages: | Language | Extension | Description | | - | - | - | | **AppliedML** | `.aml` | High-level language with automatic ABI generation. Recommended for most developers. | | **Assembly** | `.oasm` | Low-level bytecodes for advanced users. | * **File import**: Load `.aml` or `.oasm` source files * **Compile**: Sends source to the network compiler, returns bytecode + ABI * **Error reporting**: Line numbers and error messages from the compiler ### Deploy * **Address preview**: Predict your contract's address before deploying (based on sender + nonce) * **Constructor parameters**: JSON array input for initialization values * **Deploy fee**: set by the size of the code, the way the Octra reference client sets it: 200,000 OU plus 1,000 OU per started KiB. A site may set its own deploy fee up to 10,000,000 OU * **Source verification**: Verify deployed bytecode matches your source on-chain ### Interact * **ABI-driven forms**: Load a contract's ABI to get typed input fields for each method * **View vs Call**: Read-only queries (free) or state-changing transactions (costs OU) * **FHE parameter encryption**: Use `enc(42)` syntax to encrypt integer parameters with FHE before sending * **Auto-decrypt results**: Long return values are automatically tested for FHE decryption ### Contract Registry Keep a local registry of your deployed contracts with name, address, language, and ABI for quick access. *** ## Built-in DApp Browser Browse Octra ecosystem dApps without leaving the desktop wallet: * **DApp directory**: Quick links to ecosystem sites * **URL navigation**: Visit any URL directly * **Open in window**: Launch dApps in a native window for sites that don't support iframe embedding * **Connected DApp management**: View and manage permissions for connected dApps *** ## Networks The desktop app supports switching between Octra networks: | Network | Privacy (FHE) | Explorer | | - | :-: | - | | Mainnet | Yes | [octrascan.io](https://octrascan.io) | | Devnet | Yes | [devnet.octrascan.io](https://devnet.octrascan.io) | RPC endpoints are configured automatically by the app (mainnet connects directly to `octra.network`). An **RPC proxy toggle** in Settings routes mainnet and devnet requests through the 0xio proxy. It is off by default, it is never used as a silent fallback, and a network you add yourself never uses it. Switch networks in **Settings > Network**, which also shows the latest epoch and how fast each network answers. Devnet is recommended for testing FHE operations and smart contract development. *** ## Security * **Encrypted vault**: All private keys encrypted with AES-256-GCM at rest * **Keys always need a password**: a wallet with keys is never stored without one. A vault that holds only watch addresses can stay without a password; adding a wallet with keys asks you to create one first, and an older vault with keys and no password asks for one at unlock * **Crash-safe saves**: the vault is written to a new file and swapped in only once it is complete, and the previous copy is kept as a backup the app opens if the vault cannot be read. A file the app cannot read is never overwritten * **PBKDF2 key derivation**: 600,000 iterations for password-to-key conversion * **Biometric unlock**: Touch ID (macOS) / Windows Hello, with a password fallback. On macOS only a fingerprint opens it, never the Mac login password: the prompt's one button is **Cancel** on the lock screen, or **Use password** where a password field is waiting. If you add or remove a fingerprint, Touch ID turns off and **Settings > Security & Privacy** says so; turn it on again there. The password copy an older version kept for Touch ID is deleted * **Auto-lock**: Configurable inactivity timeout * **Recovery phrase and private key**: One screen shows either, opened as **Recovery phrase** or **Private key** from **Settings > Security & Privacy**, from **Wallets**, or from **Back up** on Home. It asks for the password or Touch ID first in a small sheet (Touch ID first when it is on; **Cancel** goes back). **Show** keeps the words or the key on screen until **Hide**; they are also hidden when you leave the screen, switch to another window, hide or minimise the app or it locks, and showing them again asks for the password or Touch ID. A first backup ends with **I wrote them down** and Home stops asking; a wallet you imported, by phrase or by private key, starts as backed up and closes with **Done** * **Password guess limit**: Wrong passwords count against one limit everywhere a password is asked: unlock, showing a phrase or key, deleting a wallet, creating or deriving a wallet, and changing or removing the password. After five misses the app waits 30 seconds, doubling up to an hour * **Address and public key**: Shown in the wallet's details in **Wallets**, each with **Copy** * **Copy without leaks**: **Copy** on a recovery phrase or private key stays on this computer (no Universal Clipboard, no Windows clipboard history or cloud sync), is hidden from clipboard managers, and is cleared after 60 seconds unless you copied something else. A phrase or key you paste into import is cleared from the clipboard too * **Deleting a wallet**: Asks for the password first * **Input validation**: Address format, amount bounds, and JSON parsing checks * **Sandboxed browser**: Restricted iframe permissions, blocks `javascript:`, `data:`, and `file:` schemes * **Isolated frames**: Living NFTs and `oct://` pages load from a listener inside the app, bound to this computer only (`127.0.0.1`, a random port, a random 256-bit path per document), each with a security policy of its own and an opaque origin. The app treats them as remote content and its permission system refuses every command from them, so untrusted content cannot reach the wallet or other sites. Locking the wallet drops every staged document. A seed-only living NFT program gets no network access at all, and one that reads the chain may reach only its own network's RPC * **dApp windows**: A dApp opened in its own window can only send wallet requests, which go through the same approvals as the in-app browser. It cannot reach any other part of the app * **Menu bar approvals**: Every send, encrypt, decrypt, migration and key update from the menu bar signs only on the wallet and network its review showed, at the live network fee the review showed. A fee that cannot be read blocks the action with a retry; no default fee is ever signed * **Sentry crash reporting**: Automated error reporting for stability improvements *** ## Debug Console The desktop app includes a built-in debug console for developers and advanced users: * **RPC logs**: View raw RPC requests and responses * **PVAC timing**: Monitor FHE operation performance (proof generation, key decompression) * **Network diagnostics**: Check connectivity, latency, and endpoint status *** ## Auto-Updater The desktop app checks [0xio.xyz](https://0xio.xyz/install) for updates automatically and prompts you to install new versions when available. Update artifacts are **minisign-verified** against a bundled public key before installation. *** ## Navigation & Productivity * **Command palette**: A keyboard-driven launcher to jump between screens and actions. * **Sidebar wallet switcher**: Switch between your wallets directly from the sidebar. * **Collapsible sidebar**: Collapse the sidebar to maximize screen real estate when working with contracts or browsing dApps. * **Octra Circles browser**: Browse native `oct://` on-chain content via the Circle bridge. Files stored in a circle as chunks are put back together and checked against their hashes before they show, in the browser and NFT art (see [Chunked Circle Files](/wallet/nft-standards#chunked-circle-files)). The Circles page lists the circles the wallet deployed on the current network with the files it published to each (the wallet's own record: the network has no listing call). Several files can be added at once, each with an editable path, then one review lists every file with its transaction and fee and the files go out one transaction at a time: public files up to 2 MiB, encrypted files up to 32 MiB, and an encrypted file is listed with a keyed digest, not a plain hash of its content. A deploy or upload is shown as done only once the chain confirms it; one that is not confirmed can only be checked again, never sent twice. The read passphrase of an encrypted circle is never stored, so the page asks for it again before adding files, and only the owner wallet can open an encrypted circle's content. *** ## Getting Started ### Installation 1. Download the latest build from [0xio.xyz/install](https://0xio.xyz/install) 2. Follow the platform-specific installer instructions 3. Open the app. It also appears in your menu bar (macOS) or system tray ### Creating a Wallet 1. Set a **master password** to encrypt your vault. It cannot be skipped for a wallet with keys; watching an address needs none 2. Choose **Create New Wallet** or **Import** with an existing seed phrase 3. Your wallet address and balances will appear on the dashboard ### Pairing with the Extension Pair once, with both the desktop app and the browser extension running: 1. In the extension, tap the desktop status or open **Settings**, **Connections**, **0xio Desktop** and tap **Pair** 2. 0xio Desktop shows a 6 digit code. The extension shows three codes: pick the one that matches 3. Approve the request in the desktop app. If you press Pair on the desktop before you pick in the extension, it shows Waiting until you do 4. The extension shows **Paired**, a **0xio Desktop** chip appears in its status bar, and private proofs now run in the desktop app Nothing is saved or shared until you pick the right code and the desktop approves. A wrong pick stops the pairing (**Wrong code. Pairing stopped**), and you start again with **Try again**. Pairing also stops when the desktop refuses it, after two minutes or when the connection closes, and you can cancel it at any time. The desktop shows one pairing review at a time; the next one opens 3 seconds after the last closes. Paired apps are listed in the desktop under **Settings > Security & Privacy**, **Paired apps**, each with when it was last used and **Remove**. The choices below sit in the **Connections** group on the same tab, each takes effect at once, and the panel on the right shows how each one is set. * **Forget unused pairings after**: 7, 30 or 90 days, or Never (90 days by default). A pairing not used for that long is removed; the extension then shows Not paired with Pair * **Websites can connect without the extension**: on by default. Sites you approve still need a review in the menu bar. Off refuses websites and closes the open ones at once * **Notify me about site requests**: on by default. A site request that waits while the menu bar panel is closed shows a macOS notification * **Allow older extensions without pairing**: on by default. The 0xio extension 3.0.3 and older cannot pair, so while this is on they can still run their own private proofs in the desktop app, and nothing else. Turn it off to refuse them; they then run proofs in the browser. Extension 3.0.4 and later always pair, and if the desktop app is too old to pair the extension asks you to update it and runs proofs in the browser > [!TIP] > > Keep the desktop app running while you use the browser extension; it can stay in the menu bar with the window closed. # 0xio DEX Source: https://docs.0xio.xyz/products/dex Cross-chain swap protocol for Octra Network: swap OCT and ETH with on-chain settlement and solver-based fills. > [!NOTE] > > **Status: Live on Devnet** > The 0xio DEX is live at [dex.0xio.xyz](https://dex.0xio.xyz). Cross-chain swaps between OCT and ETH are available on devnet. ## Overview **0xio DEX** is a **cross-chain swap protocol** that enables users to swap native tokens across chains, starting with **OCT ↔ ETH** between Octra and Ethereum Sepolia. The protocol uses a **solver-based architecture** with on-chain escrow and oracle verification. ## How It Works ### OCT → ETH **1. Connect Wallets** Connect your 0xio wallet (Octra) and MetaMask (Sepolia) **2. Get Quote** Enter the OCT amount and the solver provides a real-time quote with TWAP pricing **3. Lock OCT** OCT is locked in the SwapVault contract on Octra with a minimum receive commitment **4. Solver Fills** The solver detects the lock, sends ETH to your Sepolia address (\~30s) **5. Oracle Attests** The oracle independently verifies the ETH was sent and attests on-chain **6. Settlement** After a challenge window, the solver claims the locked OCT ### ETH → OCT **1. Connect Wallets** Connect MetaMask (Sepolia) and 0xio wallet (Octra) **2. Lock ETH** ETH is locked in the EthEscrow contract on Sepolia **3. Solver Sends OCT** The solver detects the lock and sends OCT to your Octra address **4. Solver Records the Fill** The solver calls `fill()` on the escrow with the Octra transaction hash **5. Oracle Attests + Settlement** The oracle verifies the OCT payout and `attest()` is recorded; after the challenge window the solver settles and receives the locked ETH > [!NOTE] > > You are paid first: the solver sends OCT before it claims anything on Ethereum. If the solver never pays, the ETH stays in the escrow and you refund it after the lock expiry. The timing risk sits with the solver: a lock that expires before `fill()` is recorded can no longer be claimed. ## Features * **Cross-Chain Swaps**: OCT ↔ ETH between Octra and Ethereum Sepolia * **TWAP Pricing**: 5-minute time-weighted average of the 0xio Oracle's OCT price (the live Uniswap pool price, cross-checked against CoinGecko and DexScreener) * **On-Chain Rate Protection**: `min_receive` committed at lock time, so the solver can't underpay * **Slippage Control**: Configurable slippage tolerance (0.1%, 0.5%, 1%, or a custom value up to 5%) * **Liquidity Display**: Real-time solver balance shown before swap * **Network Detection**: Auto-prompts MetaMask to switch to Sepolia * **Live swap tracking**: Every swap shows three steps (sent from your wallet, on the way, arrived) with a timer, survives a page reload, and offers a one-click refund when a swap can't be filled * **Activity**: Live-updating transaction list with on-chain state at [dex.0xio.xyz/history](https://dex.0xio.xyz/history) * **DEX Stats**: Volume, fees, solver status at [dex.0xio.xyz/stats](https://dex.0xio.xyz/stats) * **Price Chart**: Live OCT/USD chart next to the swap card (a compact price strip on phones) ## Security The protocol is built around a layered defense model: at no point can the solver underpay or the user lose locked funds. ### 5-Layer Defense | Layer | Protection | | - | - | | **Solver Bond** | 20K OCT staked as collateral, slashed on fraud | | **On-Chain Rate Commitment** | `min_receive` prevents solver from underpaying | | **Oracle Verification** | Independent fill verification before attestation | | **Challenge Window** | Dispute period after attestation, before the solver can settle | | **Stale Fill Refund** | User reclaims funds if oracle doesn't attest within timeout | ### Key Security Properties * **Pay first, claim later**: The solver delivers the destination tokens before it records `fill()`, and claims the locked funds only after the oracle attests and the challenge window passes * **Idempotent payouts**: Every solver transaction is stored before broadcast and retried with the same bytes, so a crash or timeout never pays twice; an unclear payout stops for manual review instead of being resent * **Fee snapshot**: Fee rate locked at swap time, can't be changed retroactively * **Reentrancy guard**: All ETH transfer functions protected * **Emergency refund**: Owner can refund unfilled swaps (blocked after fill) * **Reconciliation**: The solver periodically reconciles its database with on-chain state and only ever moves a swap forward ## Contract Addresses (Devnet) | Contract | Network | Address | | - | - | - | | SwapVault v7 | Octra Devnet | `octHqBuw3TGsgeGpu6GiumRTjQwc8VCRAo1ANhy5zL4ZVPc` | | EthEscrowV2 | Sepolia | `0x835454daE518108105BC2ac22cb11738A5D69554` | | Solver Wallet | Octra | `oct4Ay2HKoKbVSndUVdDaf1gxtYhdXeRVpkHvv43rhRoxio` | | Solver Wallet | Sepolia | `0x3ec4bd68e2E3190FB3050005D8B9C132aFd9D684` | | Treasury | Octra | `octCWWvj4EKPk1QA9TZrNTXZ6dJEbcQaS3fWAcmobPnyuqt` | | Treasury | Sepolia | `0x80961b1e16D483c7EB5CA32e1fFd8157E5188598` | ## Architecture ``` User (0xio Wallet + MetaMask) │ ▼ Frontend (dex.0xio.xyz) │ ├── Quote: Solver API (solver.0xio.xyz) │ └── TWAP pricing from Oracle (oracle.0xio.xyz) │ ├── OCT→ETH: SwapVault v7 (Octra) → Solver sends ETH (Sepolia) │ └── ETH→OCT: EthEscrowV2 (Sepolia) → Solver sends OCT (Octra) ``` ### Swap Lifecycle (OCT → ETH) ``` lock() → fill() → attest() → [challenge window] → settle() │ │ │ │ │ │ │ └── Solver gets OCT │ │ └── Oracle verifies ETH was sent │ └── Solver sends ETH + posts dest tx hash └── User locks OCT with min_receive commitment ``` ### Swap Lifecycle (ETH → OCT) ``` lock() → send OCT → fill() → attest() → [challenge window] → settle() │ │ │ │ │ │ │ │ │ └── Solver gets ETH │ │ │ └── Oracle verifies the OCT payout │ │ └── Solver records the Octra tx hash on the escrow │ └── Solver pays OCT to the user first └── User locks ETH in EthEscrowV2 ``` ## Fee Structure | Parameter | Value | | - | - | | Swap fee | 0.5% (50 bps) | | Fee destination | Protocol treasury (per-chain) | | Minimum swap (OCT→ETH) | 10 OCT (the vault's minimum fee makes smaller locks fail) | | Minimum swap (ETH→OCT) | 0.001 ETH | | Maximum swap | 10,000 OCT / 10 ETH | ## Services | Service | URL | Role | | - | - | - | | DEX Frontend | [dex.0xio.xyz](https://dex.0xio.xyz) | Swap interface | | Activity | [dex.0xio.xyz/history](https://dex.0xio.xyz/history) | Transaction list + on-chain state | | DEX Stats | [dex.0xio.xyz/stats](https://dex.0xio.xyz/stats) | Volume, fees, solver status | | Solver API | solver.0xio.xyz | Quote, swap tracking, health | | Oracle | oracle.0xio.xyz | TWAP prices, fill verification | ## Solver API ### GET /api/quote Get a swap quote with current TWAP pricing. ``` GET https://solver.0xio.xyz/api/quote?from=OCT&to=ETH&amount=100 ``` Response includes: rate, receive amount, fee, liquidity availability, intent hash. ### GET /api/swap/:id One swap by id: the quote `intent_hash` for OCT→ETH, or `escrow_` plus the lowercase keccak256 of the intent hash for ETH→OCT. Returns 404 when the solver has not seen it yet. ### GET /api/swaps List recent swaps with status tracking. `?address=` filters by source or destination address (case-insensitive). ### GET /api/chains Supported chains/tokens, the live contract addresses (SwapVault, EthEscrow), solver wallets and the lock limits (`min_lock_oct`, `min_lock_eth`). The DEX frontend locks only into its pinned contracts and pauses swaps if this endpoint reports different ones. ### GET /api/health Solver status: RPC connectivity, bond amount, liquidity on both chains. ### GET /api/price Proxied oracle price feed (no API key needed from frontend). ## Refund Mechanisms | Scenario | Mechanism | | - | - | | Solver never fills | User taps Refund on the swap card or the Activity page once the lock expiry passes (`refund()`) | | Solver fills but oracle never attests | Same Refund button after the stale-fill window (`refundStaleFill()` / `refund_stale_fill`) | | Emergency | Owner calls `emergencyRefund()` (immediate, unfilled swaps only) | ## Related Repos | Repo | Role | | - | - | | **0xio-solver** | Rust solver backend (Axum + Tokio) | | **0xio-oracle** | Price feeds (TWAP) + fill verification | | **@0xio/sdk** | Octra wallet SDK for DApp integration | ## Roadmap ### Near Term * Solana support (SOL ↔ OCT) * Multi-token support (wOCT, USDC) * Multi-solver competition * Mainnet deployment ### Medium Term * Liquidity pools (CLMM) * Limit orders * Advanced routing (multi-hop, split orders) ### Long Term: Cross-Chain Privacy Layer * **Accept wETH/wSOL on their native chains, encrypt via Octra's FHE, transfer privately, withdraw anywhere** * Octra becomes a privacy middleware: users put in wETH, get out wETH, with full amount privacy in between * FHE-powered private swaps (AMM operating on encrypted data) * Relayer network for gasless, unlinkable withdrawals * Compliance via selective disclosure (viewing keys) > [card] [Cross-Chain Privacy Layer: Full Design](/concepts/privacy-layer) > > Deep dive into how 0xio uses Octra's FHE as a privacy backend for any blockchain. Architecture, phases, and comparison with Umbra, Railgun, and Aztec. # Browser Extension Source: https://docs.0xio.xyz/products/extensions The 0xio browser extension: an Octra wallet with private balances and an EVM wallet, a built-in bridge, NFTs and site connections, for Chrome, Brave, Edge, Opera and Firefox. The **0xio Browser Extension** is a non-custodial wallet for the Octra Network with an EVM wallet built in. It keeps public and private (FHE-encrypted) OCT balances, holds a 0x address on Ethereum, Robinhood Chain and any EVM network you add, bridges between Octra and Ethereum, and connects to Octra and EVM websites. > [!NOTE] > > **Status: Live (v3.0.4) on the Chrome Web Store.** Install from the [Chrome Web Store](https://chromewebstore.google.com/detail/0xio-wallet/anknhjilldkeelailocijnfibefmepcc) or from [0xio.xyz/install](https://0xio.xyz/install). The Firefox version is in review on Mozilla Add-ons. ## Requirements * Chromium browsers (Chrome, Brave, Edge, Opera) version 111 or later * Firefox 140 or later (142 on Android) * Manifest V3 ## Getting Started The welcome screen offers **Create a wallet** and **I already have a wallet**. The Terms of Service and Privacy Policy are linked below them. * **Create a wallet:** create a password, confirm it on the next screen, optionally add a passkey, choose your networks and land on **Your wallet is ready**, which lists your Octra and EVM addresses. You back up the recovery phrase afterwards, from the **Back up** tag on Home. * **I already have a wallet:** paste a recovery phrase (12 or 24 words), a private key or an address into one field. The extension tells which it is, offers a one-tap fix for a mistyped word, and shows the wallet it found with its balance. An address adds a watch-only wallet. The password needs at least 8 characters and unlocks the extension in this browser. See [Installation & Setup](/wallet/installation) for each step. ### Adding More Wallets In the wallet list, **Add wallet** offers: * **Create wallet:** a new wallet and recovery phrase, saved at once. * **Add account:** another address from one of your wallets' recovery phrases. It gets its own Octra and 0x address. * **Import wallet:** a phrase, a private key or an address (watch-only). The wallet list shows where each wallet comes from (Phrase, Private key or Watch-only) and both of its addresses, each with a copy button. Switching wallets says which wallet it switched to. *** ## Home ![Extension Home with amounts hidden](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/home.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=96832bed54bb49a5523debc2d374f6d1) Home in the extension, amounts hidden * **Header:** your wallet's picture and name (click for the wallet list), the network switch (**All networks**, **Octra** or **EVM**) and Settings. * **Balance:** the total in USD over a chart of the day (green up, red down, grey when flat). Tags at the top right of the balance card ask for action, most urgent first, for example **Back up** or **Set up private**. * **Actions:** **Send**, then **Receive**, **Bridge**, **Privacy** and **History**. On EVM, Privacy stays visible but disabled. * **Assets:** OCT, the encrypted (private) OCT row, Octra tokens and EVM assets, each with its USD value. Tap one for its page with a 1D, 7D or 1M price chart, the balance, actions and recent activity. * **Status bar:** the open site's connection chip, a **0xio Desktop** chip when the desktop app is running private proofs, and the **Mainnet** or **Testnet** chip. Balances that are not read yet show `--`, never 0. Home reads balances lightly every 30 seconds while open and not at all while hidden or locked. See [Wallet Interface](/wallet/interface) for a full tour. *** ## Sending * **Octra:** public OCT, Octra tokens and private sends. **Bulk** sends OCT to up to 5 recipients from the public balance. * **EVM:** ETH and tokens on any EVM network that is on. See [EVM Networks](/wallet/evm#sending). * **Fees:** one live network fee per send, read from the node. It drives the shown fee, **Max**, every balance check and the signature. You can pick another tier or a custom fee up to 10,000,000 OU, never below the operation's floor or the network's minimum. **Fast** shows only when the node quotes it. If the fee cannot be read, Review is blocked with **Network fee unavailable**. On EVM networks the fee shows in USD first, with the native amount beside it. See [Network Fees](/wallet/sending#network-fees). * **Review:** every send opens a review with **Back** and **Send**. After Send, a result screen shows **Sending**, then **Sent** once the network confirms it (**Queued** for private sends). A busy node gets the same signed transaction again, a used nonce asks for a new review, and nothing is ever re-signed on its own. See [What Happens After You Send](/wallet/sending#what-happens-after-you-send). See [Sending Transactions](/wallet/sending). *** ## Privacy The **Privacy** screen moves OCT between your public and private balances and claims private transfers sent to you: * **Encrypt** (public to private), **Decrypt** (private to public) and **Claim**, each with a review and the live network fee. * **Set up private:** a wallet with no encryption key on chain shows a **Set up private** tag on Home. Its sheet shows the one-time network fee, and **Turn on** registers the key. * **Private on hold:** when the network holds a private balance (for example an older format waiting for a migration), Home shows a tag and the Encrypted row reads **On hold**. The sheet shows the node's reason. * **Proof rules:** before any private action, and again right before signing, the extension reads the proof rules the network states. When the network states none or cannot be reached, private actions pause and nothing is sent. See [Network Proof Rules](/wallet/privacy-operations#network-proof-rules). All proofs run on your device. With [0xio Desktop](/products/desktop) running and paired, they run in the desktop app instead, which is faster. See [Privacy Operations](/wallet/privacy-operations). *** ## EVM Wallet Every wallet made from a recovery phrase also has a 0x address. The extension shows ETH and tokens, sends, receives, shows EVM NFTs and lists EVM history next to Octra. Networks can be turned on or off, given your own RPC, or added from a list of popular chains or the public chain list. See [EVM Networks](/wallet/evm). *** ## Bridge **Bridge** on Home bridges between Octra and Ethereum without leaving the wallet: * **Bridge to Ethereum:** OCT on Octra becomes wOCT on Ethereum, claimed with the wallet's 0x address. * **Bridge to Octra:** wOCT on Ethereum becomes OCT on Octra. * **Any chain to OCT:** pay with a token on another chain and receive OCT on Octra. * **Claim:** a page for transfers to Ethereum, grouped as Ready, In progress and Completed, with **Find my transfers** and **Add by hash**. Each step has its own review, and the Octra network fee is read live. See [0xio Bridge](/products/bridge#bridge-inside-the-extension). *** ## History History lists Octra and EVM activity together, newest first, on two pages: **Mainnet** and **Testnet**. Contract calls read as actions (Swap, Approve, Bridge or the method in words) with the contract named. Every row opens a details card, and contract calls have a **View call data** page. See [Wallet Interface](/wallet/interface#history). *** ## NFTs * Every collection the wallet holds shows without an import step; **Import** stays for other contracts. * The three newest pieces come first, then one row per collection, then **Hidden**. * Hide a piece or a whole collection with Undo. Hidden items are kept per network and wallet. * A piece's page shows its art, traits, status, holder and contract, with **List for sale** and **Send**, or **Change price** and **Cancel listing** for a listed piece. Every step opens a review with the fee. * Octra collections (SNS-1, Bionts and living NFTs) and EVM collections (ERC-721 and ERC-1155) share the list. See [NFT Standards](/wallet/nft-standards). * Art stored in a circle as chunks is put back together and checked against its hashes before it shows. See [Chunked Circle Files](/wallet/nft-standards#chunked-circle-files). *** ## Connecting to Sites The extension connects to Octra sites through the 0xio SDK and to EVM sites, which list it as **0xio Wallet**. EVM sites can be connected to several of your wallets, and a site only ever sees the wallet in use. The status bar shows the open site's connection, and **Settings > Connections > Connected sites** lists them all. See [Connecting to Sites](/wallet/connecting-sites). *** ## Settings Settings opens on your wallet, then: * **Security:** password, passkey unlock, auto-lock time, and Reset wallet under This browser. * **Backup:** recovery phrase and private key (the EVM key too while EVM is on). Shows **Back up again** when a wallet needs a new backup. A wallet with no password creates one first, then goes on to what was asked for. * **Connections:** connected sites and the address book. * **Appearance:** theme (Dim, Day or Octra) and language (English, Chinese, Japanese, Korean or Indonesian). * **Networks:** Octra (Mainnet, Devnet or a node you add, and the optional RPC proxy), then the EVM networks. * **Tokens and NFTs:** a switch for every token and collection, shown or hidden as likely spam. Imported tokens carry an **Imported** tag. * **EVM:** turn EVM on or off for the open wallet. **Lock wallet** sits at the bottom, with links to these docs, the Terms and the [Privacy Policy](https://0xio.xyz/privacy). *** ## Locking * The toolbar icon shows a padlock while the wallet is locked. * Unlock with your password, or tap **Use passkey** where a passkey is set up. The passkey prompt never opens by itself. * Locking shuts down the private balance engine and clears decrypted private balances and wallet data held in memory. *** ## Notices Messages appear as one small pill above the status bar. Information fades after 4 seconds, errors and warnings after 6 seconds. Hovering pauses it. Copy buttons show **Copied** in place, and successful actions show in the screen itself rather than as a notice. *** ## RFC-O-1 Provider The extension implements the **RFC-O-1** wallet provider standard and exposes `window.octra`: ```javascript theme={null} const accounts = await window.octra.request({ method: 'octra_requestAccounts' }); const balance = await window.octra.request({ method: 'octra_getEncryptedBalance' }); window.octra.on('accountsChanged', (accounts) => { /* ... */ }); window.octra.on('connect', (info) => { /* ... */ }); ``` The `@0xio/sdk` includes an `OctraProviderAdapter` that uses `window.octra` as a transport. For EVM sites the extension is a standard EIP-1193 provider, see [EVM Provider](/developers/sdk-guide#evm-provider). *** ## Networks | Network | Privacy (FHE) | Explorer | | - | :-: | - | | Octra Mainnet | Yes | [octrascan.io](https://octrascan.io) | | Octra Devnet | Yes | [devnet.octrascan.io](https://devnet.octrascan.io) | | Your own Octra node | If you mark it as running privacy | The explorer you set, if any | | EVM networks | No | Each network's explorer | Octra Devnet is a test network with full FHE support. It sits on the **Testnet** side together with the EVM testnets. *** ## Desktop Companion When [0xio Desktop](/products/desktop) runs on the same computer, the extension connects to it and runs private proofs there, which is faster than in the browser. * **Pair once:** tap the desktop status or open **Settings**, **Connections**, **0xio Desktop** and tap **Pair**. 0xio Desktop shows a code, the extension shows three: pick the one that matches, then approve on the desktop. Nothing is saved or shared until the right code is picked and the desktop approves; a wrong pick stops the pairing. See [Pairing with the Extension](/products/desktop#pairing-with-the-extension). * **Status:** the status bar shows a **0xio Desktop** chip, which tells whether proofs run in the desktop app or in this browser. * **No signing key:** the desktop app builds proofs but never receives the key that signs your transactions. # Mobile Wallet Source: https://docs.0xio.xyz/products/mobile The 0xio Mobile Wallet for iOS and Android with biometric unlock, PIN security, and the same FHE privacy features as the browser extension. > [!NOTE] > > **Status: Live (v1.3.3).** Install from [0xio.xyz/install](https://0xio.xyz/install). v1.3.4 is in progress. > > * **iOS**: on TestFlight > * **Android**: open testing on Google Play The **0xio Mobile App** is the 0xio Octra wallet for iOS and Android. Built with React Native 0.86 and Expo SDK 57, it runs the same pvac-rs privacy engine as the other clients, natively on the phone. ## Mobile-Exclusive Features ### PIN & Biometric Security The mobile app keeps your keys in the device's secure storage and unlocks with a PIN or biometrics. * **PIN Code:** Set a 6-digit PIN with rate-limited retry protection. * **Stays locked:** once the wallet locks (auto-lock, **Lock now** or too many wrong PINs), closing and reopening the app keeps it locked until you unlock it. * **FaceID / TouchID:** Log in and sign transactions using your unique biometric signature. * **Every transaction is confirmed:** each send, approval, signature and connection ends on a review you confirm with a slide, whether or not a PIN or biometrics are set. Tap outside the review to back out. With **Ask before sending** on (Settings, Security), the PIN or biometric check follows the slide, and turning it off asks for it first. No address skips this check. * **Secure Storage:** Your encrypted keys are protected by the device's hardware-backed Keychain/Keystore. ### Push Notifications Stay informed with real-time alerts for incoming transactions, claim-ready private transfers, and network events. ### DApp Browser Browse and interact with Octra dApps directly from the mobile wallet without leaving the app. The start page lists the ecosystem's sites by category, with your recent sites below. One bar at the top of the start page takes anything you type: * **An app name or words:** matching apps, your recent sites, then a web search. * **An Octra address** (or a payment request): Send to this address, Open in Octrascan, or Save as contact. * **A transaction hash:** your matching transaction, and Open in Octrascan. * **An `oct://` address:** a circle hosted on Octra, read from the chain. Public circle pages load with their images, scripts and stylesheets, and files stored as chunks are put back together and checked against their hashes first (see [Chunked Circle Files](/wallet/nft-standards#chunked-circle-files)). Sealed circles ask for their passphrase. * **Anything else:** opened as a site. Scan sits beside the bar. While you browse, the address sits at the bottom within reach of your thumb. A lock shows only on https pages, plain http is marked not secure, and a green dot means the site is connected to your wallet. Only https sites can reach the wallet; a plain http page can be read but cannot connect. The **...** menu next to the address reloads the page, shares its link, opens it in your phone's browser, switches the network (after asking) and disconnects the site. Every request from a site opens in its own sheet naming the site and the wallet it acts for, one request at a time: * **Connect** lists what the site can see (your address and balance), what it can ask (signatures you review), any private permissions, and that it can never move funds on its own. Tap **Change** to connect another wallet. Slide to connect. * **Sign a message** shows the whole message. Signing moves no funds and costs no fee. Slide to sign. * **A transaction** shows a receipt: what it does, the amount with its USD value, the recipient, the fee, the network and your wallet. A contract call shows its arguments exactly as they are signed. Slide to approve. ![Mobile connect sheet naming the site and wallet with Slide to connect](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/mobile/connect-review.jpg?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=534c35d75a6fe4932f47906827cd06ac) Connecting a site in the mobile app With **Ask for PIN before signing** on, Face ID or the PIN follows the slide. A request to sign a transaction without sending it says the site can send what you sign, a transaction the site signed earlier is only sent after you approve it, and sites cannot submit through raw RPC calls. If you switch wallet or network before a request is signed, nothing is signed or sent. A site connection lasts 24 hours. ### Browser Settings Tap the gear on the Browse start page: * **Connected sites** for this wallet and network. * **Ask for PIN before signing** (on by default). Turning it off asks for your PIN or biometrics. * **Search with** DuckDuckGo, Brave or Google. * **Outside links open in** 0xio or your phone's browser. * **Keep recent sites** (on by default, 30 days). * **Warn on lookalike sites** (on by default). * **Clear recent sites** and **Clear site data**, each with two taps. ### Lookalike Site Warnings A site whose name is spelt close to an app listed in the browser, or is written in another alphabet, is marked **Lookalike** next to its address. Before it can connect, sign, send or switch network, a red warning names the app it resembles. **Close** refuses the request; **Continue** goes on if you meant to open that site. Listed apps are never flagged. ### Connect a dApp (WalletConnect) Use a dApp on another device from your phone: tap the scan icon on the home screen. The camera opens on the code; you can also paste a connection link. The connect sheet shows the site, the wallet and the network, each with **Change**, and warns when the site asked for another network. Slide to connect. A watch-only wallet cannot connect, since it cannot sign. Sites are named by the address WalletConnect verified for them: * **Verified:** the sheet shows the site's verified address. * **Not verified:** the sheet shows the name the site gave, tagged **Unverified**. A name spelt like a listed app is tagged **Lookalike**. * **Invalid or flagged:** a site whose address fails verification, or one flagged as a scam, is refused before any sheet opens. Request sheets carry the same tag. Contract calls show their arguments exactly as they are signed. Live sessions are listed on the same screen as wallet on network, each with Disconnect (two taps). A session stays with the wallet and network it was approved on: if the app is on another one later, its requests ask you to switch back or reconnect. A connection request that arrives after you leave the screen is refused, never approved unseen. Requests from a connected site use the same sheets and slide as the browser. ### Connected Sites **Settings > Connected dApps** (or Browser settings) lists every site by wallet and network, the one you are on first. Tap a site to see what it sees, can ask and can never do, and Disconnect it (two taps) for that wallet and network only. WalletConnect sessions are listed below, and **Disconnect all** clears everything. ### Collectables The NFTs tab opens on your newest piece, then a shelf per collection. Pull down to refresh. Tap a piece for its art, status, traits and more from its collection. * **Send** a piece to an address, a contact or one of your wallets. * **List for sale**, **Change price** or **Cancel listing** on a supported market, with the price in OCT or USD. The first listing approves the market for that piece only. * Each step ends on a review you slide to confirm. * **Hide** a piece or a whole collection from **More**, with Undo. **Hidden**, under the last shelf, shows them again. Hidden pieces are kept per wallet and network. * A piece you send leaves the gallery at once and its page shows **Sent**. * Tap **+** to import a collection. * Art and living NFT programs stored in a circle as chunks are put back together and checked against their hashes before they show (see [Chunked Circle Files](/wallet/nft-standards#chunked-circle-files)). ### Address Book Save and label frequently used addresses. Search by name or address, tap a contact to send to it or copy its address, and edit or delete it (two taps). The app says when an address is not valid or is already saved. ### Multi-Wallet Support Create or import multiple wallets and switch between them from the wallet list (tap the wallet name on Home, or swipe from the left edge). All wallets use the same master root key derivation as the extension and desktop. **Add a wallet** is one sheet: **Create new wallet**, **Derive account** from a recovery phrase already on the phone, and under **Already have one**: **Recovery phrase**, **Private key** or **Watch an address**. **Rename** a wallet from the wallet list or Settings: a sheet with the wallet's picture, quick names (Savings, Daily, Trading, Cold) and its full address. ### Wallet Pictures Every wallet has its own picture, drawn from its address: the 0xio braces around a composition of circles, in muted colours. It appears on the home screen, in the wallet list, in the address picker and on the Receive screen. The same address always draws the same picture, and the browser extension draws it identically, so a wallet looks the same on your phone and in your browser and the wrong one stands out before you read its address. ### History History follows your wallet's address on every network the app knows (mainnet, devnet and your own nodes), newest first, with each entry marked with its network and chips to filter to one. It loads more as you scroll, and you can pull down to refresh. Entries read as sentences: **Received from**, **Sent to** or **Sent privately**, with a contact or wallet name where the app knows one. NFT and token transfers show what actually moved. Tap an entry for its receipt: the amount with its USD value and status, To and From with names and full addresses, the time, the network, the fee and the total, then the transaction hash to copy, **Send again** or **Send back**, **Save contact** for a new sender, and **View on Octrascan** on the transaction's own network. ### Your Own Node Add a network in **Settings > Network** by giving the node's address. The app checks that the node answers as an Octra node as you type, fills in a name, and asks you to confirm you trust the node with your address and what you sign. You choose whether it supports private balances and can add an explorer. Removing a network asks first; removing the one you are on moves the app to Octra Mainnet. **Settings > Advanced > Test connection** checks the network you are on: whether the node answers and how fast, whether it answers as Octra, its latest epoch, private balances, and the backup route. ### Stealth Transfers Send private FHE-encrypted transfers where the amount is hidden from everyone except the recipient. ### Bulk Send Bulk sends are available in [0xio Desktop](/products/desktop) and the browser extension. The mobile app sends to one recipient at a time. ### Optional RPC Proxy By default, balances and history come straight from the Octra node. Turn on the **RPC proxy** in **Settings > Advanced** to route mainnet and devnet requests through the 0xio proxy, which compresses history responses for slow connections. The proxy is never used silently as a fallback, and a network you add yourself never uses it. ### Sentry Crash Reporting Crash reports help fix problems quickly. They never include keys, recovery phrases or PINs, and you can turn them off under **Crash reports** in Settings; see the [Privacy Policy](https://0xio.xyz/privacy) for what is sent. ### Dynamic Recommended Fees The app recommends different OU fee amounts per operation type (standard transfer, FHE encrypt, decrypt, etc.) based on current network conditions. * Every fee is the network's live quote for that operation: a transfer, a token send or NFT action (a contract call), encrypt, decrypt, a private send or a claim. No default fee is ever shown or signed. * A fee that cannot be read shows **Unavailable** and **Could not read the network fee. Try again.** The review and the slide stay off until a live fee exists. * The fee on the review is the fee signed, as long as the network still accepts it. You are asked to review again only when the network's minimum fee rises above it: **The network fee changed. Review again.** * A site, circle or WalletConnect request uses the site's fee only when the network accepts it, else the live recommended fee. Without a live fee the request is refused before the approval opens. * **Fast** is offered only when the network quotes it; otherwise the fee sheet shows Standard and Custom. * Private operations have floors: key switch, encrypt, decrypt and private send 5,000 OU, claim 3,000 OU. * A custom fee below the network minimum or above 10,000,000 OU cannot be used. See [Network Fees](/wallet/sending#network-fees). ### Private Balance Status When your private balance cannot be used as is (the network holds it for a migration, its key needs an update, or it was made by an older version), a tag sits beside your balance on Home. Tap it to see why and, when your account allows it, re-register your encryption key. ### Stealth Claim Caching Previously claimed stealth transfers are cached locally, so the claim scan only processes new transfers on subsequent runs. *** ## Core Functionality ### Dashboard & Balances The **Home Screen** shows your total balance over a faint chart of the day's worth. Drag across the chart to read the worth and change at any moment, and pull down on your assets to refresh. Send, Receive and Privacy sit right below, and only your asset list scrolls. Tap the eye beside the balance to hide amounts. Each asset shows its name over its symbol, the amount and its USD value, or **No price yet**. Tap one for its page: **Octra** has Send, Receive and Encrypt with your public activity; **Private OCT** has Send, Receive and Decrypt with your private activity only; a custom token shows its contract and decimals, and one you imported can be removed. The glass bar at the bottom switches between Wallet, History, Browse and NFTs: tap a tab, or hold the bar and slide your finger to carry the lens to another tab. While the app starts its FHE engine, a small **Initializing FHE** pill shows above the bar and you can keep using the wallet. ![Mobile Home with the balance blurred, Send, Receive and Privacy, and the tab bar](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/mobile/home.jpg?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=080f2973e30c50162ce02975ee80d134) Home in the mobile app ### Receiving **Receive** shows your QR code and address. Type an amount on its keypad to turn the QR into a payment request; **Share request** sends it as a link. Someone scanning it with 0xio gets your address and the amount filled in. See [Payment Requests](/wallet/receiving#payment-requests-mobile) for the format. ### Sending Assets The **Send Flow** is optimized for one-handed use: * **Pick a recipient:** type or paste an address, or choose a contact or a recent recipient. With nobody saved yet, your other wallets are listed. * **Scan to Pay:** scan an address, or a payment request that fills in the amount too. * **Private sends:** pick **Private OCT** to send privately. * **Slide to send** on the review. You can close the progress screen while it runs; the result arrives as a notice. * **Not confirmed:** when the app cannot tell whether a send went through, it checks the network first. If it still cannot tell, the send stays pending and says **Check History before sending again**. See [Not Confirmed](/wallet/sending#not-confirmed). ### Privacy Suite (FHE) Tap **Privacy** on Home for a sheet over your wallet: 1. **Encrypt:** Move funds from Public -> Private. 2. **Decrypt:** Move funds from Private -> Public. 3. **Waiting for you:** incoming private transfers, each with **Claim**, and **Claim all**, which claims them one at a time with Pause and Stop. A private balance can carry a limited number of encrypted layers. When an encrypt, decrypt or private send meets that limit, the review says the balance is refreshed first, shows the fee for both transactions, and one slide runs both. Private actions follow the proof rules the network states, read before you prepare and again right before signing. When the network does not state them, private actions pause and nothing is sent. See [Network Proof Rules](/wallet/privacy-operations#network-proof-rules). *** ## Getting Started ### Creating a Wallet 1. Install the app from [0xio.xyz/install](https://0xio.xyz/install). 2. Tap **Create a wallet**. 3. **Set a PIN and biometrics** to protect the wallet on this phone. If the app closes before the PIN is set, it reopens on this step, never on Home. 4. Name your wallet and copy its address on **Your wallet is ready**. 5. **Back up your recovery phrase:** tap the **Back up** pill on Home. After your PIN, hold to show your 12 words, write them down in order offline, then tap **I wrote them down**. Until you do, Receive reminds you first. ### Importing Already have the Browser Extension or another 0xio wallet? 1. Tap **I already have a wallet**. 2. Type, paste or scan your recovery phrase (12 or 24 words) or private key into the one field. The app tells which it is and flags typos. 3. Check the wallet it found, with its address and balance, then import it. Your public and private balances sync automatically. ### Watching an Address From the wallet list, tap **Add a wallet**, then **Watch an address**, and paste or scan any `oct...` address. The app shows its balance and latest activity before you add it. A watched wallet holds no keys, so it can never send. *** ## Network Support The mobile app supports multiple Octra networks: | Network | Privacy (FHE) | Explorer | | - | :-: | - | | Mainnet | Yes | [octrascan.io](https://octrascan.io) | | Devnet | Yes | [devnet.octrascan.io](https://devnet.octrascan.io) | | Your own node | If you say it runs the FHE stack | The explorer you set, if any | Switch networks in **Settings > Network**. Devnet is recommended for testing FHE features such as encrypted balances, private transfers, and the encrypt/decrypt flow. > [card] [Install 0xio Mobile](https://0xio.xyz/install) > > Get the 0xio Mobile Wallet for iOS and Android. # 0xio Bot Source: https://docs.0xio.xyz/products/telegram-bot Monitor Octra Network addresses from Telegram with the 0xio Bot, including real-time alerts for transfers, balance tracking, and wallet management. # 0xio bot User Guide Welcome to the official documentation for **0xio bot**, a Telegram bot designed to monitor your **Octra Network** addresses in real-time. Receive instant notifications for incoming and outgoing transactions, track balances across mainnet and devnet, query live 0xio DEX pool data, and manage multiple wallets directly from Telegram. ## Getting Started To start using the bot, open [@NullXio_bot](https://t.me/NullXio_bot) in Telegram and tap the **Start** button or send the command: ```bash theme={null} /start ``` You will be greeted by the main menu, which gives you quick access to your wallets, settings, and tools to add new addresses. *** ## Key Features * **Real-Time Monitoring**: Get notified instantly when you send or receive OCT, including stealth payments, contract calls, and self-transfers (encrypt/decrypt/claim). * **Multi-Network**: Track addresses on **mainnet and devnet** at the same time, with per-network toggles. * **Multiple Wallets**: Track up to **5 different Octra addresses** simultaneously. * **DEX Integration**: Query live price, pool state, and swap quotes from the 0xio DEX WOCT/0XIO CLMM pool with `/price`, `/pool`, and `/quote`. * **Privacy Control**: Toggle balance visibility in notifications to keep your holdings private. * **Detailed Insights**: View transaction hashes, counterparty addresses, and direct links to the Octra Explorer. * **Address Validation**: Built-in checks ensure you only track valid Octra addresses, with auto-detection of addresses pasted in chat. *** ## Command Reference Manage the bot efficiently using these commands: | Command | Description | | - | - | | `/start` | Open the main menu and initialize the bot. | | `/wallets` | View your tracked wallets, balances, and manage them. | | `/add` | Quickly add a new Octra address to your watchlist. | | `/settings` | Configure notifications, networks, and balance visibility. | | `/help` | Display the help message and available commands. | | `/price` | Current price of the 0xio DEX WOCT/0XIO pool (devnet). | | `/pool` | Pool state: active bin, reserves, and fees (devnet). | | `/quote {amt} {tok}` | Get a swap quote, e.g. `/quote 100 WOCT` (devnet). | *** ## Managing Wallets ### Adding a Wallet 1. Click **Add Wallet** in the main menu or send `/add`. 2. Paste your **Octra Network address**. > **Note:** Addresses must start with `oct` and be 47 characters long. 3. Enter a **custom name** for the wallet (e.g., "Main", "Savings"). 4. The bot will sync your balance and begin watching for new transactions immediately. ### Viewing & Editing Access your wallet list by sending `/wallets`. Click on any wallet to: * **Rename**: Change the display label. * **Delete**: Stop tracking the address. * **Explorer**: View the address on the official Octrascan explorer. ### Wallet Limits Each user can watch a maximum of **5 wallets**. If you reach this limit, you must delete an existing wallet before adding a new one. *** ## Settings & Privacy You can customize how notifications appear in your chat. ### Notification Balance By default, the bot displays your updated wallet balance in every transaction notification. * **Visible**: Shows `Balance: 150.0 OCT` in the alert. * **Hidden**: Removes the balance line for privacy (useful if you share screenshots or use the bot in public). **To toggle this setting:** 1. Send `/settings`. 2. Click the **Hide Balance** or **Show Balance** button. ### Network Selection The bot monitors **mainnet** and **devnet** simultaneously. Use `/settings` to enable or disable per-network notifications. Each transaction alert and explorer link is tagged with its network. > [!NOTE] > > An optional AI transaction-analysis feature can be unlocked with an activation code using `/ai {code}`. *** ## DEX Queries The bot doubles as a companion for the [0xio DEX](https://dex.0xio.xyz). It reads the on-chain WOCT/0XIO concentrated-liquidity (CLMM) pool on devnet: * `/price`: current pool price. * `/pool`: active bin, reserves, and fee tier. * `/quote {amount} {token}`: a swap quote, e.g. `/quote 100 WOCT`. *** ## Notification Types When a transaction occurs on the Octra Network, you will receive a notification similar to this: **Received Transaction** > **RECEIVED** 100.0 OCT > From: [oct...abcd](https://octrascan.io/address.html?addr=oct...abcd) > Wallet: [Main](https://octrascan.io/address.html?addr=your-wallet-address) > Balance: 1250.0 OCT **Sent Transaction** > **SENT** 50.0 OCT > To: [oct...xyz1](https://octrascan.io/address.html?addr=oct...xyz1) > Wallet: [Main](https://octrascan.io/address.html?addr=your-wallet-address) > Balance: 1200.0 OCT *Clicking the links in the notification will take you directly to the transaction details on the blockchain explorer.* *** ## FAQ **Q: Why does the bot say "Invalid Address"?** A: Ensure your address starts with `oct` and contains exactly 47 alphanumeric characters. **Q: Can I track the same address on multiple accounts?** A: Yes, the bot tracks public blockchain data, so an address can be monitored by multiple users. **Q: How fast are notifications?** A: The bot polls the Octra Network approximately every 30 seconds to check for new activity. # Connecting to Sites Source: https://docs.0xio.xyz/wallet/connecting-sites Connect the 0xio browser extension to Octra and EVM websites, choose which wallet a site uses, and manage connected sites. The 0xio browser extension connects to two kinds of websites: * **Octra sites**, through the [0xio SDK](/developers/sdk-guide) and the `window.octra` provider. * **EVM sites** (Ethereum and other EVM networks). Sites find the wallet through EIP-6963 and list it as **0xio Wallet**. The extension also sets `window.ethereum` when no other wallet has set it. A site sees nothing until you approve its connection, and every transaction and signature it asks for opens a review first. > [!NOTE] > > This page covers the browser extension. The mobile app connects to sites in its built-in browser and through WalletConnect, see [Mobile Wallet](/products/mobile#connect-a-dapp-walletconnect). The desktop app has its own built-in browser, see [Desktop Wallet](/products/desktop#built-in-dapp-browser). ## Connecting Sites ask to connect from their own page. The connect review shows the site, the wallet it will see and the network: * The site can view this address and its balances. * Every transaction and signature needs your approval. Choose **Reject** or **Connect**. ### When the Open Wallet Has No EVM Address A wallet imported from an Octra private key has no 0x address. When such a wallet is open and an EVM site asks to connect, the review lists your wallets that have an EVM address, with the one you used last picked first. Watch-only wallets are never listed. ![Connection request listing wallets with an EVM address while a private key wallet is open](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/site-wallet-picker.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=a17ad477eabbb46a65bdb2b74485d4a9) Choosing a wallet for an EVM site If none of your wallets has an EVM address, the review offers **Add a wallet**: import a recovery phrase or create a new wallet. The review stays open and comes back with the new wallet picked. ## Several Wallets per Site An EVM site can be connected to more than one of your wallets: * When you open a wallet that is connected to the site, the site moves to it. * When you open a wallet that is not connected to the site, the site keeps the wallet it had. * Switching wallets never disconnects a site. * A site only ever sees the wallet in use. It cannot learn which of your other wallets are connected to it, and a request naming another address is refused. An Octra site keeps one wallet. Switching wallets does not move it, and its requests sign with its own wallet. When a site on another wallet asks for a private operation, the wallet asks first: switch to that wallet, or move the site to the open one. Removing a wallet from 0xio lists its connected sites first, removes it from every site, and disconnects a site left with no wallet. ## The Site Chip The status bar at the bottom of the wallet shows the open tab's site: | Chip | Meaning | | - | - | | Host with a dot | The site uses the open wallet | | Wallet picture and host | The site uses another of your connected wallets | | **Not connected** | The open wallet is not connected to this site | ![Site chip menu with the connected wallets, the network and Disconnect](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/site-chip-menu.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=03740cc6f6d7375d3337e863b93c7d05) The site chip menu Tap **Not connected** to open the connect review for the open wallet. On a connected site the chip's menu lists the site's wallets, **Connect another wallet**, the **Network** the site is on (choose another of your networks to move it) and **Disconnect**. Choosing another of the site's wallets opens a short review: both stay connected, and the site uses the new one until you open another connected wallet. ## Mainnet and Testnet 0xio is on Mainnet or Testnet as a whole (see [EVM Networks](/wallet/evm#mainnet-and-testnet)). When a site asks for a network on the other side, the review asks to **Switch 0xio to Mainnet** or **Switch 0xio to Testnet**. Approving moves Home, balances, History and every connected site to that side, then puts the site on the network it asked for. Rejecting changes nothing. ![Network request asking to switch 0xio from Mainnet to Testnet, with Reject and Switch to Testnet](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/site-switch-side.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=76494a682b0177f24a1c20036b5723c2) A site asks for a testnet ## Reviewing Requests Every request from a site opens its own review, names the site, and shows the wallet that signs with its balance (EVM reviews label it **Signing with**). Each review ends with **Reject** and the action, for example **Connect**, **Sign**, **Send**, **Switch**, **Add and switch**, **Add token** or **Deploy**. * **Messages:** the full message. A message that is not readable text is shown as hex with a warning. A token permit says that it lets the spender move your tokens without a separate approval. * **Transactions:** what the call does (for example a token transfer or approval), the amount, the recipient or spender, and the network fee (in USD first on priced networks). A call 0xio cannot decode says so. * **Octra contract calls:** the contract, the method and its arguments exactly as they are signed, large numbers included. A sequence lists every step with its arguments and the amount it attaches. * **Token approvals:** tap the pencil on the Amount row to change the amount, or turn on Unlimited. The amount signed is the one shown, and never more than the site asked for unless you pick Unlimited. * **Fees:** site transactions use the wallet's live network fee. The fee the site sent is used only when the network gives none, and the review marks it **Suggested by site**. * **Contract deployment:** a review with the code size, the new contract's address and the live fee. * **Add a network** and **Add token** show what will be added. Add networks only from sites you trust: the RPC sees your address and your activity on that network. Three requests need no new review, because nothing new is signed: connecting a site that is already connected, a transaction you already signed in a review and the site now sends, and setting up private receiving on a wallet that is already set up. Setting it up for the first time is a transaction with a fee and has its own review. What you review is what is signed: the review and the signature are built from the same fields. A request that names two different recipients, two different methods or two different argument lists is refused before any review opens. After you approve, the wallet checks again that the signing wallet is still in 0xio, still connected to the site, and still has the same address. If anything changed, nothing is signed. ## Connected Sites **Settings > Connections > Connected sites** lists every site once, with its first wallet, a **+N** count for more wallets and its network. Open a site to see its wallets (each with **Remove**), **Connect another wallet**, the network and **Disconnect**. ![A connected site page with its wallet, network and Disconnect](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/connected-site.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=a0b62b0b5be3660ab2592d8c0425c204) A connected site > [!WARNING] > > Disconnecting stops a site from reading your address and asking for transactions. It does not cancel pending transactions or revoke token approvals already on chain. ## While Locked While the wallet is locked, sites receive no address and no balances. The same connection comes back after you unlock, without a new prompt. ## For Developers * Octra sites: [SDK Guide](/developers/sdk-guide). * EVM sites: the provider supports the standard EIP-1193 methods. See [EVM Provider](/developers/sdk-guide#evm-provider) for the methods that open a review, those answered locally and those refused. # EVM Networks Source: https://docs.0xio.xyz/wallet/evm Use Ethereum, Robinhood Chain and other EVM networks in the 0xio browser extension: the 0x address, networks, tokens, NFTs, sending and history. The 0xio browser extension (3.0 and later) holds an EVM wallet next to your Octra wallet. One recovery phrase gives each wallet an Octra address (`oct...`) and an EVM address (`0x...`). Balances, tokens, NFTs, sends and history for both sit in the same wallet. > [!NOTE] > > EVM networks are public. Private balances, encrypt, decrypt and private sends exist on Octra only. ## Your 0x Address * The 0x address is derived from the same recovery phrase at `m/44'/60'/0'/0/i` (secp256k1), so it matches the address other EVM wallets derive from that phrase. Accounts added from a phrase get the 0x address at the same index as their Octra account. * Only the address is stored. The EVM key is derived for one signature at a time, checked against the 0x address and wiped after. * A wallet imported from an Octra private key has no 0x address. The wallet says why wherever EVM is needed. * A phrase made by an older 0xio version gets a note that other EVM wallets will not accept it. * **Settings > Backup > Private key** shows the EVM key as well, with its own QR code, while EVM is on. ### Turning EVM On or Off After you create a wallet or import a recovery phrase, **Choose your networks** asks which networks to use. Octra is always on; EVM is on by default. Later, **Settings > EVM** turns EVM on or off for the open wallet. Off keeps the same 0x address, so turning it back on shows the same address again. Nothing moves on chain either way. ## Networks ### Mainnet and Testnet The **Mainnet** or **Testnet** chip in the status bar at the bottom of the wallet moves Octra and the EVM networks together: | Side | Octra | EVM networks | | - | - | - | | Mainnet | Octra mainnet | Ethereum, Robinhood Chain and the mainnets you add | | Testnet | Octra devnet | Sepolia, Robinhood Chain Testnet and the testnets you add | ### Managing Networks **Settings > Networks** lists Octra (always on), then every EVM network of the side you are on, in one searchable list with a switch each. A network you turn off stays in the list but is never checked and never shows on Home. ![Settings Networks list with Octra and the EVM networks, each with a switch](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/networks.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=6ca9777fa901c56e09b44fb21020653a) Settings > Networks Tap a network to open its page: * **On or off** * **RPCs:** the network's own RPC, plus any you add. A new RPC must answer with the network's chain ID before it is saved. Tap one to use it; remove the ones you added. * **Explorer and symbol** * **Remove network** for networks you added, or **Reset to default** for built-in ones. Reset brings back the network's own RPC and explorer, turns it on and removes the RPCs you added. ### Adding a Network Tap **+ Add network** in Settings > Networks: * **Popular:** 30 common chains (17 mainnets and 13 testnets), one tap each. * **Search:** also searches the public chain list from chainlist.org, fetched on your first search and kept for a day. * **Custom network:** enter an `https://` RPC URL; the chain ID and details are read from it. ![Add a network sheet with search and popular chains](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/add-network.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=4fe37ce2be75b801de4cf8a1d0d73932) Add a network Whatever you pick, its RPC must answer with the expected chain ID before the review, and again when it is saved. A site can also ask to add a network; that opens a review (see [Connecting to Sites](/wallet/connecting-sites)), and a site can never change the RPC, name or symbol of a network you already have. ## Home The switch in the header picks what Home shows: **All networks**, **Octra** or **EVM**. * **All networks** adds Octra and EVM together in USD. * **EVM** lists ETH and tokens on every EVM network that is on, with a network badge on each logo and an **All EVM** filter. * EVM prices and the EVM chart come from DefiLlama. Testnet tokens have no price. Tap an asset for its page: the price with a 1D, 7D or 1M chart, your balance, actions for that token and its recent activity. ## Tokens * **Listed tokens** show on the built-in networks without any setup, for example USDC, USDT and wOCT on Ethereum. * **Tokens you hold** are found through the 0xio oracle and join Home, unless they look like spam (links or bait words in the name, or a symbol that imitates ETH, USDC, USDT, OCT and similar). Listed tokens, tokens you imported and tokens you have sent are never treated as spam. * **Import token:** paste a 0x contract. It is looked up on every known EVM network at once: the network it is on is filled in, several matches are listed, or a network search opens. * **Settings > Tokens and NFTs** lists every token and collection under **Shown on Home** and **Hidden as likely spam**, with a switch each. Your switch always wins over the spam rules. Hidden tokens leave Home and its total and are not read. ## Receiving **Receive** has **Octra** and **EVM** tabs. The EVM tab shows your 0x address with the logos of its networks. The same 0x address works on every EVM network; send only EVM assets to it. ## Sending Pick **Send** with an EVM asset, or choose EVM when Send asks Octra or EVM on All networks. **1. Pick the asset and recipient** Choose the coin or token and type, paste or pick a 0x recipient. The address book takes 0x contacts. The zero address and the token's own contract are refused. **2. Enter the amount** The live network fee is read from the network. A fee far above the network's gas price, or above the cap of a built-in network, is refused. **3. Review** The review shows the amount, recipient, network and the **Network fee**: in USD first, with the native amount beside it. On testnets and coins with no price it shows the native amount only. **Back** returns to the form; **Send** signs. The review is bound to the wallet, address, network, RPC, recipient, amount and asset it showed, so any change asks for a new review. **4. Status** The screen follows the transfer until it confirms. A transfer still unconfirmed after 5 minutes ends as **Still pending** or **Replaced or dropped**, with a link to the explorer. Once a signed transfer reaches the RPC it is never signed again. If the reply is lost, the wallet follows the transfer by its own hash instead of returning to the form. ## NFTs * Octra and EVM collections share one NFTs list, with a network badge on each cover. The list follows the Home switch. * EVM holdings (ERC-721 and ERC-1155) come from the 0xio oracle; images load through the 0xio proxy. * **Import** an EVM NFT by contract and token ID. The network is detected the same way as for tokens, and the preview checks that this wallet holds it. * A piece's page shows the art, network, standard, contract, token ID and an explorer link, with **Send** (through a review) and **Hide** with Undo. ## History EVM activity shows in **History** next to Octra, newest first, on the same **Mainnet** and **Testnet** pages. See [Wallet Interface](/wallet/interface#history). * Ethereum and Sepolia are read through the 0xio oracle. A network you added is read from its own explorer when that explorer is Blockscout. * A network with no history source is named at the end of the list: **History not available on** that network. * History is read when you open it and when you refresh, not on a timer. * Only ETH, listed, imported and shown tokens appear. Zero-value transfers, spam-flagged and hidden tokens are left out, so address-poisoning transfers never show. * Contract calls read as actions (Swap, Approve, Bridge and others). Tap a row for its details, and **View call data** for the method, every argument and the raw input. ## Bridging wOCT on Ethereum can be bridged to Octra, and OCT to Ethereum, from inside the wallet with the same 0x address. See [0xio Bridge](/products/bridge#bridge-inside-the-extension). # Installation & Setup Source: https://docs.0xio.xyz/wallet/installation Install and set up the 0xio browser extension on Chrome, Brave, Edge, Opera and Firefox: requirements, creating or importing a wallet, and backing up the recovery phrase. The 0xio wallet ships as a browser extension, a desktop app and a mobile app. All three use the same key derivation, so a recovery phrase opens the same Octra wallet in every client. This guide focuses on the browser extension; for desktop and mobile, see [Desktop Wallet](/products/desktop) and [Mobile Wallet](/products/mobile). ## System Requirements ### Supported Browsers The extension is a Manifest V3 build and runs on Chromium browsers and Firefox. > [card] Chrome > > Version 111 or later > [card] Brave, Edge and Opera > > Any version built on Chromium 111 or later > [card] Firefox > > Version 140 or later (142 on Android). In review on Mozilla Add-ons ### Current Version **Extension v3.0.4**, published on the Chrome Web Store. > [!NOTE] > > Other clients version independently: **Desktop v0.6.0** and **Mobile v1.3.3** (v1.3.4 is in progress). Every client installs from [0xio.xyz/install](https://0xio.xyz/install). ## Installation Steps **1. Open the Chrome Web Store listing** Go to the [0xio Wallet listing](https://chromewebstore.google.com/detail/0xio-wallet/anknhjilldkeelailocijnfibefmepcc) on the Chrome Web Store. **2. Add Extension** Click "Add to Chrome" (the button adapts to Brave, Edge and Opera) and confirm. **3. Pin to Toolbar** Pin the extension to your browser toolbar for easy access. The icon shows a padlock while the wallet is locked. **4. Launch Setup** Click the 0xio extension icon to begin setup. ## Creating Your First Wallet The welcome screen offers two ways in: **Create a wallet** and **I already have a wallet**. The Terms of Service and Privacy Policy are linked below them. ![0xio extension welcome screen with Create a wallet and I already have a wallet](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/welcome.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=40c0c07eac5df9c691e908358565312d) The welcome screen **1. Create a password** Choose a password of at least 8 characters. It unlocks 0xio in this browser and never leaves this device. Confirm it on the next screen. **2. Passkey (optional)** Where the browser supports it, you are offered to unlock with a passkey (Touch ID, Windows Hello or a security key). **Not now** skips it; you can add one later in Settings > Security. Passkeys are not available on Firefox. **3. Choose your networks** Octra is always on. EVM is on by default and gives the wallet a 0x address from the same recovery phrase. See [EVM Networks](/wallet/evm). ![Choose your networks step with Octra always on and an EVM switch](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/choose-networks.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=3b6f74cd87c286838ac3d635bc7255ab) Choose your networks **4. Your wallet is ready** The last screen lists your Octra address and, with EVM on, your 0x address. > [!NOTE] > > Your password derives a key with PBKDF2-SHA256 (900,000 iterations on the extension) that protects your vault with AES-256-GCM. It only locks the wallet on this device. Your recovery phrase is what controls your funds on every device. ### Back Up Your Recovery Phrase A new wallet is usable at once; you back up its recovery phrase from Home. Until you do, Home shows a **Back up** tag and Receive asks you to back up first. **1. Open the backup** Tap the **Back up** tag on Home and enter your password. **2. Show the words** The words stay hidden until you tap **Show**, and stay shown until you tap **Hide**, leave the screen or the wallet locks. **Copy phrase** copies them; the extension clears the clipboard after 60 seconds when it can tell nothing else was copied since, which it cannot once the popup has closed. **3. Write them down** Write the words down in order, offline, then tap **I have written it down**. > [!WARNING] > > **CRITICAL SECURITY INFORMATION** > > Your 12-word Recovery Phrase is the master key to your funds. 0xio generates this phrase **locally on your device** using the BIP-39 standard with cryptographic randomness. The Octra master key is derived as `HMAC-SHA512("Octra seed", seed)[0:32]` (no BIP-44 path), and your Octra address is `SHA-256(public key)` encoded as Base58 with an `oct` prefix. The EVM address comes from the same phrase at `m/44'/60'/0'/0/0`. > > **If you lose this phrase, your funds are permanently inaccessible.** > > 0xio cannot recover lost phrases as they never leave your device. #### Best Practices - DO: * Write it down on paper immediately * Store in a fireproof safe * Consider metal backup plates for long-term storage * Split across multiple secure locations for large holdings * Test your backup by importing on a second device #### Best Practices - DON'T: * Store in cloud services (Google Drive, Dropbox, iCloud and similar) * Take photos of your phrase * Store in email or messaging apps * Store digitally on internet-connected devices * Share with anyone, including support staff ## Importing an Existing Wallet If you already have a 0xio wallet or another Octra wallet: **1. Select Import** Click **I already have a wallet** on the welcome screen. **2. Paste your phrase or key** Paste or type a 12 or 24 word recovery phrase, or an Octra private key, into the one field. The extension tells which it is and offers a one-tap fix for a mistyped word. **3. Check the wallet it found** Before you continue, the wallet shows the address it found with its balance. Check it is the one you expect. **4. Password and networks** Create a password for this device and choose your networks, as above. > [!NOTE] > > A wallet imported from an Octra private key has no EVM address; only a recovery phrase gives both. An EVM private key or a 0x address pasted into the field is named as such and not imported as an Octra wallet. ## Watch-Only Wallets Follow any Octra address without its keys: paste the `oct...` address into the same import field. Its balance shows before you add it. **Use Cases:** * Monitoring cold wallet balances * Observing project treasury addresses * Keeping an eye on another device's wallet > [!NOTE] > > A watch-only wallet can view balances and history but cannot send, sign, bridge or run privacy operations. ## Next Steps Once your wallet is set up: > [card] [Fund Your Wallet](/wallet/receiving) > > Learn how to receive OCT and start transacting > [card] [Explore Privacy Features](/wallet/privacy-operations) > > Discover how to encrypt and decrypt balances > [card] [Send Your First Transaction](/wallet/sending) > > Learn how to send public and private transfers > [card] [Security Settings](/wallet/security) > > Configure auto-lock, backup, and advanced security # Wallet Interface Source: https://docs.0xio.xyz/wallet/interface Tour the 0xio browser extension: Home, the status bar, History with Mainnet and Testnet pages, transaction details and call data, Settings and locking. > [!NOTE] > > This guide covers the **0xio browser extension** (v3.0). The desktop and mobile apps share the same ideas (a balance overview, Send, Receive, History and a Privacy hub) with layouts of their own; see [Desktop Wallet](/products/desktop) and [Mobile Wallet](/products/mobile). ## Home ![Extension Home with amounts hidden, the network switch, Send and the asset list](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/home.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=96832bed54bb49a5523debc2d374f6d1) Home on All networks, with amounts hidden ### Header * **Wallet picture and name:** each wallet has its own picture drawn from its address (the 0xio braces around a composition of circles). The mobile app draws the same picture for the same wallet. Click it to open the wallet list. * **Network switch:** three glyphs choose what Home shows: **All networks**, **Octra** or **EVM**. All networks adds Octra and EVM together in USD. * **Settings** ### Balance Card * **Total balance** in USD, over a chart of the day: green when up, red when down, grey when flat. Hover over the chart to read the value at any point. * **Day's change** in percent. It follows the value you hold, so an empty wallet reads 0.0%. * **Eye toggle** to hide amounts. * **Tags** at the top right of the card, most urgent first. For example **Back up** (the recovery phrase is not backed up yet), **Set up private** (the wallet can turn on its private balance) or a private balance on hold. Tap a tag for its sheet. Balances not read yet show `--`, never 0. If one network or token read fails, the total shows what is known and notes that some balances are missing. ### Actions **Send** sits beside a row for **Receive**, **Bridge**, **Privacy** and **History**, each named on hover. On the EVM view, Privacy stays visible but disabled, since private balances exist on Octra only. ### Assets Each asset shows its logo, name, amount and USD value. On Octra you see OCT, the **Encrypted** row (your private balance) and Octra tokens; on EVM you see ETH and tokens, each logo with a badge for its network. Tap an asset for its page: the price with a 1D, 7D or 1M chart that shows price and time under the pointer, your balance, actions that keep that token selected, and its recent activity. An imported token can be removed from its page. ### Status Bar A strip along the bottom of the wallet shows: | Chip | What it shows | | - | - | | Site | The open tab's site and whether the open wallet is connected to it, or **Not connected**. See [Connecting to Sites](/wallet/connecting-sites#the-site-chip) | | 0xio Desktop | Shown when the desktop app is running private proofs for the extension | | Mainnet or Testnet | The side 0xio is on. Switching it moves Octra and the EVM networks together | ## Screens | Screen | What it does | | - | - | | Home | Balance, actions and assets | | Send | Send OCT, tokens, private transfers and EVM assets | | Receive | Your Octra and EVM addresses with QR codes | | Bridge | Bridge to Ethereum, Bridge to Octra, Any chain to OCT, and the Claim page | | Privacy | Encrypt, Decrypt and Claim | | History | Octra and EVM activity on Mainnet and Testnet pages | | NFTs | Your collections, newest pieces first | | Settings | Security, Backup, Connections, Appearance, Networks, Tokens and NFTs, EVM | | Wallet list | Switch, add, rename and remove wallets | ## History History lists everything your address did, newest first, Octra and EVM together. It has two pages: * **Mainnet:** Octra mainnet and the EVM mainnets. * **Testnet:** Octra devnet and the EVM testnets. Rows here carry a **Testnet** tag. Each page shows how many entries it holds. History opens on the side 0xio is on. A network you added yourself is included while it is active. ![History on the Testnet page with Testnet tags on Sepolia transfers](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/history-testnet.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=ef2b0bdedca9010ee5e018952f5377db) The Testnet page of History ### Rows * **Transfers** show what moved, with a sign: received or sent OCT, tokens and NFTs, encrypt, decrypt, private sends and claims, and key updates. * **Contract calls** read as actions: **Swap**, **Approve**, **Bridge**, **Mint**, **Claim** and others, or the method in words when there is no common name. The contract is named (known contracts, listed tokens, verified explorer names) or shown as a shortened address. One row lists every asset the call moved. A call that moved nothing shows no 0 amount, and a failed call shows **Failed** with no sign. * **Pending** transactions show above the list. **Cancel** works only before a transaction reaches a node; one already sent can be hidden on this device instead. * **EVM networks with no history source** are named at the end of the list: **History not available on** that network. A network that could not be read says so, with Refresh to try again. ### Transaction Details Tap any row, Octra or EVM, for its details: the amount, status, type, from and to, token, network and fee when known, with **Copy hash** and **View in explorer** (each network opens its own explorer). Contract calls also list the method and its arguments by name (for example Recipient, Minimum received or Expires), and an approval at the maximum reads **Unlimited**. ![Details of a contract call with status, type, method, contract, fee, network and View call data](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/transaction-details.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=a7fa996e71bbe429eb3e1659661e2365) Details of a contract call ### View Call Data For a contract call, **View call data** opens a page inside the details with: * the method and its selector * the contract * every argument with its type and full value * the raw input ![Call data page with method, function signature, selector, contract and named arguments](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/call-data.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=e3cc8ee1f91105c38c8fe6ea70719572) The call data page ## Privacy Screen The Privacy screen moves OCT between your public and private balances: * **Encrypt:** public to private. * **Decrypt:** private to public. * **Claim:** incoming private transfers, each claimed after its own review. Each operation shows the live network fee before you confirm. See [Privacy Operations](/wallet/privacy-operations). ## Settings * **Security:** password (change or remove it), passkey unlock, auto-lock after 5, 15, 30 or 60 minutes or never, and Reset wallet under This browser. * **Backup:** the recovery phrase and private key of the open wallet, behind your password. With EVM on, the EVM key is shown too. * **Connections:** connected sites and the address book. * **Appearance:** theme (Dim, Day or Octra) and language (English, Chinese, Japanese, Korean or Indonesian). * **Networks:** Octra and every EVM network of the side you are on. See [EVM Networks](/wallet/evm#managing-networks). * **Tokens and NFTs:** show or hide every token and collection. * **EVM:** turn EVM on or off for the open wallet. **Lock wallet** sits at the bottom. ## Lock Screen * Enter your password, or tap **Use passkey** if you set one up. The passkey prompt does not open by itself. * The wallet locks itself after the auto-lock time you set. * While locked, the toolbar icon shows a padlock. ## Watch-Only Wallets Watch-only wallets (added with an address only) show balances and history and can receive funds. They cannot send, sign, bridge or run privacy operations. History marks them **Watch-only**. ## Notices Short messages appear as one pill above the status bar and fade on their own: information after 4 seconds, errors and warnings after 6. Hover to keep one on screen. Copy buttons say **Copied** in place. # NFT Standards Source: https://docs.0xio.xyz/wallet/nft-standards Reference for NFT standards supported by 0xio Wallet on Octra, including SNS-1 collections and the single-token standard with view function details. # NFT Standards 0xio Wallet supports two NFT standards on the Octra Network. The wallet automatically detects which standard a contract uses via on-chain ABI inspection and adapts its behavior accordingly. ## SNS-1 (Spectrum NFT Standard) SNS-1 is the collection-based NFT standard on Octra, designed by [Spectrum](https://x.com/The_SpectrumX). It supports multiple tokens per contract with sequential token IDs, IPFS metadata, royalties, and operator approvals. ### View Functions | Method | Signature | Description | | - | - | - | | `collection_name()` | `→ string` | Collection display name | | `symbol()` | `→ string` | Collection ticker symbol | | `total_supply()` | `→ int` | Total minted tokens | | `token_uri(token_id)` | `int → string` | IPFS URI to metadata JSON | | `owner_of(token_id)` | `int → address` | Current token owner | | `creator_of(token_id)` | `int → address` | Original minter | | `royalty_of(token_id)` | `int → int` | Royalty in basis points (e.g. 500 = 5%) | | `balance_of(addr)` | `address → int` | Number of tokens owned by address | | `get_token_info(token_id)` | `int → string` | Extended token information | ### State-Changing Functions | Method | Signature | Description | | - | - | - | | `mint(to, collection, metadata_uri, royalty_bps)` | `address, string, string, int` | Mint a new token | | `transfer(to, token_id)` | `address, int → bool` | Transfer token ownership | | `transfer_from(from, to, token_id)` | `address, address, int → bool` | Transfer on behalf of owner | | `approve(approved, token_id)` | `address, int → bool` | Approve address for token | | `set_operator(operator, approved)` | `address, bool → bool` | Set operator for all tokens | ### Metadata JSON (IPFS) ```json theme={null} { "name": "Token Name", "description": "Token description", "image": "ipfs://bafybei...", "attributes": [ { "trait_type": "Network", "value": "Octra" }, { "trait_type": "Edition", "value": "Genesis" } ], "collection": "Collection Name", "creator": "oct...", "royalty_bps": 500 } ``` Token IDs are sequential starting from 0. ### Example Contract * **Devnet**: `octFc9ynSen79FarWJqsLExjJTHbp27iQAfy3CG9218pr9z` *** ## Bionts (Single-Contract NFT) Bionts are autonomous, single-contract NFTs created by [Euint Labs](https://biont.network), where each biont is its own deployed smart contract. Total supply is always 1, and the token ID is always 0. The `token_uri()` function returns full SVG markup directly from contract state. It is fully on-chain, with no IPFS or external servers. ### View Functions | Method | Signature | Description | | - | - | - | | `name()` | `→ string` | Token name (also `biont_name()`) | | `symbol()` | `→ string` | Token symbol | | `total_supply()` | `→ int` | Always 1 | | `token_uri()` | `→ string` | Raw SVG markup (on-chain generative art) | | `biont_owner()` | `→ address` | Current owner | | `balance_of(addr)` | `address → int` | Returns 1 if owner, 0 otherwise | | `is_alive()` | `→ bool` | Whether the biont is alive | | `defense_level()` | `→ int` | Current defense stat | ### State-Changing Functions | Method | Signature | Description | | - | - | - | | `transfer_to(new_owner)` | `address → bool` | Transfer ownership (no token_id needed) | | `wander()` | `→ bool` | Move the biont | | `hunt()` | `→ bool` | Hunt another biont | ### On-Chain SVG Unlike SNS-1 which uses IPFS for metadata, Bionts return full SVG markup from `token_uri()`. The SVG is generated from live contract state: name, wander count, defense stats, and alive/dead status all render dynamically. 0xio Wallet detects raw SVG responses (strings starting with `/.json` | | Before reveal | `oct:///unrevealed.json` | | Inline JSON | `data:application/json;utf8,{"name":"...","image":"oct://...","animation_url":"oct://..."}` | Inline JSON is UTF-8 text, not base64, and URLs inside it keep their own percent escapes. The wallet parses it in place (capped at 256 KB) and never sends it to `fetch()`. Percent-encoded and `;base64` bodies are read too. The metadata's `animation_url` (and usually `image`) points at the program, for example: ``` oct:///index.html?seed=&contract=&token=&st=&rpc= ``` ### Two Kinds of Program * **Seed-only** (Geodesics): the program draws from `?seed=` alone and makes no network requests. Its frame has `connect-src 'none'`. * **Chain-state** (the query names `contract=`): the program reads its own state with read-only `contract_call` requests to the Octra JSON-RPC. `st=` carries a state snapshot with commas and colons, and devnet tokens carry `rpc=https://devnet.octrascan.io/rpc`. Mainnet tokens carry no `rpc=`, and the program defaults to `https://octra.network/rpc`. ### What a Program May Reach * The frame runs sandboxed in an opaque origin, with no wallet access, no keys and no storage. * A chain-state program may reach exactly one endpoint, the `/rpc` path of the network its token lives on: `https://octra.network/rpc` on mainnet, `https://devnet.octrascan.io/rpc` on devnet, or the user's own node on a custom network. The frame's `connect-src` names only that endpoint. * An `rpc=` value is kept only when it names that endpoint. Any other host is dropped and the network's endpoint is used, so a token cannot make the wallet contact a server of its choosing. The 0xio RPC proxy is never reachable from a program. * The full query reaches the program byte for byte, through `new URLSearchParams(location.search)`. * In the extension the sandbox policy names the mainnet and devnet endpoints, and each program document narrows it to its own network. On a custom node the program has no network, and its `contract_call` requests are answered through the wallet's read-only bridge instead. * Live programs run on the detail screen only, one at a time. Every read counts against the same per-IP rate limit as the wallet. ### Burns, Reveals and Caching * Tokens can be burned. A burned id reverts on `token_uri`, and the wallet drops it from the gallery instead of leaving an empty tile. A busy or unreachable node is not a burn. * Files at one `oct://` path can change, so program and metadata files are not kept on disk. A program stays in memory for 5 minutes. * `token_uri` is read again when a moving token's page opens, after a manual refresh, and once per session, so reveals, burns and new state show up. ### Chunked Circle Files A large file can be stored in a circle as chunks, the way the Octra reference client uploads it. The extension, the mobile app and 0xio Desktop put the file back together and check it before showing it: in NFT art and living NFT programs on all three, in the mobile app's browser, and in the desktop's Circles screen and browser. A chunk or a whole file that does not match its hash is not shown. * The file's own path answers with content type `application/vnd.octra.circle.chunks+json` and a manifest: `format` `octra.circle.asset.chunks.v1`, the real `content_type`, `encoding` `identity`, `size_bytes` (digits) and the `sha256` of the whole file. * `chunks` lists each part in order: `path` `/.octra/chunks//0000`, `0001` and so on, its `size_bytes` and its own `sha256`. Each chunk is served as `application/octet-stream`. * Limits: up to 32 chunks of at most 2 MiB each, and 32 MiB in all. * The wallet checks the sha256 of every chunk and of the assembled file. ### Discovery Devnet's `octra_tokensByAddress` does not list the new-layout contracts yet. For an imported contract the registry does not list, the wallet reads `balance_of(wallet)`, then `owners_page(start, 50)` when the ABI has it (one owner or `-` per id), else `owner_of` id by id. Burned ids leave gaps past the live supply, so the scan does not stop at `get_total_supply`. These contracts answer `get_name` and `get_symbol` instead of `collection_name` and `symbol`. ### Devnet Test Set | Item | Address | | - | - | | Contract | `octEYrJrypHj2xndM39PokSMM5gGoxEEncKB8k9qGFyhrym` | | Renderer circle | `octBqsZH2c1GBnBrYhUJPJHdmLuT8YMPXFpEKsPKqEYYYfi` | | Metadata circle | `octGsV8bztVuWtMuhAgcrccxx8pS19tMbyqennJp3xegmaK` | *** ## EVM NFTs (Extension) The browser extension also shows ERC-721 and ERC-1155 tokens held by the wallet's 0x address, in the same NFTs list as Octra collections, with a network badge on each cover. * Holdings are read through the 0xio oracle (`/api/evm/nfts`), one network at a time; tokens the explorer flags as scam or spam are dropped. See [Oracle API](/developers/oracle-api#evm-nfts). * Images load through the 0xio proxy. * **Import** takes a contract and token ID; the network is detected, and the preview checks that this wallet holds the token. * **Send** uses `safeTransferFrom` for the token's standard, after a review with the fee. *** ## How 0xio Wallet Detects the Standard 1. **Fetch ABI** via `octra_contractAbi` to determine which methods the contract exposes 2. **Name detection**: tries `collection_name()` (SNS-1), `name()`, `biont_name()`, `get_name()` in priority order 3. **Transfer method**: detects `transfer(to, token_id)` (SNS-1) or `transfer_to(address)` (Bionts) from ABI 4. **Image rendering**: if `token_uri()` returns ` [card] Encrypt > > Move funds from Public → Private > [card] Decrypt > > Move funds from Private → Public > [card] Claim > > Accept incoming private transfers ## Setting Up Your Private Balance A wallet needs its encryption key registered on chain once before it can hold a private balance. This costs a one-time network fee. * **Extension:** Home shows a **Set up private** tag once the wallet can turn on its private balance. Its sheet shows the live one-time fee, and **Turn on** registers the key. * **Desktop:** Home shows a tag once the wallet holds enough OCT for the fee. It opens a review with the live fee; **Turn on** registers the key after Touch ID or your password. The menu bar panel offers the same. * **Mobile:** Home shows a blue **Set up private** tag. Its sheet shows the one-time fee, and **Slide to turn on** registers the key after your PIN or biometrics. The fee shown is the fee signed, and never below 5,000 ou. If it cannot be read, setup is blocked with a retry rather than signed at a default fee. ## Network Proof Rules Every private operation carries zero-knowledge proofs, and the network decides which proof rules it accepts. Before any private action (setup, encrypt, decrypt, private send, claim, key update or migration), the wallet asks the network for its rules. It reads them again right before signing. * **Rules stated:** the action goes ahead under those rules. * **Rules not stated:** private actions pause with a message that the network did not say which proof rules it takes. Nothing is sent. Public sends keep working. * **Network not reachable:** the wallet says it could not reach the network to check its proof rules. Nothing is sent; try again later. * **Rules changed in between:** nothing is sent, and you prepare the action again. When the network binds proofs to the encryption key, every key switch also carries a proof that you own the key. The wallet adds it for you. This works the same in the extension, the mobile app (from v1.3.4) and 0xio Desktop. ## Encrypting Funds (Public → Private) "Shielding" makes your funds invisible to blockchain observers. ### When to Encrypt * Before making confidential purchases * Protecting salary or business payments * Hiding holdings from public view * Preparing for private swaps on DEX * Enhancing overall financial privacy ### How to Encrypt **1. Open the Privacy Screen** Go to the Privacy screen (Encrypt / Decrypt / Claim) in your wallet **2. Select Encrypt** Choose the "Encrypt" option **3. Enter Amount** Input the amount to shield from your Public Balance **4. Review Fee** Check the network fee estimate (quoted live from the node in ou) **5. Confirm** Confirm on the review, which shows the amount and the live network fee **6. Wait for Confirmation** A proof-generation overlay shows progress; confirmation follows shortly after ### Technical Process When you encrypt funds: 1. **Local Encryption:** Your device performs FHE encryption on the amount 2. **Transaction Creation:** Encrypted payload is prepared 3. **Signing:** You sign the transaction with your private key 4. **Broadcast:** Encrypted transaction sent to network 5. **Validation:** Validators process without seeing the amount 6. **Confirmation:** Funds move to your Private Balance > [!NOTE] > > **Privacy Guarantee:** The encryption happens entirely on your device before any data touches the internet. Validators never see your plaintext amount. ### Costs Encrypt fees are denominated in **operational units (ou)** and quoted live from the node, with a floor of 5,000 ou (decrypt and private sends share it). The fee is paid from your public balance, so the amount plus the fee must fit in it. If the fee cannot be read, the operation is blocked with **Network fee unavailable** instead of using a fixed fee. Encrypt also generates proofs locally, so it takes longer than a plain transfer. ## Decrypting Funds (Private → Public) "Unshielding" makes your funds publicly visible again. ### When to Decrypt * Sending to exchanges that don't support FHE * Interacting with public DeFi protocols * When faster transaction speed is needed * Reducing gas costs for routine operations ### How to Decrypt **1. Open the Privacy Screen** Go to the Privacy screen and select the Decrypt tab **2. Select Decrypt** Choose the "Decrypt" option **3. Enter Amount** Specify the amount to unshield from your Private Balance (balance validation prevents over-decryption) **4. Review Fee** Check the network fee estimate (in ou) **5. Confirm** Confirm on the review. The network fee is paid from your public balance **6. Confirmation** Funds appear in your Public Balance after confirmation > [!WARNING] > > Once funds are decrypted to your public balance, their movement becomes visible on the blockchain. Consider your privacy needs carefully. ## Claiming Private Transfers When someone sends you a private (stealth) transfer, you claim it to move the funds into your private balance. ### Finding Pending Transfers 1. Open the **Privacy screen** and switch to the Claim tab 2. Run the stealth scan to query the blockchain (live progress is shown while it scans outputs) 3. Detected transfers appear in a list ### Claim Process **1. View Pending Transfer** See details of incoming private transfer: * Sender address * Estimated amount (if disclosed) * Timestamp **2. Click Claim** Press "Claim" button next to the transfer **3. Review Fee** A network fee (in ou) covers decryption and the state update **4. Confirm Transaction** Sign the claim transaction **5. Funds Added** After confirmation, funds appear in your Private Balance ### Claim Fees * **Denominated in ou** and quoted live from the node before you confirm, never below 3,000 ou * **Paid By:** Recipient (you) * **Reason:** Computational cost of decryption and the on-chain state update ### Bulk Claiming If you have multiple pending transfers, use **Claim All** to process every detected transfer in one action. The wallet still settles each claim on-chain individually, so a fee applies per claim. On the mobile app, Claim all runs under one review with Pause and Stop. ## Encryption Key Updates and Held Balances From time to time the network upgrades its encryption scheme. When that happens: * **Key update:** the wallet asks you to re-register your encryption key before you can encrypt or decrypt. If you hold a private balance, it can be migrated to the new key in the same transaction. The review shows the live fee. * **Balance on an older key:** when the network offers it, **Migrate** moves a balance held under an older key with a proof about the encrypted balance itself. The wallet picks this route on its own when the network lists it as open. * **Held balance:** when the network holds a private balance (for example one sealed under an older scheme that waits for a network migration), Home shows a tag such as **Private on hold** and the Encrypted row reads **On hold**. The tag's sheet shows the reason. Your public balance is unaffected and nothing is lost. * **Refresh first:** a private balance can carry only a limited number of encrypted layers. When an operation meets that limit, the mobile app and the desktop menu bar say the balance is refreshed first and show the fee for both transactions under one review. In the desktop window the form shows Refresh key first with the layers as dots of eight; Review refresh opens its own review (layers now and after, the live fee and what runs next), and the operation is reviewed on its own after it. ## Faster Proofs with 0xio Desktop Proofs run on your device. When [0xio Desktop](/products/desktop) runs on the same computer and is paired with the extension (see [Pairing with the Extension](/products/desktop#pairing-with-the-extension)), the extension runs its proofs in the desktop app, which is faster than in the browser. The desktop app never receives the key that signs your transactions. ## Privacy Best Practices ### Maximize Privacy > [accordion] Keep Most Funds in Private Balance > > Maintain majority of holdings in encrypted state. Only decrypt what you need for immediate use. > [accordion] Use Separate Addresses > > Consider using different addresses for public and private activities to reduce correlation. > [accordion] Encrypt Immediately After Receiving > > When receiving public transfers, encrypt them quickly to minimize exposure. > [accordion] Batch Privacy Operations > > If making multiple encrypt/decrypt operations, batch them to save on fees. ### Balance Management Strategy **Conservative Privacy Model:** ```text theme={null} Public Balance: 5-10% (for gas and immediate needs) Private Balance: 90-95% (long-term holdings) ``` **Balanced Model:** ```text theme={null} Public Balance: 30% (active trading, DeFi) Private Balance: 70% (savings, private transactions) ``` **Public-First Model:** ```text theme={null} Public Balance: 70% (frequent trading, lower fees) Private Balance: 30% (selective privacy) ``` ## Monitoring Privacy Operations ### Transaction History View all privacy operations: 1. See all encrypt/decrypt operations 2. Track gas costs over time 3. Verify operation success ## Troubleshooting ### "Insufficient Balance for Encryption" * Check you have enough **Public Balance** to cover the amount plus the network fee * The wallet validates that amount + fee fits your public balance before submitting ### "Private Actions Are Paused" or "Unavailable on This Network" * The network did not state the proof rules it takes, so the wallet sends nothing private * Public sends still work * Try again later, or switch to a network that states its rules (see [Network Proof Rules](#network-proof-rules)) ### "Claim Transaction Failed" * Ensure sufficient OCT to cover the network fee * Try refreshing pending transfers list * Transfer may have been auto-claimed ### "Decrypt Taking Too Long" * Network congestion can delay operations * Check transaction status in History * Wait longer during peak times * Consider increasing the fee (ou) > [!TIP] > > For urgent privacy operations during network congestion, use the custom fee option to set a higher ou amount for priority processing. # Receiving Funds Source: https://docs.0xio.xyz/wallet/receiving Receive OCT and Octra tokens in 0xio using a single oct... address that accepts both public and private transfers from any sender. Your Octra address receives both public and private transfers. In the browser extension, a wallet with EVM on also has a 0x address for Ethereum and other EVM networks; see [EVM Networks](/wallet/evm#receiving). ## Your Wallet Address ### Address Format * **Format:** `oct...` (47 characters: `oct` + 44 Base58) * **Example:** `octabc123def456ghi789jkl012mno345pqr678satu901` * **Universal:** This single address receives both public and private (stealth) transfers * **Deterministic:** Derived from your Ed25519 public key as `SHA-256(public key)` encoded in Base58 with an `oct` prefix ### Finding Your Address **1. Open Receive Tab** Click "Receive" in the main navigation **2. Pick the address** In the extension, Receive has an **Octra** tab and an **EVM** tab. Send only Octra (OCT) assets to the Octra address, and only EVM assets to the 0x address **3. Copy or Share** * Copy the address (the button shows **Copied** in place) * Show the QR code for in-person transfers * Share it through messaging apps ![Receive screen on the EVM tab with the 0x address, QR code and network logos](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/receive-evm.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=551274cbe82223b18b9da2d9812414f7) Receive on the EVM tab > [!NOTE] > > Until a new wallet's recovery phrase is backed up, Receive asks you to back it up first. ## Sharing Your Address ### Copy to Clipboard 1. Click on your address 2. Visual confirmation appears 3. Paste anywhere to share ### QR Code Perfect for in-person payments: 1. Tap the QR code icon 2. The other person scans it with their wallet 3. Your address fills into their Send screen 4. **Optional:** Save the QR code image ### Payment Requests (Mobile) On the mobile app, Receive has a keypad for an amount. With no amount the QR and **Share** carry your plain address. Once you type an amount, the QR becomes a payment request and the button reads **Share request**. **Copy address** always copies the plain address. When someone scans the request with the 0xio mobile app, Send opens with your address and the amount already filled in, in public OCT. They still review it and slide to send. The request is a plain text URI: ``` octra:
?amount= ``` * `amount` is in OCT, not micro-OCT: a plain decimal with up to 6 decimal places, greater than 0 and under 1,000,000,000 (for example `octra:oct...?amount=12.5`) * The `octra:` scheme is case-insensitive * Only `amount` is read; any other parameter is ignored. There is no token, memo or network parameter, so the request is always for OCT on the network the payer is using * An empty or zero amount is treated as a plain address * An invalid address, or an amount that is not a plain decimal, refuses the whole request; nothing is filled in To build a request from your own app or site, encode the URI above in a QR code. ## Receiving Public Transfers When someone sends you a public transfer: **1. Transaction Broadcast** Sender broadcasts transaction to network **2. Validation** Validators confirm the transaction (3-5 seconds) **3. Automatic Credit** Funds appear in your **Public Balance** immediately **4. Notification** On the mobile app, optional push notifications tell you about incoming transactions ### No Action Required Public transfers are automatic. Just share your address and wait for confirmation! ## Receiving Private Transfers Private (stealth) transfers require a claim step: the sender posts an encrypted stealth output that only you can detect, and you claim it into your private balance. ### Why Claiming Exists This design: * Lets only the recipient detect and decrypt incoming stealth outputs * Gives recipients control over when the claim fee is paid * Allows recipients to validate transfers before claiming ### Claiming Process **1. Open the Privacy Screen** Go to the Privacy screen (Encrypt / Decrypt / Claim) in your wallet. **2. Scan for Pending** The wallet scans the network's stealth outputs for ones addressed to you, resuming from where it last stopped. Waiting transfers are checked again every 30 seconds; detected transfers appear in the Claim list. **3. Click Claim** Click "Claim" next to a pending transfer, or claim them all at once. **4. Confirm** Confirm on the claim review, which shows the live network fee. Before any proof, the wallet reads the output fresh from the node and checks its amount. **5. Credited** Funds move to your **Private Balance** after confirmation. > [!NOTE] > > On the mobile app, incoming private transfers are listed under **Waiting for you** in the Privacy sheet. Each has its own **Claim**, and **Claim all** claims them one at a time under one review, with Pause and Stop. ### Claim Fees * **Denominated in ou** and quoted live from the node before you confirm * **Paid By:** Recipient (you) * **Why:** Claiming runs decryption and writes a state update on-chain ## Monitoring Incoming Funds ### Transaction History View all received funds in the **History** tab: 1. Filter by "Received" 2. See sender addresses 3. View amounts and timestamps 4. Check confirmation status ### External Monitoring Use the **0xio Telegram Bot** for real-time alerts: * Instant notifications to Telegram * Monitor multiple wallets * Privacy mode available * [Learn more about the bot](/products/telegram-bot) ## Address Book Save addresses that frequently send you funds: 1. Go to **Settings > Connections > Address book** (extension) or **Address Book** (mobile and desktop) 2. Add a contact 3. Enter a name and an Octra address (the extension also takes 0x addresses) **Benefits:** * Quickly identify senders * Verify legitimate addresses * Avoid phishing attempts ## Best Practices ### Security > [!WARNING] > > **Never share your private key or recovery phrase!** > > Only share your public wallet address (`oct...`). Anyone asking for your private key or recovery phrase is trying to steal your funds. ### Address Verification When receiving large amounts: 1. **Verify address through multiple channels** 2. **Send a small test amount first** 3. **Confirm receipt before large transfer** 4. **Double-check address character-by-character** ### Privacy Considerations **For Public Transfers:** * Sender can see your current balance * Transaction amount is visible to everyone * Consider using a dedicated public address **For Private Transfers:** * Sender only knows your address * Amount is encrypted * Your balance remains confidential > [!TIP] > > **Pro Tip:** For recurring private payments (like salary), create a dedicated address to organize your finances while maintaining privacy. ## Troubleshooting ### "I sent funds but don't see them" 1. **Check transaction status** on block explorer 2. **Wait for confirmations** (up to 30 seconds during congestion) 3. **Verify correct address** was used 4. **Check if it's a private transfer** requiring claim 5. **Refresh your balance** manually ### "Private transfer isn't showing" 1. Go to the **Privacy screen** and open the Claim tab 2. Wait for the scan to finish 3. Claim any found transfers 4. Make sure the sender's network matches yours: a transfer on devnet is not visible on mainnet ### "Address rejected as invalid" * Ensure address starts with `oct` * Verify exactly 47 characters * Check for typos or extra spaces * Try copying directly from sender # Security Features Source: https://docs.0xio.xyz/wallet/security Secure your 0xio Wallet with auto-lock timers, password rules, seed phrase protection, and advanced settings to keep funds and privacy safe. 0xio provides multiple layers of security to protect your funds and privacy. The protections below apply across the browser extension, desktop app, and mobile app, with a few platform-specific additions noted where relevant. ## Biometric Unlock All three clients support biometric unlock with a password or PIN fallback: * **Extension:** a passkey (Touch ID, Windows Hello or a security key) can unlock the wallet instead of the password. Set it up in **Settings > Security**. The lock screen opens the passkey prompt only when you tap **Use passkey**. Passkeys are not available on Firefox, and changing or removing the password turns the passkey off. * **Desktop:** Touch ID / Windows Hello. On desktop, biometrics are the **primary** prompt for sensitive actions, with your password as the fallback * **Mobile:** Face ID / Touch ID / fingerprint for transaction approval and unlock, with PIN fallback > [!NOTE] > > Biometrics gate access to the locally encrypted vault. They never replace your recovery phrase, which remains the only way to restore the wallet on a new device. ## Auto-Lock Automatically lock your wallet after periods of inactivity. ### Configuration 1. Navigate to **Settings > Security** and open the auto-lock setting 2. Select timeout period: * 5 minutes * 15 minutes * 30 minutes * 1 hour * Never (not recommended) ### How It Works * Timer resets with any wallet interaction * Locked wallet requires your password (or passkey) to reopen * Locking clears decrypted private balances and wallet data held in memory, and shuts down the private balance engine * While the extension is locked, its toolbar icon shows a padlock and sites receive no address or balances > [!WARNING] > > **Security Recommendation:** Use 15 minutes or less for shared or public computers. ## Password Management ### Changing Your Password **1. Open Security Settings** Go to Settings > Security and choose Change password **2. Enter Current Password** Verify your identity with existing password **3. Enter New Password** Create a strong new password (at least 8 characters, 12 or more recommended) **4. Confirm New Password** Re-enter to confirm **5. Apply Changes** Password updated immediately > [!NOTE] > > **Important:** Changing your password does NOT require your Recovery Phrase. The password only protects local access to your encrypted vault: it derives a key with PBKDF2 (900,000 iterations on the extension, 600,000 on desktop; mobile gates the vault behind a PIN at 100,000 iterations) that decrypts an AES-256-GCM vault. ### Password Requirements * **Minimum:** 8 characters (12+ recommended) * **Recommended:** * Mix of uppercase and lowercase * Include numbers * Include special characters (!@#\$%^&\*) * Avoid common words or patterns ## Recovery Phrase Management ### Viewing Your Recovery Phrase > [!WARNING] > > **Extreme Caution:** Only view your recovery phrase in a secure, private location. Never screenshot or photograph it. 1. **Settings > Backup**, then the recovery phrase (extension). On a new wallet, the **Back up** tag on Home opens the same backup 2. Enter your password 3. Write down all 12 words in order 4. Hide or close the view immediately ### Multiple Devices Use your Recovery Phrase to access the same wallet on multiple devices: * Each device can have its own password * Balance changes sync via blockchain automatically * Transactions from any device reflect on all devices * No cloud synchronization required ### Device Security **Recommended:** * Different passwords per device * Enable auto-lock on all devices * Never store recovery phrase digitally * Regular security audits ## Advanced Security Options ### Export Private Key > [!WARNING] > > **Advanced Users Only:** Exporting your private key is rarely necessary and increases security risk. **Use Cases:** * Importing to other Octra-compatible wallets * Advanced development and testing * Cold storage solutions **How to Export:** 1. **Settings > Backup**, then the private key (extension). With EVM on, the EVM key is shown too, with its own QR code 2. Enter password 3. Copy private key (never screenshot) 4. Store securely (treat like recovery phrase) ### Transaction Signing All transactions require explicit approval: * **Review:** Every transaction, approval included, shows full details before anything is signed (including the contract method and its arguments for contract calls) * **Confirm:** In the extension the review ends with a button pair: **Back** and **Send** for your own sends, **Reject** and the action (Connect, Sign, Send and so on) for site requests. The desktop menu bar asks for Touch ID or your password after its review. On mobile every send, approval, signature and connection is confirmed with a slide, then Face ID or the PIN when **Ask before sending** (sends) or **Ask for PIN before signing** (sites) is on * **Fee shown is fee signed:** the network fee on the review is read live, and that fee is the one signed. A fee that cannot be read blocks the action instead of falling back to a default * **Signing wallet shown:** Every approval, message signatures included, names the wallet and network it signs with * **Bound to what you reviewed:** a transaction is signed only on the wallet and network its review showed. Switching wallet or network before it is sent refuses it * **What you see is what is signed:** site reviews show the exact arguments, amounts and memo that are signed. The extension refuses a site request whose fields disagree before any review * **Never re-signed on its own:** a busy node gets the same signed transaction again; a used nonce or an unclear result asks you to review or check History, never signs a new one * **No Auto-Sign:** Even for connected dApps * **Cancel Anytime:** Before signing completes ### Connection Management #### Connected dApps View and manage website connections: 1. **Settings > Connections > Connected sites** (extension) 2. See every connected site with its wallets and network 3. Open a site to remove a wallet from it or disconnect it 4. Disconnecting does not revoke token approvals already on chain See [Connecting to Sites](/wallet/connecting-sites). #### Permission Types Octra sites ask for these permissions when they connect: | Permission | Description | | - | - | | `accounts` | Your address and public balance | | `public_transactions` | Ask you to sign public transactions and messages | | `contract_calls` | Ask you to sign smart contract calls | | `contract_views` | Read-only contract calls | | `private_balance_read` | Your private balance, decrypted values and pending transfers (sensitive) | | `private_proofs` | Encrypting values and building proofs (sensitive) | | `private_transfers` | Ask you to send private transfers (sensitive) | | `private_claims` | Ask you to claim private transfers (sensitive) | EVM sites see only the 0x address of the wallet in use and its balances. > [!TIP] > > **Best Practice:** Regularly audit connected dApps and revoke unused connections. ## Session Security ### Connected dApp Sessions Each connected site holds a session scoped to the permissions you granted. Review and revoke them under **Connected sites** (see [Connection Management](#connection-management)). Disconnecting immediately ends that site's session. ### Secure Logout Always lock the wallet on shared devices: 1. Extension: **Settings > Lock wallet** 2. Desktop: **Lock** in the menu bar panel, or Cmd+L while the panel is open 3. Unlock requires your password, PIN, passkey or biometrics ## Watch-Only and Balance Privacy * **Watch-only mode:** Import an address without its private key to monitor balances and history. Watch-only wallets cannot send, sign, or run privacy operations. * **Hide balances:** Toggle the eye control to mask balance amounts on screen, useful in public or shared settings. ## Mobile-Specific Protections The mobile app adds device-level safeguards: * **Screenshot and screen-recording prevention** on sensitive screens * **Secure clipboard** handling for copied addresses and keys * **Jailbreak / root detection** with an on-screen warning when a compromised device is detected * **PIN lock** with auto-lock and rate limiting, backed by the device keychain/keystore * **Honest browser lock:** the in-app browser shows a lock only on https pages, marks plain http as not secure, and shows none for `oct://` content * **Lookalike site warnings:** the in-app browser flags a site whose name is spelt close to a listed app, or uses another alphabet, and warns before it can connect, sign or send (on by default, in Browser settings) * **https sites only:** only pages served over https can reach the wallet from the in-app browser * **Verified WalletConnect sites:** a WalletConnect site is named by its verified address, or tagged **Unverified**; a site that fails verification or is flagged as a scam is refused * **Erase this phone:** lists each wallet as backed up or not before anything is erased, and asks once more when one is not ## Network Security ### RPC Endpoint Validation 0xio validates RPC endpoints before connecting: * **Known Networks:** Pre-configured RPCs for Octra mainnet and devnet and the built-in EVM networks * **Custom Octra Networks:** a node must answer as an Octra node before it is added, with a warning that it sees your address and the transactions you sign there. Its URL must be http or https * **Custom EVM Networks:** the RPC must be https and must answer with the network's chain ID before it is saved. A site can never change the RPC, name or symbol of a network you already have * **Optional RPC proxy:** wallets talk to the Octra node directly. The 0xio RPC proxy is used only when you turn it on, never as a silent fallback ### Phishing Protection Built-in protections against common attacks: * **Address Validation:** Octra addresses must be `oct` plus 44 Base58 characters; pasted spaces are stripped. Sends to your own address are refused * **Transaction Preview:** Always shows full details, the signing wallet and the network * **Spam filtering:** tokens and NFT collections that look like spam (links or bait words in the name, or a symbol imitating a known token) start hidden, and EVM history leaves out zero-value transfers and spam tokens, so address-poisoning transfers never show * **Lookalike sites (mobile):** see below * **Wallet pictures:** Each wallet's picture is drawn from its address, the same in the mobile app and the extension, so a wrong wallet looks wrong before you check the address * **Isolated content:** living NFTs run sandboxed with no access to the wallet or its keys; a program may reach only its own network's RPC. On desktop, `oct://` pages and the Circles preview also run in isolated frames with a security policy of their own ## Security Best Practices ### DO: * Enable auto-lock (15 minutes or less) * Use a strong, unique password * Store recovery phrase offline securely * Regularly review connected dApps * Verify addresses before sending * Start with small test transactions * Keep browser and extension updated ### DON'T: * Share password or recovery phrase with anyone * Store recovery phrase digitally * Screenshot or photograph sensitive information * Use the same password across services * Leave wallet unlocked on shared devices * Approve transactions without review * Connect to untrusted dApps ## Emergency Procedures ### Compromised Device If you suspect your device is compromised: **1. Secure Access on New Device** Install 0xio on a secure device using your recovery phrase **2. Transfer Funds** Immediately transfer all funds to a new wallet **3. Create New Wallet** Generate a new wallet with a fresh recovery phrase **4. Abandon Old Wallet** Never use the compromised wallet again ### Lost Recovery Phrase > [!WARNING] > > **Critical:** If you lose your recovery phrase and lose access to all devices with the wallet: > > **Your funds are PERMANENTLY INACCESSIBLE.** > > 0xio cannot recover lost recovery phrases. They are generated locally and never transmitted. **Prevention:** * Maintain multiple secure backups * Consider metal backup solutions * Store in separate physical locations * Test recovery on a second device ## Security Audit ### Regular Security Checks Perform monthly security audits: * [ ] Recovery phrase backed up securely * [ ] Password is strong and unique * [ ] Auto-lock enabled * [ ] Connected dApps reviewed * [ ] No suspicious transaction history * [ ] Wallet apps are on the latest version * [ ] Device security software updated ### Reporting Security Issues Found a vulnerability? **Report to:** [security@0xio.xyz](mailto:security@0xio.xyz) * Do not publicly disclose until patched * Include detailed reproduction steps # Sending Transactions Source: https://docs.0xio.xyz/wallet/sending Send public or private transfers of OCT and tokens from 0xio with step-by-step instructions for standard and FHE-encrypted transactions. 0xio lets you send native OCT, Octra tokens and private stealth transfers. To send a token instead of OCT, pick it on the Send screen; the steps below otherwise apply to both. Sending ETH and tokens on EVM networks is covered in [EVM Networks](/wallet/evm#sending). Every send ends on a review before anything is signed. In the extension the review has **Back** and **Send**; the mobile app confirms with a slide. ![Review send sheet with amount, recipient, sender, network, fee and total, and Back and Send buttons](https://mintcdn.com/0xio/IfCaCnXJw-kyqMLz/images/extension/send-review.png?fit=max\&auto=format\&n=IfCaCnXJw-kyqMLz\&q=85\&s=8b58ca03e4ba054d0c4c27f144d8167c) The send review in the extension ## Standard Public Send Send a transparent transaction visible on the blockchain. **1. Open Send Tab** Click "Send" in the main navigation **2. Enter Recipient** Enter the recipient's Octra address (starts with `oct`) * 47 characters (`oct` + 44 Base58) * Validates automatically * Can paste or scan QR code. On mobile, scanning a payment request (`octra:
?amount=`) also fills the amount; see [Payment Requests](/wallet/receiving#payment-requests-mobile) **3. Enter Amount** Specify the amount to send * Enter in OCT units * Shows fiat equivalent (if available) * Displays available balance **4. Select Source** Choose **"Public Balance"** as the source **5. Review Transaction** Review the transaction details: * Recipient address * Amount to send * Network fee (see below) * Total including fees **6. Confirm and Sign** Tap **Send** on the review to sign. In the extension a result screen then shows **Sending**, and **Sent** once the network confirms it ### Transaction Details * **Speed:** Typically a few seconds, subject to network conditions * **Privacy:** Transaction is visible on the blockchain * **Network fee:** Quoted live from the node via `octra_recommendedFee` and shown before you confirm > [!NOTE] > > Fees are denominated in **operational units (ou)** and displayed converted to OCT (1 OCT = 1,000,000 ou). The wallet reads its estimates from the node; you can pick another tier or set a custom ou value. One fee drives everything for a send: the fee shown, **Max**, the balance checks and the signature. If the fee cannot be read, the extension shows **Network fee unavailable** and blocks Review instead of guessing. See [Network Fees](#network-fees). ## Private Send Send a stealth transfer where the amount stays confidential. The wallet derives a one-time stealth address for the recipient via ECDH (x25519), encrypts the amount with PVAC-HFHE, and attaches a zero-knowledge range proof, all generated locally on your device. **1. Open Send Tab** Navigate to Send in the main menu **2. Enter Recipient** Input the recipient's Octra address **3. Enter Amount** Specify the amount to send **4. Select Private Balance** Choose **"Private Balance"** as the source **5. Review Encrypted Details** Review transaction information: * Recipient (visible) * Amount (will be encrypted) * Network fee (higher than a plain transfer because of proof generation) **6. Confirm** Confirm on the review. Proofs run before sending and can take a few minutes; the result screen shows **Queued** while they run ### How Private Transfers Work 1. **Local Proof Generation:** Your device encrypts the amount (PVAC-HFHE) and builds a range proof. A full-screen progress overlay shows each step (verify → encrypt → range proof → submit). 2. **Stealth Output:** A one-time stealth output is derived from the recipient's view key via ECDH so the recipient, and only the recipient, can detect it. 3. **Broadcast:** The encrypted payload and proof are submitted to the network. 4. **Validation:** Validators verify the proof on encrypted data without learning the amount. 5. **Claim:** The recipient scans for incoming stealth outputs and claims them into their private balance. ### Transaction Details * **Speed:** Slower than a public send because proofs are generated locally * **Privacy:** Amount is encrypted; the sender's address may be visible, the recipient is shielded behind a stealth output * **Network fee:** Quoted live from the node, and never below 5,000 ou (proof-bearing transactions cost more ou than a plain transfer) > [!WARNING] > > Private transfers require the recipient to **claim** the stealth output before the funds appear in their private balance. See [Receiving Funds](/wallet/receiving) and [Privacy Operations](/wallet/privacy-operations). ## Bulk Send Send to multiple recipients in one flow. Each recipient is submitted as its own transaction and tracked individually, so the batch reports per-recipient success and failure. **1. Open Bulk / Batch Mode** Switch the Send screen to bulk with the "Bulk" tab (extension and desktop). The mobile app has no bulk send. **2. Add Recipients** Add addresses with individual amounts. * **Extension:** add up to 5 recipients, each with its own address and amount; remove rows with the trash icon. Bulk always sends OCT, and every address and amount is checked. * **Desktop:** add rows individually or paste/import a CSV (`address,amount` per line). The panel beside the rows shows the total, the fee per send and in total and what is left, flags duplicate addresses, and warns when the total exceeds your available balance. The review lists every send in order with Reject and Send, and the progress shows in the same card. **3. Choose Balance Type** Select the Public balance as the source. Private (FHE) bulk sends are available only in [0xio Desktop](/products/desktop); the extension sends privately to one recipient at a time. **4. Review Total** Review the total amount (fees included), the per-recipient breakdown and the fee. **5. Confirm** Confirm and sign. The wallet processes each transfer and reports how many succeeded. In the extension a bulk send reads **Sent** only once every transfer confirms on chain. ### When to Use Bulk Send * **Time Saving:** Queue many transfers from a single screen instead of one at a time * **Ideal For:** * Payroll and salary payments * Airdrops and token distributions * Multi-party settlements * Team payments > [!NOTE] > > Bulk send submits a separate transaction per recipient, so total fees scale with the number of transfers; there is no per-batch discount. ## Confirming a Send (Mobile) On the mobile app every send ends on a review: amount, recipient (with "First time sending to this address" when new), memo, fee, network and total. Slide to send to confirm, or tap outside the review to back out. With **Ask before sending** on (Settings, Security), Face ID or your PIN follows the slide; there is no address that skips it. While a send runs you can close its progress screen. It keeps going on the wallet and network it was made on, and its result arrives as a notice. ## Network Fees Every Octra fee is the node's live quote for that operation (`octra_recommendedFee`). No wallet signs a fee it made up: when the quote cannot be read, the action waits with a retry. ### Fee Tiers The node quotes a **minimum**, a **recommended** and sometimes a **fast** fee. The wallet offers them as presets, plus **Custom**: * **Recommended** is the default for every send. * **Fast** is optional. When the node gives no fast quote, only the Fast preset is hidden; the other presets and Custom stay. * **Custom** takes a whole number of ou, at least the operation's floor (below) and the network's live minimum, and at most 10,000,000 ou. Higher fees confirm faster. During congestion a higher ou value helps a transaction get processed sooner. ### Floors for Private Operations Private operations carry proofs, so each has a floor. Every preset is raised to it, and a custom fee below it is refused. | Operation | Lowest fee | | - | - | | Key switch (turning on or updating the private balance) | 5,000 ou | | Encrypt | 5,000 ou | | Decrypt | 5,000 ou | | Private (stealth) send | 5,000 ou | | Claim | 3,000 ou | ### Ceiling Sends and private operations top out at **10,000,000 ou**. A custom fee above it is refused, and a node quote above it is not used. Contract deploys pay a fee set by the size of the code instead. ### The Fee You Review Is the Fee You Sign Right before signing, the wallet reads the live fee again. The reviewed fee is signed as long as the network still takes it. When the network's minimum has moved above it, nothing is signed and you are asked to review again. > [!NOTE] > > **Running your own node:** the wallet takes the first of the node's `recommended`, `base_fee` and `minimum` that is a whole number from 1 to 10,000,000 ou, and only when the minimum is not above it. A node that quotes none of them shows that its fee is unavailable. A `fast` value is used only when it is at least the recommended fee. EVM networks quote their own fees; see [EVM Networks](/wallet/evm#sending). ## Transaction Hash The wallet computes each transaction's hash on your device before it sends it, the same way the network computes it. That is the hash shown in the result and in History, and you can look it up on the explorer even when the node's reply is lost. ## What Happens After You Send The node answers every submit, and the wallet acts on that answer. A transaction is never signed a second time on its own. | Node answer | What the wallet does | | - | - | | Accepted | Waits for the network to confirm it | | Not accepted for now (busy or temporary) | Sends the **same signed bytes** again, up to 3 tries in all | | Nonce already used | Asks you to review again; nothing is re-signed. The mobile app first checks whether the nonce went to this same transaction | | Rejected | Stops with **The network rejected this transaction.** Nothing is spent | | Too large | Stops with **This transaction is too large.** | | No reply | Checks the network for the transaction before calling it failed | > [!NOTE] > > For developers and node operators: the codes sent again unchanged are `107`, `110`, `117` and `-32005`. Code `112` means the nonce is already used. Codes `100` to `105`, `108`, `109`, `111`, `113` to `116`, `118`, `-32600` and `-32602` are rejections, and HTTP `413` is too large. ### Not Confirmed Sometimes the result of a send cannot be read: the reply times out, arrives twice, or says the nonce is taken. The mobile app then looks the transaction up on chain, first by its hash, then by your address and nonce: * **Found:** it counts as sent. * **Another transaction took that nonce:** you are asked to review again. * **Neither:** the send stays pending and reads **Not confirmed**, with **Could not check whether this went through. Check History before sending again.** There is no Try again. In the extension such a send stays on **Sending** until History settles it. > [!WARNING] > > When a send shows **Not confirmed** or stays on **Sending**, open **History** before you send the same payment again. It may already be on its way, and a second send would pay twice. ## Transaction States ### Pending * Transaction submitted to the network * Awaiting validator confirmation, usually a few seconds * A transaction that may have reached the node stays on **Sending** (or **Not confirmed** on mobile), and History settles it. Only a rejection from the node returns you to the form ### Confirmed * Transaction included in block * Funds transferred successfully * Transaction hash available for explorer ### Failed * Transaction rejected by the node, dropped or expired * Nothing is spent when the node rejects a transaction; the extension shows the node's reason * **Common causes:** * Insufficient balance for gas * Network connectivity issue * Invalid recipient address ## Adding a Memo Include a message with your transaction: 1. Open the memo field on the Send screen 2. Enter memo text (up to 200 bytes in the extension; the length is counted in bytes, so some characters count as more than one) 3. **Note:** The memo is stored on-chain and visible to everyone. Memos go with public OCT sends; token sends carry none **Use Cases:** * Payment references * Invoice numbers * Personal notes > [!WARNING] > > Memos are permanent and cannot be edited after the transaction is confirmed. ## Best Practices ### Security * Always double-check recipient addresses * Send a small test transaction first for large amounts * Verify addresses through multiple channels * Be cautious of phishing attempts ### Privacy * Use **Private Balance** for sensitive transactions * Encrypt funds before private transfers * Monitor pending claims for incoming private transfers ### Efficiency * Use **Bulk Send** for multiple recipients * Consider standard priority unless urgent * Group similar transactions to save on fees