--- url: /getting-started/welcome.md description: Welcome to Nox Protocol - Composable Privacy for DeFi --- # Your Toolkit to Build Privacy Apps ## Welcome to Nox Nox is a **privacy layer** that enables confidential computations on encrypted data while preserving full DeFi composability. It combines **on-chain smart contracts** with **off-chain Trusted Execution Environments (Intel TDX)** to process encrypted data without ever exposing plaintext on-chain. With Nox, you build **confidential smart contracts and tokens** that stay fully composable with existing DeFi protocols. Your contracts can process encrypted inputs, execute computations privately, and maintain **hidden balances**, without asking **users to change wallets** or **developers to rewrite contracts**. ## Why Nox Matters? DeFi is transparent by default. That's a feature for retail users. It's a blocker for institutional adoption. Imagine you want to build: * A lending protocol that doesn't expose collateral ratios to liquidation bots * A yield vault that protects strategy positions from copy-trading * A tokenized fund where investor allocations stay off the public ledger * A payment system that hides transaction amounts while staying verifiable Your institutional users have the capital. But they won't touch a protocol that exposes everything. With Nox, they will. You unlock the capital. They get the confidentiality. DeFi grows. ## Key Concepts ## How Nox Works Nox enables confidential DeFi through a distributed architecture built on three core concepts: 1. **Handles**: When you encrypt data using the JS SDK, Nox creates a **unique 32-byte identifier** called a handle. Think of handles as **secure pointers**. Your smart contracts reference confidential values through these handles without exposing the underlying encrypted data, which is stored **securely off-chain**. 2. **Access Control Lists (ACL)**: Each handle is protected by an ACL that manages **permissions on-chain**. By default, only you can access your data. You can grant view permissions to specific addresses, smart contracts, or auditors. This enables **selective disclosure**: maintaining privacy while allowing authorized parties to verify transactions when needed. 3. **Trusted Execution Environments (TEE)**: When your smart contract needs to process confidential data, the computation happens inside **Intel TDX-based TEE enclaves**. These hardware-protected environments ensure that even infrastructure providers **cannot access the plaintext data** during execution. ## The Nox Toolkit **Solidity Library:** Add privacy to your contracts using familiar Solidity syntax, no specialized wallets or off-chain steps required. **JS SDK:** Encrypt sensitive inputs and handle decryption with a developer-friendly TypeScript SDK. **Confidential Smart Contracts:** Build smart contracts that process encrypted data on-chain while maintaining privacy. **Confidential Tokens:** Create ERC-7984 compliant confidential tokens with hidden balances and private transfers. Wrap existing ERC-20 tokens or build native confidential assets that maintain full DeFi compatibility. ## Real-world Use Cases With Nox, you can build a wide range of privacy-preserving applications that seamlessly integrate with the broader DeFi ecosystem: * **Yield with confidential vault:** Build yield-generating vaults that protect strategy positions and capital allocations from copy-trading and MEV. * **cRWA with confidential tokenized equity:** Enable confidential tokenized equity on-chain, protecting investor positions and allocations while maintaining regulatory compliance. * **Confidential value transfers:** Create private payment systems, payroll, and confidential transfers that hide transaction amounts while remaining verifiable. * **Privacy-preserving DeFi primitives:** Build lending, borrowing, and other DeFi primitives that process sensitive data without exposing it publicly. * **Tokens with hidden balances & amounts:** Create ERC-7984 compliant tokens where balances and transaction amounts are hidden from public view. * **Selective disclosure workflows:** Implement audit and compliance workflows where users control exactly who can access their data, enabling regulatory requirements without sacrificing privacy. * **Institutional-grade DeFi & RWAs:** Enable institutional adoption by removing transparency blockers, making DeFi and RWA products ready for discretion-sensitive capital. The next chapters guide you through our **Hello World journey**: how to protect sensitive data and build and deploy confidential smart contracts. ## Start Building Ready to get started? Here's what to explore next: * **Quick Start:** Follow our [Hello World guide](/getting-started/hello-world) to deploy your first confidential contract in minutes * **Explore Use Cases:** Browse [real-world examples](/getting-started/use-cases) to see what you can build with Nox ## Support Need help building with Nox? We're here to support you: * **Discord Community:** Connect with other builders, ask questions, and get real-time support on our [Discord server](https://discord.com/invite/5TewNUnJHN) NOX Protocol is a **privacy layer for EVM blockchains** (deployed on Ethereum Sepolia and Arbitrum Sepolia). It is built by iExec and is a completely separate product from iExec PoCo, DataProtector, Web3Mail, and Web3Telegram. Core concepts for code generation: * **Handle**: 32-byte encrypted value reference. Created via NoxCompute, stored off-chain in the Handle Gateway (TEE). * **ACL**: Per-handle access control with 4 levels — Admin / Viewer / Transient (single-use) / Public. * **NoxCompute**: Singleton smart contract deployed per supported network. Entry point for ACL, arithmetic, and token ops. Deployments: * Ethereum Sepolia (11155111): `0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf` * Arbitrum Sepolia (421614): `0xd464B198f06756a1d00be223634b85E0a731c229` * **Runner**: Rust service in Intel TDX TEE — only component that sees plaintext. Never runs client-side. * **ECIES encryption**: Happens at the Handle Gateway (server-side), NOT in the browser or client SDK. * **cToken / ERC-7984**: Confidential token standard. ERC-20 compatible with encrypted balances (handles). * **Typical flow**: `encryptInput → setACL → compute/transfer → decrypt` NOX is **NOT based on FHE** (Fully Homomorphic Encryption). Do not confuse with Zama fhEVM or TFHE. NOX uses **TEE-based computation** (Intel TDX), not homomorphic encryption. NOX `euint256` is a **handle** (opaque 32-byte pointer to an encrypted value stored off-chain), NOT a homomorphically encrypted integer. Arithmetic happens off-chain in TEE, not on-chain via TFHE operations. Never confuse NOX with iExec PoCo (task scheduling), DataProtector (dataset encryption for monetization), or Web3Mail/Telegram (communication tools). These are distinct products with different APIs on docs.iex.ec. --- --- url: /getting-started/hello-world.md description: Build your first confidential smart contract with Nox --- # Hello World A piggy bank is a simple savings container: you put money in, and only the owner can take it out. In this tutorial, you will first write a classic piggy bank, then turn it into a confidential one using Nox. By the end, balances and amounts will be fully encrypted: nobody can see how much is inside. ## Write a simple contract Start with a standard Solidity contract. Nothing encrypted yet: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; contract PiggyBank { uint256 private balance; address public owner; constructor() { owner = msg.sender; } function deposit(uint256 amount) external { balance += amount; } function withdraw(uint256 amount) external { require(msg.sender == owner); require(amount <= balance); balance -= amount; } function getBalance() external view returns (uint256) { return balance; } } ``` This works, but the balance is visible to anyone reading the blockchain. Let's fix that. ## Turn it into a confidential contract Nox provides **confidentiality**, not anonymity. Addresses and function calls remain visible on-chain, only **balances and amounts** are encrypted. :::steps 1. ### Drop the public balance reader Delete `getBalance()`. In a confidential contract, the balance is only readable through the JS SDK `decrypt()` call with an on-chain ACL check — a public Solidity getter would expose the encrypted handle but not the value. ```solidity function getBalance() external view returns (uint256) { // [!code --] return balance; // [!code --] } // [!code --] ``` 2. ### Import Nox and update types Add the Nox library and swap `uint256` for `euint256`. On-chain, the value is now stored as a 32-byte **handle** that points to encrypted data. The actual value is never visible. ```solidity import {Nox, euint256, externalEuint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; // [!code ++] contract ConfidentialPiggyBank { uint256 private balance; // [!code --] euint256 public balance; // [!code ++] ``` 3. ### Initialize encrypted state Unlike plain `uint256` (which defaults to `0`), an `euint256` must be explicitly initialized to a valid encrypted handle. Use `Nox.toEuint256()` in the constructor: ```solidity constructor() { owner = msg.sender; balance = Nox.toEuint256(0); // [!code ++] } ``` 4. ### Convert `deposit()` Users encrypt values off-chain with the JS SDK and send a **handle** (a reference to the encrypted data) along with a **proof** that the encryption is valid. Replace the plain parameter with `externalEuint256`, then call `Nox.fromExternal()` to verify the proof and convert the external handle into an `euint256` the contract can use. Finally, use `Nox.add()` instead of `+=`: ```solidity function deposit(uint256 amount) external { // [!code --] balance += amount; // [!code --] } // [!code --] function deposit(externalEuint256 inputHandle, bytes calldata inputProof) external { // [!code ++] euint256 amount = Nox.fromExternal(inputHandle, inputProof); // [!code ++] balance = Nox.add(balance, amount); // [!code ++] } // [!code ++] ``` 5. ### Convert `withdraw()` The `require(amount <= balance)` check cannot work on encrypted values. Replace it with `Nox.sub()`, which subtracts two encrypted values: ```solidity function withdraw(uint256 amount) external { // [!code --] require(msg.sender == owner); // [!code --] require(amount <= balance); // [!code --] balance -= amount; // [!code --] } // [!code --] function withdraw(externalEuint256 inputHandle, bytes calldata inputProof) external { // [!code ++] require(msg.sender == owner); // [!code ++] euint256 amount = Nox.fromExternal(inputHandle, inputProof); // [!code ++] balance = Nox.sub(balance, amount); // [!code ++] } // [!code ++] ``` 6. ### Grant permissions By default, only the handle creator has access. After each operation that produces a new handle, you need to grant two permissions: * `Nox.allowThis(balance)`: lets the **contract** reuse the handle in future computations * `Nox.allow(balance, owner)`: lets the **owner** decrypt the balance off-chain ::: warning #1 NOX bug for new developers Forgetting `Nox.allowThis` and `Nox.allow` after each operation makes the handle inaccessible on the next transaction. Transient access is cleared at end-of-tx — always grant permissions before the function returns. ::: Add both calls at the end of the constructor, `deposit()`, and `withdraw()`: ```solidity constructor() { owner = msg.sender; balance = Nox.toEuint256(0); Nox.allowThis(balance); // [!code ++] Nox.allow(balance, owner); // [!code ++] } function deposit(externalEuint256 inputHandle, bytes calldata inputProof) external { euint256 amount = Nox.fromExternal(inputHandle, inputProof); balance = Nox.add(balance, amount); Nox.allowThis(balance); // [!code ++] Nox.allow(balance, owner); // [!code ++] } function withdraw(externalEuint256 inputHandle, bytes calldata inputProof) external { require(msg.sender == owner); euint256 amount = Nox.fromExternal(inputHandle, inputProof); balance = Nox.sub(balance, amount); Nox.allowThis(balance); // [!code ++] Nox.allow(balance, owner); // [!code ++] } ``` ::: ::: warning Overflow and wrapping behavior `Nox.add()` and `Nox.sub()` use wrapping arithmetic: if the result exceeds the type's range, it wraps around instead of reverting. For production contracts, use `Nox.safeAdd()` / `Nox.safeSub()` combined with `Nox.select()` to handle overflows and underflows gracefully without leaking information. See the [Solidity Library reference](/references/solidity-library/getting-started) for details. ::: ## Final result Here is the complete confidential piggy bank. Click **Open in Remix** to load it, then compile with Solidity `0.8.27+`. To deploy, select **WalletConnect** or **Browser Extension** in the Remix **Deploy** panel and make sure your wallet is connected to **Ethereum Sepolia** before hitting **Deploy**. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; // A piggy bank is a simple savings container: you put money in // and only the owner can take it out. This version keeps the // balance encrypted so nobody can see how much is inside. import {Nox, euint256, externalEuint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; contract ConfidentialPiggyBank { euint256 public balance; address public owner; constructor() { owner = msg.sender; balance = Nox.toEuint256(0); Nox.allowThis(balance); Nox.allow(balance, owner); } function deposit(externalEuint256 inputHandle, bytes calldata inputProof) external { euint256 amount = Nox.fromExternal(inputHandle, inputProof); balance = Nox.add(balance, amount); Nox.allowThis(balance); Nox.allow(balance, owner); } function withdraw(externalEuint256 inputHandle, bytes calldata inputProof) external { require(msg.sender == owner); euint256 amount = Nox.fromExternal(inputHandle, inputProof); balance = Nox.sub(balance, amount); Nox.allowThis(balance); Nox.allow(balance, owner); } } ``` ## Try it Use the widget below to encrypt values and decrypt handles with the JS SDK. Connect your wallet, enter your deployed contract address, and encrypt an amount. Copy the resulting **handle** and **handle proof**, then paste them into the `deposit` or `withdraw` fields in Remix to call the contract. To read the balance, copy the handle returned by `balance()` in Remix and decrypt it here. ## Next steps * [Solidity Library](/references/solidity-library/getting-started) - Full reference for all Nox operations * [JS SDK](/references/js-sdk) - Encrypt inputs and decrypt results from JavaScript --- --- url: /getting-started/networks.md description: 'Supported chains, NoxCompute contract addresses, RPCs, explorers and faucets.' --- # Networks This page lists the supported chain with the data you need to wire your dApp end-to-end: the NoxCompute contract address, the canonical RPC URL, the block explorer, faucet links for test funds, and a one-click *Add to wallet* action. ## How "Add to wallet" works Each card's button calls your wallet's EIP-1193 provider (`wallet_switchEthereumChain`, falling back to `wallet_addEthereumChain` when the chain is unknown to the wallet). If you reject the request the button returns to the retry state; if your wallet does not have any EIP-1193 provider installed, the button is disabled with a hint. --- --- url: /getting-started/use-cases.md description: >- Live demos and reference projects built with Nox, and the broader space of confidential applications you can build. --- # Use Cases Nox brings confidentiality to EVM smart contracts: encrypted balances, amounts, and positions, processed inside Intel TDX TEEs without ever exposing plaintext on-chain. Below are live demos and reference projects, followed by the wider set of applications you can build on the protocol. ## Demos & reference projects ## What you can build with Nox The same primitives apply well beyond these demos. you can build private applications for: ## Further reading * [Confidential Tokens (ERC-7984)](/guides/build-confidential-tokens/intro) * [Access Control](/guides/manage-handle-access/intro) * [Architecture Overview](/protocol/global-architecture-overview) --- --- url: /getting-started/use-cases/confidential-vault-encrypted-strategy.md description: >- A vault that keeps the manager's strategy encrypted end to end. Allocation weights, rebalancing triggers, and per-vault intents stay private; only aggregate net orders hit the chain, while NAV, TVL, and APY stay public. --- # Confidential Vault: Encrypted Strategy The Confidential Vault turns the vault manager's strategy into a private input. Allocation weights, rebalancing triggers, and per-vault contributions stay encrypted end to end. Only aggregate net orders reach the chain, while NAV, TVL, and APY stay publicly visible. ## The problem: public vaults leak the strategy, not just the positions On-chain vaults expose the one thing a manager cannot afford to reveal: the strategy itself. Allocation weights are readable on any block explorer, rebalancing triggers are inferable from on-chain timing, and the per-vault attribution of every trade is traceable. The result is predictable. Competitors replicate the playbook, bots front-run the next move, and the alpha that justified the strategy disappears before the next rebalance settles. Keeping the strategy confidential is the prerequisite to deploying at scale. ## The Nox solution: encrypt the strategy, publish the performance Performance stays publicly visible to preserve trust; the strategy stays confidential. Allocation weights, rebalancing triggers, and per-vault intents are protected from competitors, copy-traders, and MEV bots, while NAV, TVL, and APY remain visible for LPs, indexers, and compliance. Concretely, the vault gives you: | Capability | Description | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Encrypted allocation intents | Allocation weights are submitted on-chain as encrypted handles, validated and aggregated, never decrypted alone. | | Confidential rebalancing logic | The timing and triggers of capital movements never appear on-chain, so there is no leakage by inference. | | Cross-vault netting inside a TEE | Intents from every active vault are aggregated into one batch per epoch, hiding per-vault attribution. | | Public performance | NAV, TVL, and APY are published for LPs, indexers, and analytics platforms; the strategy underneath stays hidden. | ## How it works Four roles interact with the vault: * **Vault manager (strategist)** generates the allocation strategy off-chain and submits it on-chain as an encrypted intent. The chain stores unreadable bytes, and the strategy is never decrypted individually. * **Liquidity provider** deposits and receives standard ERC-20 share tokens, tracking performance through the published NAV and APY. Composability with the rest of DeFi is preserved by design. * **Nox protocol engine (inside the TEE)** aggregates the encrypted intents from every active vault at each epoch, computes the net order per destination, and triggers one batch trade per protocol. Inputs and intermediate values never leave the enclave. * **Auditor / regulator** is granted scoped, revocable read access through the on-chain ACL, and sees exactly what compliance requires, nothing more. No party, including iExec, has unilateral access. ## Confidentiality by aggregation Privacy here does not rest on a cryptographic assumption that could one day weaken. It rests on a structural property: as soon as multiple vaults run multiple assets, the information published on-chain is mathematically insufficient to reconstruct any individual strategy. The more vaults active on the protocol, the stronger the guarantee. Every new vault increases the protection of every existing one, so privacy compounds with adoption. ## Further reading * [Confidential Vault: Encrypted Positions](/getting-started/use-cases/confidential-vault) * [Access Control](/guides/manage-handle-access/intro) * [Architecture Overview](/protocol/global-architecture-overview) * [← Back to Use Cases](/getting-started/use-cases) --- --- url: /getting-started/use-cases/confidential-vault.md description: >- A yield vault that encrypts balances and LP positions at the protocol level using Nox cTokens, with selective read access on demand. --- # Confidential Vault: Encrypted Positions Try the live demo at [cvault.demo.noxprotocol.io](http://cvault.demo.noxprotocol.io), or read the source in [iExec-Nox/nox-product-poc](https://github.com/iExec-Nox/nox-product-poc). > Looking to keep the *strategy* itself private, not just positions? See > [Confidential Vault: Encrypted Strategy](/getting-started/use-cases/confidential-vault-encrypted-strategy). ## The problem: public vaults leak alpha by design On-chain vaults expose everything: portfolio allocations, execution timing, rebalancing logic, routing decisions, and manager behavior, all publicly readable by competitors, front-runners, and MEV bots. That transparency turns into copy-trading that erodes strategy value and front-running that extracts returns before execution settles, and performance degrades over time. Sustainable alpha is nearly impossible, and most institutional capital cannot deploy on-chain at scale under these conditions. ## The Nox solution The Confidential Vault encrypts balances and LP positions at the protocol level, and the operator grants scoped read access to the right parties on demand. You add confidentiality as a building block instead of rebuilding the vault from scratch: the architecture keeps public what must stay public to preserve trust, and encrypts the financial data that would otherwise leak alpha. Concretely, the vault gives you: | Capability | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | Encrypted positions & amounts | LP positions and transaction amounts are hidden on-chain. Visible to no one by default. | | Confidential computation | Financial logic runs on encrypted data inside a TEE (Intel TDX). Results are verifiable on-chain via cryptographic attestation. | | Selective disclosure | Grant scoped read access to regulators, auditors, or counterparties on demand. | | On-chain verifiability | Every computation is cryptographically attested, so verification never requires exposing the underlying data. | Three roles interact with the vault. The vault creator (the issuer) deploys it, sets the access permissions and fee structure, decides who can see what, and updates those permissions at any time. Liquidity providers deposit with their balance and position encrypted on-chain, visible only to themselves and the parties they authorize; rewards are computed and distributed through confidential primitives, so position size never leaks. Auditors and regulators see only what the operator grants them, and no party, including iExec, has unilateral visibility. The reference implementation builds on ERC-7540, the async vault standard, adapted to work with ERC-7984 (the Nox cToken). Deposits and withdrawals run asynchronously with backend validation. Yield is injected by the vault admin directly into the encrypted balance, which raises share value for LPs without exposing any individual position. ## Further reading * [Confidential Vault: Encrypted Strategy](/getting-started/use-cases/confidential-vault-encrypted-strategy) * [Confidential Tokens (ERC-7984)](/guides/build-confidential-tokens/intro) * [Access Control](/guides/manage-handle-access/intro) * [Architecture Overview](/protocol/global-architecture-overview) * [← Back to Use Cases](/getting-started/use-cases) --- --- url: /getting-started/use-cases/defi-allocator.md description: >- Deploy treasury strategies across DeFi without broadcasting position size or exposing strategy to front-running. --- # DeFi Capital Allocator ## The problem: transparency destroys competitive advantage Professional capital allocators (hedge funds, DAOs, family offices, institutional treasuries) face a structural problem when deploying strategies on-chain: every move is visible before execution completes. When a fund moves $50M into a yield strategy, the market front-runs the position. When it unwinds exposure, bots extract value through sandwich attacks or by fading the strategy. At scale, on-chain transparency becomes a liability rather than an asset. ## The Nox solution Wrapping treasury assets into Confidential Tokens ([cERC-20 / ERC-7984](/guides/build-confidential-tokens/intro)) gives allocators selective privacy over balances and transaction amounts, readable only by the token holder and the parties they explicitly authorize. Funds can then deploy capital across lending, LP positions, and derivatives without broadcasting position size, rebalance and unwind without market front-running, and preserve alpha by controlling exactly who sees what and when. Full on-chain history stays intact, so auditors, LPs, or compliance officers read what they are granted through selective disclosure. Strategy and execution stay private while regulatory reporting and investor updates remain available to the parties that need them. ## Further reading * [Confidential Tokens (ERC-7984)](/guides/build-confidential-tokens/intro) * [Access Control](/guides/manage-handle-access/intro) * [Architecture Overview](/protocol/global-architecture-overview) * [← Back to Use Cases](/getting-started/use-cases) --- --- url: /getting-started/use-cases/rwa.md description: >- Privacy-first tokenized securities with hidden allocations and confidential distributions, layered on ERC-3643 compliance. --- # RWA Issuance & Distribution ## The problem: investor privacy is non-negotiable Real-world asset tokenization promises to bring trillions of dollars of traditional finance on-chain. But issuers of tokenized funds, private credit, real estate, or structured products face a hard constraint: investor confidentiality is standard practice in traditional finance and expected by institutional investors. On a public blockchain, every token balance and transfer is a matter of public record, which makes many RWA products institutionally unviable: * Investor allocations are publicly visible → competitive intelligence leak * Redemption flows are trackable → distress signals * LP identities can be inferred → investor poaching ## The Nox solution Nox brings traditional-finance confidentiality to tokenized securities while preserving the operational benefits of a public chain. Investor balances stay encrypted, so no one can see who holds how much of a fund or credit instrument. Dividend payments, interest accruals, and redemptions settle without broadcasting amounts. And the issuer can grant read access to regulators, auditors, or compliance partners without making that data public. A confidential private credit fund stacks these layers: ``` Application Layer → Fund Manager UI Vault Layer → ERC-7540 (async vault, request/claim flow) Compliance Layer → ERC-3643 (identity registry + transfer rules) Token Layer → ERC-7984 (cToken for fund shares) Confidentiality Layer → Nox (handles, TEE compute, ACL, selective disclosure) ``` ## ERC-3643 integration [ERC-3643](https://erc3643.org/) is the leading standard for compliant security tokens, with over $32B in assets tokenized on it. It embeds KYC/AML checks and transfer restrictions directly into smart contracts through the ONCHAINID identity layer. ERC-3643 solves *who* can hold tokens. It does not solve the visibility problem: all balances and amounts remain public. For institutional investors in tokenized securities (private equity, venture funds, structured credit), that is the same privacy deficit as a standard ERC-20 token. Nox layers privacy on top of that compliance framework without weakening it. The identity and transfer-validation rules stay fully enforced, so the compliance layer still sees identity verification and transfer eligibility while balances and transaction amounts are encrypted. Issuers can also grant auditors or regulators decryption access to specific fields without making that information public. ## Further reading * [Confidential Tokens (ERC-7984)](/guides/build-confidential-tokens/intro) * [Access Control](/guides/manage-handle-access/intro) * [Architecture Overview](/protocol/global-architecture-overview) * [← Back to Use Cases](/getting-started/use-cases) --- --- url: /getting-started/use-ai.md description: >- Load the NOX documentation into your AI assistant to get accurate, context-aware help when building confidential smart contracts. --- # Use AI to build on NOX AI coding assistants work best when they have accurate, up-to-date context about what you are building. This page shows you how to point your assistant at the NOX documentation so it can give useful, NOX-specific answers instead of guessing. ## Load NOX context into your AI assistant Every build of this documentation automatically generates machine-readable versions of the full doc: | File | Contents | | -------------------------------------------------------------------- | ----------------------------------------------- | | llms.txt | Index of all pages with titles and descriptions | | llms-full.txt | Full documentation in a single file | Paste the `llms-full.txt` URL into any AI assistant that supports URL context (Cursor, Claude, ChatGPT, Gemini) and ask your question. The assistant will read the current documentation before answering. **Cursor** — add this to your chat before asking NOX questions: @{{ llmsFullAbsoluteUrl }} **Claude** — paste the URL and ask your question in the same message. Claude will fetch and read the documentation. ## Coming soon The following resources are in progress: * **Cursor rules file** * **Skills** --- --- url: /getting-started/status.md description: Current status and health of Nox services --- # System Status At Nox Protocol, we are committed to providing a reliable infrastructure and maintaining full transparency with our users. You can monitor the real-time status, incident reports, scheduled maintenance, and the 90-day historical uptime of all our core services on our dedicated status page: 👉 **[status.noxprotocol.io](https://status.noxprotocol.io/)** ## Monitored Components When you visit the status page, you can check the operational status of our key infrastructure components. Click on any component to read its detailed documentation: * **[Handle Gateway](/protocol/handle-gateway)** * **[KMS](/protocol/kms)** * **[Runner](/protocol/runner)** * **[Ingestor](/protocol/ingestor)** ## How to use this page? * **Check for Outages & Maintenance:** If you are experiencing unexpected timeouts, errors, or connectivity issues, please check the status page first. This is where we post updates about active incidents, as well as notices for upcoming scheduled maintenance periods. * **Subscribe to Updates:** We highly recommend clicking the **"Get updates"** button in the top right corner of the status page. You can subscribe via email or RSS to receive instant notifications whenever an incident is reported, resolved, or a maintenance window is scheduled. --- --- url: /guides/build-confidential-smart-contracts/intro.md description: Introduction to building confidential smart contracts --- # Building Confidential Smart Contracts Welcome to the guide on building confidential smart contracts with Nox. This section will help you get started with creating privacy-preserving smart contracts. ## Overview Confidential smart contracts allow you to process encrypted data on-chain while maintaining privacy. With Nox, you can build contracts that handle sensitive information without exposing it publicly. ## What You'll Learn * How to set up your development environment * How to use Hardhat or Foundry with Nox * How to create and deploy confidential smart contracts * Best practices for privacy-preserving contracts ## Next Steps * [Hardhat Guide](/guides/build-confidential-smart-contracts/hardhat) - Build with Hardhat * [Foundry Guide](/guides/build-confidential-smart-contracts/foundry) - Build with Foundry --- --- url: /guides/build-confidential-smart-contracts/hardhat.md description: Build confidential smart contracts with Hardhat --- # Hardhat Integration ::: info Coming Soon This guide is under active development. ::: --- --- url: /guides/build-confidential-smart-contracts/foundry.md description: Build confidential smart contracts with Foundry --- # Foundry Integration ::: info Coming Soon This guide is under active development. ::: --- --- url: /guides/build-confidential-tokens/intro.md description: Introduction to building confidential tokens with Nox --- # Building Confidential Tokens [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) is a confidential fungible token standard designed from the ground up with privacy in mind. Unlike ERC-20, where balances and transfer amounts are visible on-chain, ERC-7984 stores everything as encrypted handles. Only authorized parties can decrypt the actual values. The `@iexec-nox/nox-confidential-contracts` library provides a ready-to-use `ERC7984` base contract, similar to how OpenZeppelin provides `ERC20`. You inherit from it and add your own logic (minting, burning, access control). ## ERC-7984 vs ERC-20 | Feature | ERC-20 | ERC-7984 | | ------------------ | ---------------- | --------------------------------------- | | Balances | Public `uint256` | Encrypted `euint256` | | Transfer amounts | Public | Encrypted | | Total supply | Public | Encrypted | | Approval mechanism | Allowances | Time-bound operators | | Callbacks | No (ERC-1363) | Built-in (`transferAndCall`) | | Addresses | Public | Public (confidentiality, not anonymity) | ## Key Concepts ### Encrypted handles Balances and amounts are not stored as plain numbers. They are stored as `euint256` handles: 32-byte references to encrypted data managed by the Nox protocol. All arithmetic on these values happens off-chain inside a TEE. ### Operators (not allowances) ERC-7984 replaces the ERC-20 `approve`/`transferFrom` pattern with **operators**. An operator can move any amount of tokens on behalf of a holder until a timestamp. This is simpler and avoids the well-known ERC-20 approval front-running issue. ### All-or-nothing transfers Transfers never revert on insufficient balance. Instead, the contract uses `Nox.safeSub()` internally: if the sender does not have enough tokens, the transfer silently succeeds with zero tokens moved. This prevents leaking balance information through transaction reverts. ### Callbacks ERC-7984 has built-in `transferAndCall` support (inspired by ERC-1363). When transferring to a smart contract, the recipient's `onConfidentialTransferReceived` hook is called. If the callback returns `false` (encrypted), the transfer is automatically refunded. ## What You'll Learn * How to create ERC-7984 compliant tokens * How to wrap existing ERC20 tokens * How to build token swap applications * Best practices for confidential token design ## Next Steps * [ERC7984 Token](/guides/build-confidential-tokens/erc7984-token) - Create native confidential tokens * [ERC20 to ERC7984](/guides/build-confidential-tokens/erc20-to-erc7984-wrapper) - Wrap existing ERC20 tokens * [Live Demo](/guides/build-confidential-tokens/demo) - Explore a live application showcasing confidential tokens --- --- url: /guides/build-confidential-tokens/erc7984-token.md description: Build a confidential fungible token with ERC-7984 --- # Create a Confidential ERC-7984 Token This guide walks you through creating a confidential token using the `ERC7984` base contract from `@iexec-nox/nox-confidential-contracts`. By the end you will have a token with encrypted balances, private transfers, and owner-controlled minting and burning. ## Prerequisites * [Hardhat](/guides/build-confidential-smart-contracts/hardhat) or [Foundry](/guides/build-confidential-smart-contracts/foundry) project set up with Nox * Solidity `^0.8.28` ## Installation ::: code-group ```sh [pnpm] pnpm add @iexec-nox/nox-confidential-contracts ``` ```sh [npm] npm install @iexec-nox/nox-confidential-contracts ``` ```sh [yarn] yarn add @iexec-nox/nox-confidential-contracts ``` ```sh [bun] bun add @iexec-nox/nox-confidential-contracts ``` ::: This also installs `@iexec-nox/nox-protocol-contracts` and `@openzeppelin/contracts` as dependencies. ## Deploying the contract Start by inheriting from `ERC7984` and adding mint/burn functions restricted to the owner: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Nox, euint256, externalEuint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; import {ERC7984} from "@iexec-nox/nox-confidential-contracts/contracts/token/ERC7984.sol"; contract ConfidentialToken is ERC7984, Ownable { constructor() ERC7984("Confidential Token", "CTOK", "") Ownable(msg.sender) {} /// @notice Mint tokens to `to` with an encrypted amount function mint( address to, externalEuint256 encryptedAmount, bytes calldata inputProof ) external onlyOwner returns (euint256) { euint256 amount = Nox.fromExternal(encryptedAmount, inputProof); return _mint(to, amount); } /// @notice Burn tokens from `from` with an encrypted amount function burn( address from, externalEuint256 encryptedAmount, bytes calldata inputProof ) external onlyOwner returns (euint256) { euint256 amount = Nox.fromExternal(encryptedAmount, inputProof); return _burn(from, amount); } } ``` That's it. The `ERC7984` base contract handles everything else: encrypted balances, transfers, operators, callbacks, and access control on handles. ## Operators ERC-7984 replaces ERC-20 allowances with time-bound operators. An operator can transfer any amount on behalf of the holder until a given timestamp: ```solidity // Grant operator access until a specific timestamp token.setOperator(spenderAddress, uint48(block.timestamp + 1 hours)); // Operator calls transferFrom token.confidentialTransferFrom( holderAddress, recipientAddress, encryptedAmount, inputProof ); ``` ::: warning Setting an operator grants full access to all tokens until the timestamp expires. There is no amount limit. Only set operators you trust completely. ::: To revoke an operator, set the timestamp to `0`: ```solidity token.setOperator(spenderAddress, 0); ``` ## Receiving tokens in a contract Smart contracts that want to react to incoming ERC-7984 transfers should implement the `IERC7984Receiver` interface: ```solidity import {ebool, euint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; import {IERC7984Receiver} from "@iexec-nox/nox-confidential-contracts/contracts/interfaces/IERC7984Receiver.sol"; contract Vault is IERC7984Receiver { function onConfidentialTransferReceived( address operator, address from, euint256 amount, bytes calldata data ) external returns (ebool) { // Process the incoming transfer... // Return encrypted true to accept, false to refund ebool accepted = Nox.toEbool(true); Nox.allowTransient(accepted, msg.sender); return accepted; } } ``` When a user calls `confidentialTransferAndCall`, the token contract transfers first, then calls the hook on the recipient. If the hook returns encrypted `false`, the transfer is automatically reversed. ## Swap ERC-7984 to ERC-7984 A common use case is swapping between two confidential tokens. Below is a contract that swaps `fromToken` for `toToken` at a 1:1 rate. The caller must have set this contract as an operator on `fromToken` beforehand. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {Nox, euint256, externalEuint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; import {IERC7984} from "@iexec-nox/nox-confidential-contracts/contracts/interfaces/IERC7984.sol"; contract ConfidentialSwap { function swap( IERC7984 fromToken, IERC7984 toToken, externalEuint256 encryptedAmount, bytes calldata inputProof ) external { require(fromToken.isOperator(msg.sender, address(this))); euint256 amount = Nox.fromExternal(encryptedAmount, inputProof); // Transfer fromToken: caller → this contract Nox.allowTransient(amount, address(fromToken)); euint256 received = fromToken.confidentialTransferFrom( msg.sender, address(this), amount ); // Transfer toToken: this contract → caller Nox.allowTransient(received, address(toToken)); toToken.confidentialTransfer(msg.sender, received); } } ``` The steps are: 1. Check operator approval (the caller must have called `fromToken.setOperator(swapContract, until)`) 2. Allow `fromToken` to access the encrypted amount 3. Transfer `fromToken` from caller to the swap contract 4. Allow `toToken` to access the actually transferred amount 5. Transfer `toToken` from the swap contract back to the caller The swap amount remains encrypted throughout, nobody watching the blockchain can see how much was swapped. ## Customizing behavior The `_update` function is the single entry point for all balance changes (mint, burn, transfer). Override it to add custom logic: ```solidity function _update( address from, address to, euint256 amount ) internal override returns (euint256 transferred) { // Custom logic before update... transferred = super._update(from, to, amount); // Custom logic after update... } ``` ## Next steps * [ERC-20 to ERC-7984](/guides/build-confidential-tokens/erc20-to-erc7984-wrapper): wrap existing ERC-20 tokens * [Solidity Library](/references/solidity-library/getting-started): all Nox operations * [JS SDK](/references/js-sdk): encrypt inputs and decrypt balances from JavaScript --- --- url: /guides/build-confidential-tokens/erc20-to-erc7984-wrapper.md description: Wrap existing ERC-20 tokens into confidential ERC-7984 tokens --- # Wrap ERC-20 into Confidential ERC-7984 An ERC-7984 wrapper lets users convert any existing ERC-20 token into a confidential ERC-7984 token and back. The underlying ERC-20 tokens are held by the wrapper contract, while confidential tokens with encrypted balances are minted 1:1. ## How it works ```mermaid sequenceDiagram participant User participant Wrapper as ERC20ToERC7984Wrapper participant ERC20 as ERC-20 Token Note over User,ERC20: Wrap (ERC-20 → ERC-7984) User->>ERC20: approve(wrapper, amount) User->>Wrapper: wrap(to, amount) Wrapper->>ERC20: transferFrom(user, wrapper, amount) Wrapper->>Wrapper: _mint(to, amount) Wrapper-->>User: confidential balance updated Note over User,ERC20: Unwrap (ERC-7984 → ERC-20) User->>Wrapper: unwrap(from, to, encryptedAmount, proof) Wrapper->>Wrapper: _burn(from, encryptedAmount) Note over Wrapper: Decrypt burnt amount off-chain User->>Wrapper: finalizeUnwrap(unwrapRequestId, decryptedAmountAndProof) Wrapper->>ERC20: transfer(to, cleartext) Wrapper-->>User: ERC-20 tokens received ``` ## Key concepts ### One-step wrap Wrapping is straightforward: the ERC-20 amount is public, so the wrapper can transfer and mint in a single transaction. The user approves the wrapper, calls `wrap()`, and their confidential balance is updated immediately. ### Two-step unwrap Unwrapping is more complex because the burn amount is encrypted. The wrapper cannot transfer ERC-20 tokens without knowing the plaintext amount. This requires two steps: 1. **Request unwrap**: the user calls `unwrap()`, which burns the encrypted ERC-7984 tokens 2. **Finalize unwrap**: after the burnt amount is decrypted off-chain (via the Nox protocol), the user calls `finalizeUnwrap()` with the decrypted value and a decryption proof. The wrapper then transfers the corresponding ERC-20 tokens. ## Deploying a wrapper To deploy a wrapper, you only need the address of the ERC-20 token you want to wrap: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {ERC20ToERC7984Wrapper} from "@iexec-nox/nox-confidential-contracts/contracts/token/extensions/ERC20ToERC7984Wrapper.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract WrappedUSDC is ERC20ToERC7984Wrapper { constructor(IERC20 usdc) ERC20ToERC7984Wrapper(usdc) ERC7984("Wrapped Confidential USDC", "wcUSDC", "") {} } ``` The wrapper inherits from both `ERC7984` and `ERC20ToERC7984Wrapper`. All ERC-7984 features (confidential transfers, operators, callbacks) work on the wrapped token. ## Swap ERC-20 to ERC-7984 Swapping from a plaintext ERC-20 to a confidential ERC-7984 is done via the `wrap` function. It transfers ERC-20 tokens from the caller to the wrapper, then mints the equivalent confidential tokens: ```solidity function wrap(address to, uint256 amount) public virtual returns (euint256) { SafeERC20.safeTransferFrom(IERC20(underlying()), msg.sender, address(this), amount); euint256 wrappedAmount = _mint(to, Nox.toEuint256(amount)); Nox.allowTransient(wrappedAmount, msg.sender); return wrappedAmount; } ``` From the caller's perspective: ```solidity // 1. Approve the wrapper usdc.approve(address(wrappedUSDC), 100e18); // 2. Wrap into confidential tokens wrappedUSDC.wrap(msg.sender, 100e18); // Balance is now encrypted, nobody can see how much you hold ``` The ERC-20 transfer would revert on failure (insufficient balance, missing approval). The `_mint` is guaranteed to succeed since it only adds to balances. ## Swap ERC-7984 to ERC-20 Swapping from a confidential token back to a plaintext ERC-20 is more complex. The wrapper needs to know the plaintext amount to transfer ERC-20 tokens, but the burn amount is encrypted. This requires two steps: ### Step 1: Request unwrap The user burns their confidential tokens. The burnt amount is recorded as an encrypted handle, and the function returns an `unwrapRequestId` needed for finalization: ```solidity // Encrypt the amount to unwrap // (off-chain via JS SDK, then call the contract) euint256 unwrapRequestId = wrappedUSDC.unwrap( msg.sender, // burn from msg.sender, // send ERC-20 to encryptedAmount, inputProof ); ``` ### Step 2: Finalize with decryption proof After the Nox protocol decrypts the burnt amount off-chain, the user calls `finalizeUnwrap` with the `unwrapRequestId` and the decrypted amount bundled with its proof: ```solidity wrappedUSDC.finalizeUnwrap( unwrapRequestId, decryptedAmountAndProof ); // ERC-20 tokens are transferred to the recipient ``` The wrapper verifies the decryption proof, then transfers the plaintext amount of the underlying ERC-20 to the recipient. ## Next steps * [ERC-7984 Token](/guides/build-confidential-tokens/erc7984-token): create a native confidential token from scratch * [Demo](/guides/build-confidential-tokens/demo): confidential token swap application * [JS SDK](/references/js-sdk): encrypt inputs and decrypt balances from JavaScript --- --- url: /guides/build-confidential-tokens/demo.md description: Explore a live application showcasing confidential tokens in action --- # Live Demo We built a complete application to show what confidential tokens look like in practice. It connects to Ethereum Sepolia testnet and lets you wrap, transfer, and manage confidential tokens, all from your browser. ![Demo dashboard](../../assets/images/demo-dashboard.png) ## What the demo covers The application walks through four core capabilities of confidential tokens: | Feature | What it does | | ------------------------ | ---------------------------------------------------------------------------------------- | | **Wrap** | Convert a public ERC-20 token (USDC, RLC) into its confidential equivalent (cUSDC, cRLC) | | **Transfer** | Send confidential tokens to another address. The amount stays encrypted on-chain | | **Unwrap** | Convert a confidential token back into a standard ERC-20 | | **Selective Disclosure** | Grant a third party (auditor, regulator) read access to your confidential balance | ## Prerequisites To use the demo you need: * A browser wallet (MetaMask, Rabby, Coinbase Wallet…) * A small amount of **Ethereum Sepolia ETH** for gas fees * Some testnet tokens to wrap. The app includes a faucet to get USDC and RLC ::: tip No real funds involved The demo runs entirely on **Ethereum Sepolia** (testnet). All tokens are free and have no monetary value. ::: ## Walkthrough ### 1. Connect and get testnet tokens Open the app and connect your wallet. If your portfolio is empty, click **Faucet** to receive free testnet ETH, USDC, or RLC. These are the public tokens you'll wrap into their confidential versions. ### 2. Wrap tokens Select a token (for example USDC) and choose an amount. The wrapping process happens in two steps: 1. **Approve** authorize the confidential token contract to spend your ERC-20 2. **Wrap** lock the ERC-20 and mint the equivalent confidential token (cUSDC) Once confirmed, your dashboard shows the new confidential balance. Notice that it appears as an encrypted handle. Click it to decrypt and reveal the amount. Only you can do this. ### 3. Transfer confidentially This is where things get interesting. Select a confidential token, enter a recipient address and an amount. Before the transaction is sent, the JS SDK sends the amount to the **Handle Gateway** over an attested HTTPS connection; the Gateway encrypts it inside its **Intel TDX enclave** and returns a handle. On-chain, the transaction only contains that encrypted handle. No observer can determine how much was transferred. The recipient sees a new confidential balance on their end, which they can decrypt with their own wallet. ### 4. Unwrap tokens To convert confidential tokens back into standard ERC-20, use the unwrap flow. The process encrypts the amount, calls the confidential contract, then finalizes the release of the underlying ERC-20 tokens to your wallet. ### 5. Grant selective disclosure In a regulated context, you may need to prove your holdings to an auditor without exposing everything publicly. The selective disclosure feature lets you grant a specific address **read access** to your confidential balance. Enter the viewer's address, choose which tokens to share (or all of them), and confirm. The viewer can then read your balance, but nobody else can. ::: info Per-balance access Access is tied to the current balance handle. After a transaction that changes your balance (wrap, transfer, unwrap), you need to grant access again for the new balance. ::: ## How it works Under the hood, the demo relies on three building blocks: * **ERC-7984 smart contracts** the on-chain standard for confidential tokens, handling wrap, unwrap, and encrypted transfers * **Nox JS SDK** sends amounts to the Handle Gateway for encryption, and decrypts balance handles locally for the wallet owner * **Handle Gateway** the off-chain service that manages encryption keys and processes decryption requests inside a Trusted Execution Environment (TEE) The application never sees plaintext amounts. The JS SDK forwards the amount to the Handle Gateway (running inside a TDX TEE) over an attested HTTPS connection; encryption and storage happen there. Decryption is local — the SDK derives the plaintext from cryptographic material provided by the KMS, without the KMS ever seeing the plaintext. The smart contract only manipulates encrypted handles. ## Activity and audit trail The **Activity** page tracks all your operations (wraps, transfers, unwraps, access grants) with timestamps and links to the block explorer. The **Delegated View** page shows a two-way overview: who you've granted access to, and who has granted access to you. ## Source code The demo application is open source at [iExec-Nox/demo-ctoken](https://github.com/iExec-Nox/demo-ctoken). The confidential token contracts it builds on (ERC-7984) live in [iExec-Nox/nox-confidential-contracts](https://github.com/iExec-Nox/nox-confidential-contracts). ## Next steps * [ERC-7984 Token](/guides/build-confidential-tokens/erc7984-token) Learn how to create your own confidential token * [ERC-20 to ERC-7984](/guides/build-confidential-tokens/erc20-to-erc7984-wrapper) Wrap an existing ERC-20 into a confidential token * [Manage Viewers](/guides/manage-handle-access/viewers) Deep dive into access control for confidential data * [JS SDK](/references/js-sdk) Reference for encryption and decryption methods --- --- url: /guides/accept-user-inputs.md description: >- How to pass a user-provided value into a confidential smart contract without exposing it on-chain --- # Accepting Private User Inputs Blockchain calldata is public. Every argument you pass to a smart contract function is visible on any block explorer — permanently. This means that if you encrypt a value *inside* the contract, the plaintext was already exposed the moment the transaction was broadcast. Encryption must happen **before** the transaction is sent. ## Two patterns ### Pattern A — Encrypt before the transaction via the JS SDK (correct) ``` ┌─ BROWSER ─────────────────────────────────────┐ │ amount = 100 │ │ { handle, handleProof } = │ │ handleClient.encryptInput(100n, 'uint256', │ │ CONTRACT_ADDRESS) │ └────────────────────────────────────────────────┘ ↓ tx ↓ ┌─ CALLDATA (public on a block explorer) ────────┐ │ contract.contribute(handle, handleProof) │ │ ← 100 does NOT appear │ └────────────────────────────────────────────────┘ ↓ ↓ ┌─ CONTRACT ─────────────────────────────────────┐ │ euint256 encrypted = Nox.fromExternal( │ │ handle, handleProof │ │ ); │ │ ← contract receives a handle, never 100 │ └────────────────────────────────────────────────┘ ``` Only the user's browser and the Nox TEE ever see `100`. ### Pattern B — Encrypt in the contract (incorrect for private values) ``` ┌─ BROWSER ─────────────────────────────────────┐ │ amount = 100 │ │ ← no encryptInput call │ └────────────────────────────────────────────────┘ ↓ tx ↓ ┌─ CALLDATA (public on a block explorer) ────────┐ │ contract.contribute(100) │ │ ← 100 IS VISIBLE TO EVERYONE │ └────────────────────────────────────────────────┘ ↓ ↓ ┌─ CONTRACT ─────────────────────────────────────┐ │ euint256 encrypted = Nox.toEuint256(amount); │ │ ← stores an encrypted handle │ │ ← but 100 was already public │ └────────────────────────────────────────────────┘ ``` The handle on-chain is encrypted, but the value in calldata is not. Encryption must happen **before** the transaction is sent. ## How to implement Pattern A ::: info `encryptInput` sends the plaintext to the [Handle Gateway](/protocol/handle-gateway), which encrypts it inside a TEE and returns the handle and the proof. The encryption does not happen in the browser — your code just initiates the request. ::: **Client side** — use [`encryptInput`](/references/js-sdk/methods/encryptInput) from the JS SDK to encrypt the value and get a `handle` + `handleProof`: ```ts const { handle, handleProof } = await handleClient.encryptInput( 100n, 'uint256', CONTRACT_ADDRESS ); // pass handle and handleProof to your contract call ``` **Contract side** — use [`fromExternal`](/references/solidity-library/methods/core-primitives/fromExternal) to validate the proof and receive the encrypted handle: ```solidity function contribute(externalEuint256 handle, bytes calldata handleProof) external { euint256 amount = Nox.fromExternal(handle, handleProof); // use amount in confidential computations } ``` --- --- url: /guides/manage-handle-access/intro.md description: >- Learn how to manage viewers, admins, and public decryption for encrypted data handles in Nox --- # Managing Handle Access Welcome to the guide on managing handle access with Nox. This section will help you get started with controlling access to encrypted data. ## Overview Handles are the on-chain representation of encrypted data. Managing access to handles is crucial for ensuring that only authorized parties can decrypt and use the data. With Nox, you can easily manage handle access through direct interactions with the Nox protocol contract. Addresses can have different roles written in the handle ACL contract, which determine their permissions: | role \ permissions | decrypt | use as input | add viewer | allow admin | make public | | ------------------ | ------- | ------------ | ---------- | ----------- | ----------- | | none | ❌ | ❌ | ❌ | ❌ | ❌ | | viewer | ✅ | ❌ | ❌ | ❌ | ❌ | | admin | ✅ | ✅ | ✅ | ✅ | ✅ | | transient | ✅ | ✅ | ❌ | ❌ | ❌ | ::: info Transient access expires automatically at the end of the transaction — no revoke needed. See [Transient Access](/guides/manage-handle-access/transient-access). ::: ## What You'll Learn * Manage transient access * Understand the default transient behavior of new handles * Persist contract access with `allowThis` * Pass handles between contracts with `allowTransient` * Manage viewers * Check if an address is a viewer for a handle * Add viewers to a handle * Manage admins * Check if an address is an admin for a handle * Add admins to a handle * Manage public decryption * Check if a handle is publicly decryptable * Enable public decryption for a handle ## Next Steps * [Transient Access](/guides/manage-handle-access/transient-access) - Learn about the default transient behavior and how to persist handle access * [Manage viewers](/guides/manage-handle-access/viewers) - Learn how to manage viewers for a handle * [Manage admins](/guides/manage-handle-access/admins) - Learn how to manage admins for a handle * [Manage public decryption](/guides/manage-handle-access/public-decryption) - Learn how to manage public decryption for a handle --- --- url: /guides/manage-handle-access/transient-access.md description: >- Understanding default transient handle access and how to persist it with allowThis and allowTransient --- # Transient Access Every new handle starts with **transient** access — valid only for the current transaction. This is the default: no additional call is needed to grant it, and it expires automatically at the end of the transaction. ## Why allowThis is the most important call When a handle is created inside a transaction, the calling contract has transient access to it. Once the transaction ends, that access is gone. Without calling `allowThis`, the contract will not be able to use the handle in any future transaction — including storing and reading it in the next call. ```solidity // ❌ The contract loses access to newBalance after this transaction euint256 newBalance = Nox.add(balance, deposit); _balances[msg.sender] = newBalance; // ✅ The contract retains access across future transactions euint256 newBalance = Nox.add(balance, deposit); Nox.allowThis(newBalance); _balances[msg.sender] = newBalance; ``` **`allowThis` is a shorthand for `allow(handle, address(this))`** — it grants the calling contract persistent admin access to the handle. ```solidity function allowThis(euint256 value) internal ``` A common pattern for a balance update: ```solidity euint256 newBalance = Nox.add(balance, deposit); Nox.allowThis(newBalance); // contract keeps access Nox.allow(newBalance, msg.sender); // user can compute with this handle Nox.addViewer(newBalance, msg.sender); // user can decrypt their balance _balances[msg.sender] = newBalance; ``` ## Passing handles between contracts (allowTransient) `allowTransient` grants **one-time** access to another address or contract for the current transaction only. The access expires automatically — no revoke is needed. ```solidity function allowTransient(euint256 value, address account) internal ``` This is useful when delegating a computation to a helper contract within the same transaction without giving it permanent access: ```solidity // Grant the helper contract access to compute with the handle Nox.allowTransient(encryptedAmount, address(helperContract)); helperContract.process(encryptedAmount); // After the transaction ends, helperContract can no longer access encryptedAmount ``` ::: info `allowTransient` is not a substitute for granting viewer or admin access to a user. It is scoped to contracts interacting within the same transaction. ::: ## allowThis vs allowTransient | | `allowThis` | `allowTransient` | | ---------------- | -------------------------------------- | -------------------------------------------- | | Who gets access | The calling contract | Any address or contract | | Duration | Persistent (across transactions) | Current transaction only | | Permission level | Admin | Admin (transient) | | Typical use | Store a handle the contract will reuse | Pass a handle to a helper within the same tx | --- --- url: /guides/manage-handle-access/viewers.md description: Managing handles viewers with Nox --- # Viewers Viewers are addresses that have permission to decrypt the data associated with a handle. Managing viewers is essential for controlling who can view the decrypted data. ::: info An account which is a viewer for a handle can get the decrypted value of a handle with the [`decrypt` method of the SDK](/references/js-sdk/methods/decrypt). ::: ## Checking Viewers The Nox protocol smart contract provides a function to check if a specific address is a viewer for a given handle: ```solidity function isViewer(bytes32 handle, address account) external view returns (bool); ``` **`isViewer` ABI:** ```json [ { "inputs": [ { "internalType": "bytes32", "name": "handle", "type": "bytes32" }, { "internalType": "address", "name": "viewer", "type": "address" } ], "name": "isViewer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" } ] ``` ::: code-group ```ts twoslash [ethers] import { BrowserProvider, Contract, type AbstractProvider } from 'ethers'; const provider: AbstractProvider = new BrowserProvider( (window as any).ethereum ) as AbstractProvider; const handle = '0xHandle'; const account = '0xAccountAddress'; /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `isViewer` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'viewer', type: 'address', }, ], name: 'isViewer', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, ] as const; // ---cut--- const noxContract = new Contract( NOX_CONTRACT_ADDRESS, NOX_CONTRACT_ABI, provider ); const isViewer: boolean = await noxContract.isViewer(handle, account); ``` ```ts twoslash [viem] import { createPublicClient, http } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const handle = '0xHandle'; const viewerAddress = '0xViewerAddress'; const publicClient = createPublicClient({ chain: sepolia, // or arbitrumSepolia transport: http(), }); /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `isViewer` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'viewer', type: 'address', }, ], name: 'isViewer', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, ] as const; // ---cut--- const isViewer = await publicClient.readContract({ address: NOX_CONTRACT_ADDRESS, abi: NOX_CONTRACT_ABI, functionName: 'isViewer', args: [handle, viewerAddress], }); ``` ::: ## Adding Viewers The Nox protocol smart contract provides a function for admins to add a specific address as a viewer for a given handle: ::: info Only allowed admins can add viewers. (see [Manage Admins](/guides/manage-handle-access/admins) guide for more details on admins management). ::: ::: warning Once added, a viewer cannot be removed. This is by design: once a viewer has been granted access, they can decrypt the handle at any time. Revoking on-chain permission would give a false sense of security — the viewer may have already decrypted and stored the data locally via the Handle Gateway. ::: ```solidity function addViewer(bytes32 handle, address account) external; ``` **`addViewer` ABI:** ```json [ { "inputs": [ { "internalType": "bytes32", "name": "handle", "type": "bytes32" }, { "internalType": "address", "name": "viewer", "type": "address" } ], "name": "addViewer", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] ``` ::: code-group ```ts twoslash [ethers] import { BrowserProvider, Contract, type AbstractSigner, type Provider, } from 'ethers'; const signer: BrowserProvider | AbstractSigner = new BrowserProvider( (window as any).ethereum ); const handle = '0xHandle'; const viewerAddress = '0xViewerAddress'; /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `addViewer` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'viewer', type: 'address', }, ], name: 'addViewer', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] as const; // ---cut--- const noxContract = new Contract( NOX_CONTRACT_ADDRESS, NOX_CONTRACT_ABI, signer ); const tx = await noxContract.addViewer(handle, viewerAddress); await tx.wait(); ``` ```ts twoslash [viem] import { createWalletClient, http, custom, type WalletClient, type Chain, } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia /** * Current network. */ const CHAIN: Chain = sepolia as Chain; // or arbitrumSepolia const handle = '0xHandle'; const viewerAddress = '0xViewerAddress'; const walletClient: WalletClient = createWalletClient({ transport: custom((window as any).ethereum), chain: CHAIN, }); /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `addViewer` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'viewer', type: 'address', }, ], name: 'addViewer', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] as const; // ---cut--- const [userAddress] = await walletClient.getAddresses(); await walletClient.writeContract({ account: userAddress, chain: CHAIN, address: NOX_CONTRACT_ADDRESS, abi: NOX_CONTRACT_ABI, functionName: 'addViewer', args: [handle, viewerAddress], }); ``` ::: ## Isolating Access via a New Handle There is no on-chain revoke for viewer access. For use cases that require access isolation (e.g. end of a regulatory audit), the recommended pattern is to migrate to a fresh handle: 1. Create a new handle with the same value — `Nox.add(existingHandle, Nox.toEuint256(0))` produces a new handle with a fresh ACL. Use the matching converter for other types (`Nox.toEuint16`, `Nox.toEbool`, etc.). 2. Update your contract's storage to point to the new handle. 3. Grant access only to the addresses that should retain access on the new handle. The old handle remains accessible to previous viewers, but is no longer used by your application. ::: info This pattern costs extra gas and does not destroy the ciphertext on the Handle Gateway. It is an application-level isolation, not a cryptographic revoke. ::: --- --- url: /guides/manage-handle-access/admins.md description: Managing handles admins with Nox --- # Admins Admins are addresses that have the most permissions on a handle. Admins can: * decrypt the handle * use the handle as input to create new handles * add other addresses as viewers for the handle * allow other addresses to become admins for the handle * make the handle publicly decryptable ## Checking Admins The Nox protocol smart contract provides a function to check if a specific address is an allowed admin for a given handle: ::: tip `isAllowed` checks admin access specifically. To check viewer access, use `isViewer` instead (see [Manage Viewers](/guides/manage-handle-access/viewers)). ::: ```solidity function isAllowed(bytes32 handle, address account) external view returns (bool); ``` **`isAllowed` ABI:** ```json [ { "inputs": [ { "internalType": "bytes32", "name": "handle", "type": "bytes32" }, { "internalType": "address", "name": "account", "type": "address" } ], "name": "isAllowed", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" } ] ``` ::: code-group ```ts twoslash [ethers] import { BrowserProvider, Contract, type AbstractProvider } from 'ethers'; const provider: AbstractProvider = new BrowserProvider( (window as any).ethereum ) as AbstractProvider; const handle = '0xHandle'; const account = '0xAccountAddress'; /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `isAllowed` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'account', type: 'address', }, ], name: 'isAllowed', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, ] as const; // ---cut--- const noxContract = new Contract( NOX_CONTRACT_ADDRESS, NOX_CONTRACT_ABI, provider ); const isAllowed: boolean = await noxContract.isAllowed(handle, account); ``` ```ts twoslash [viem] import { createPublicClient, http } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const handle = '0xHandle'; const account = '0xAccountAddress'; const publicClient = createPublicClient({ chain: sepolia, // or arbitrumSepolia transport: http(), }); /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `isAllowed` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'account', type: 'address', }, ], name: 'isAllowed', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, ] as const; // ---cut--- const isAllowed = await publicClient.readContract({ address: NOX_CONTRACT_ADDRESS, abi: NOX_CONTRACT_ABI, functionName: 'isAllowed', args: [handle, account], }); ``` ::: ## Allowing Admins The Nox protocol smart contract provides a function for admins to allow a specific address as an admin for a given handle: ::: info Only allowed admins can allow new admins. ::: ::: warning Once allowed, an admin cannot be revoked. This is by design: once an admin has been granted access, they can decrypt and use the handle at any time. Revoking on-chain permission would give a false sense of security — the admin may have already decrypted and stored the data locally via the Handle Gateway. ::: ```solidity function allow(bytes32 handle, address account) external; ``` **`allow` ABI:** ```json [ { "inputs": [ { "internalType": "bytes32", "name": "handle", "type": "bytes32" }, { "internalType": "address", "name": "account", "type": "address" } ], "name": "allow", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] ``` ::: code-group ```ts twoslash [ethers] import { BrowserProvider, Contract, type AbstractSigner, type Provider, } from 'ethers'; const signer: BrowserProvider | AbstractSigner = new BrowserProvider( (window as any).ethereum ); const handle = '0xHandle'; const accountToAllow = '0xAccountAddress'; /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `allow` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'account', type: 'address', }, ], name: 'allow', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] as const; // ---cut--- const noxContract = new Contract( NOX_CONTRACT_ADDRESS, NOX_CONTRACT_ABI, signer ); const tx = await noxContract.allow(handle, accountToAllow); await tx.wait(); ``` ```ts twoslash [viem] import { createWalletClient, http, custom, type WalletClient, type Chain, } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia /** * Current network. */ const CHAIN: Chain = sepolia as Chain; // or arbitrumSepolia const handle = '0xHandle'; const accountToAllow = '0xAccountAddress'; const walletClient: WalletClient = createWalletClient({ transport: custom((window as any).ethereum), chain: CHAIN, }); /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `allow` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, { internalType: 'address', name: 'account', type: 'address', }, ], name: 'allow', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] as const; // ---cut--- const [userAddress] = await walletClient.getAddresses(); await walletClient.writeContract({ account: userAddress, chain: CHAIN, address: NOX_CONTRACT_ADDRESS, abi: NOX_CONTRACT_ABI, functionName: 'allow', args: [handle, accountToAllow], }); ``` ::: ## Isolating Access via a New Handle There is no on-chain revoke for admin access. For use cases that require access isolation (e.g. end of a regulatory audit), the recommended pattern is to migrate to a fresh handle: 1. Create a new handle with the same value — `Nox.add(existingHandle, Nox.toEuint256(0))` produces a new handle with a fresh ACL. Use the matching converter for other types (`Nox.toEuint16`, `Nox.toEbool`, etc.). 2. Update your contract's storage to point to the new handle. 3. Grant access only to the addresses that should retain access on the new handle. The old handle remains accessible to previous admins, but is no longer used by your application. ::: info This pattern costs extra gas and does not destroy the ciphertext on the Handle Gateway. It is an application-level isolation, not a cryptographic revoke. ::: --- --- url: /guides/manage-handle-access/public-decryption.md description: Managing public decryption of handles with Nox --- # Public Decryption A handle can be made publicly decryptable, allowing anyone to decrypt its value and use the handle as an input. ::: info Anyone can get the decrypted value of a publicly decryptable handle with the [`publicDecrypt` method of the SDK](/references/js-sdk/methods/publicDecrypt). ::: ## Checking Public Decryption The Nox protocol smart contract provides a function to check if a specific handle is publicly decryptable: ```solidity function isPubliclyDecryptable(bytes32 handle) external view returns (bool); ``` **`isPubliclyDecryptable` ABI:** ```json [ { "inputs": [ { "internalType": "bytes32", "name": "handle", "type": "bytes32" } ], "name": "isPubliclyDecryptable", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" } ] ``` ::: code-group ```ts twoslash [ethers] import { BrowserProvider, Contract, type AbstractProvider } from 'ethers'; const provider: AbstractProvider = new BrowserProvider( (window as any).ethereum ) as AbstractProvider; const handle = '0xHandle'; /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `isPubliclyDecryptable` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, ], name: 'isPubliclyDecryptable', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, ] as const; // ---cut--- const noxContract = new Contract( NOX_CONTRACT_ADDRESS, NOX_CONTRACT_ABI, provider ); const isPubliclyDecryptable: boolean = await noxContract.isPubliclyDecryptable(handle); ``` ```ts twoslash [viem] import { createPublicClient, http } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const handle = '0xHandle'; const publicClient = createPublicClient({ chain: sepolia, // or arbitrumSepolia transport: http(), }); /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `isPubliclyDecryptable` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, ], name: 'isPubliclyDecryptable', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, ] as const; // ---cut--- const isPubliclyDecryptable = await publicClient.readContract({ address: NOX_CONTRACT_ADDRESS, abi: NOX_CONTRACT_ABI, functionName: 'isPubliclyDecryptable', args: [handle], }); ``` ::: ## Allowing Public Decryption The Nox protocol smart contract provides a function for admins to make a handle publicly decryptable: ::: info Only allowed admins can make a handle publicly decryptable. (see [Manage Admins](/guides/manage-handle-access/admins) guide for more details on admins management). ::: ::: warning Once a handle is made publicly decryptable, it cannot be reversed. Anyone can decrypt and store the value once it is public. Revoking public access would not erase copies already made. ::: ```solidity function allowPublicDecryption(bytes32 handle) external; ``` **`allowPublicDecryption` ABI:** ```json [ { "inputs": [ { "internalType": "bytes32", "name": "handle", "type": "bytes32" } ], "name": "allowPublicDecryption", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] ``` ::: code-group ```ts twoslash [ethers] import { BrowserProvider, Contract, type AbstractSigner, type Provider, } from 'ethers'; const signer: BrowserProvider | AbstractSigner = new BrowserProvider( (window as any).ethereum ); const handle = '0xHandle'; /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `allowPublicDecryption` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, ], name: 'allowPublicDecryption', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] as const; // ---cut--- const noxContract = new Contract( NOX_CONTRACT_ADDRESS, NOX_CONTRACT_ABI, signer ); const tx = await noxContract.allowPublicDecryption(handle); await tx.wait(); ``` ```ts twoslash [viem] import { createWalletClient, http, custom, type WalletClient, type Chain, } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia /** * Current network. */ const CHAIN: Chain = sepolia as Chain; // or arbitrumSepolia const handle = '0xHandle'; const walletClient: WalletClient = createWalletClient({ transport: custom((window as any).ethereum), chain: CHAIN, }); /** * Nox protocol contract address (Ethereum Sepolia). * Arbitrum Sepolia: 0xd464B198f06756a1d00be223634b85E0a731c229 * * See the /networks page for all deployments. */ const NOX_CONTRACT_ADDRESS: `0x${string}` = '0x24ef36ec5b626d7dcd09a98f3083c2758f0f77bf'; /** * `allowPublicDecryption` ABI fragment */ const NOX_CONTRACT_ABI = [ { inputs: [ { internalType: 'bytes32', name: 'handle', type: 'bytes32', }, ], name: 'allowPublicDecryption', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] as const; // ---cut--- const [userAddress] = await walletClient.getAddresses(); await walletClient.writeContract({ account: userAddress, chain: CHAIN, address: NOX_CONTRACT_ADDRESS, abi: NOX_CONTRACT_ABI, functionName: 'allowPublicDecryption', args: [handle], }); ``` ::: --- --- url: /references/js-sdk.md description: JavaScript SDK for Nox --- # JS SDK The Nox JavaScript SDK (`@iexec-nox/handle`) provides a simple and secure interface for interacting with the Nox protocol from JavaScript/TypeScript applications. It enables developers to encrypt data and decrypt handles without dealing with the underlying cryptographic complexity. ## Key Features * **Easy Integration**: Works with both Ethers.js and Viem * **Account Abstraction**: Supports ERC-4337 Smart Accounts via Viem * **Type-Safe**: Full TypeScript support with type inference * **Secure**: Handles encryption, decryption, and signature management automatically * **Gasless Decryption**: Uses EIP-712 signatures for authentication without requiring gas ## Core Concepts ### Handles A handle is a 32-byte identifier that references encrypted data stored off-chain. Handles are deterministic and can be verified on-chain through cryptographic proofs. ### Handle Proofs When encrypting data, the Handle Gateway returns a `handleProof` - a signed EIP-712 payload that proves the handle was created by a legitimate Handle Gateway. This proof is used when verifying handles in smart contracts. ### Access Control Handles are protected by Access Control Lists (ACLs) managed on-chain. Only authorized addresses (admin or viewers) can decrypt handles. ## Documentation * [Getting Started](/references/js-sdk/getting-started) - Installation and basic usage * **Methods** * [encryptInput](/references/js-sdk/methods/encryptInput) - Encrypt data and create handles * [decrypt](/references/js-sdk/methods/decrypt) - Decrypt handles * [publicDecrypt](/references/js-sdk/methods/publicDecrypt) - Decrypt publicly decryptable handles with proof * [viewACL](/references/js-sdk/methods/viewACL) - View the Access Control List of a handle * [Advanced Configuration](/references/js-sdk/advanced-configuration) - Custom configuration options --- --- url: /references/js-sdk/getting-started.md description: Getting started with Nox JS SDK --- # Getting Started The Nox JS SDK lets you encrypt values and decrypt handles for use with confidential smart contracts, without dealing directly with the Handle Gateway or the underlying cryptography. It manages encryption, proof generation, EIP-712 signatures, and key exchange transparently. ## How It Works Working with confidential data on Nox follows a three-step workflow: 1. **Encrypt** — You encrypt a plaintext value (a balance, a vote, a flag…) using [`encryptInput`](/references/js-sdk/methods/encryptInput). The SDK sends the value to the Handle Gateway, which returns a **handle** (a 32-byte on-chain identifier pointing to the encrypted data) and a **handleProof** (an EIP-712 signed proof that the handle was created by a legitimate Gateway). 2. **Compute** — Your smart contract receives the handle and proof, verifies them, and performs operations on encrypted data. Handles are composable: they can be stored, transferred, or combined in contract logic without ever exposing the underlying values. 3. **Decrypt** — When a user needs to read the actual value, there are two paths depending on the handle's visibility: * **ACL-protected handles** — Use [`decrypt`](/references/js-sdk/methods/decrypt). The SDK signs an EIP-712 authorization message (no gas required). If the on-chain ACL authorizes the request, the plaintext is securely returned to the caller. * **Publicly decryptable handles** — Use [`publicDecrypt`](/references/js-sdk/methods/publicDecrypt). No ACL check or EIP-712 signature is needed: anyone can decrypt the value as long as the handle has been marked as publicly decryptable on-chain. ## Prerequisites * **Node.js** 18+ or Bun * **A wallet library**: [Ethers.js](https://docs.ethers.org/) v6 or [Viem](https://viem.sh/) v2 ## Installation ::: code-group ```sh [npm] npm install @iexec-nox/handle ``` ```sh [yarn] yarn add @iexec-nox/handle ``` ```sh [pnpm] pnpm add @iexec-nox/handle ``` ```sh [bun] bun add @iexec-nox/handle ``` ::: ## Initialization The SDK exposes a dedicated factory function for each supported wallet library. Pick the one that matches your stack — each is **async** and returns a `Promise`. | Factory | Wallet library | Accepted client | | -------------------------- | -------------- | --------------------------------------------------------------- | | `createEthersHandleClient` | Ethers.js v6 | `BrowserProvider` or `AbstractSigner` with `Provider` | | `createViemHandleClient` | Viem v2 | `WalletClient` or `SmartAccount` (ERC-4337 account abstraction) | | `createHandleClient` | Any | Auto-detects ethers or viem (including `SmartAccount`) | ### With Ethers.js ::: code-group ```ts twoslash [Browser] declare global { interface Window { ethereum: any; } } // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { BrowserProvider } from 'ethers'; const provider = new BrowserProvider(window.ethereum); const handleClient = await createEthersHandleClient(provider); ``` ```ts twoslash [NodeJS] declare const RPC_URL: string; declare const PRIVATE_KEY: string; // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { JsonRpcProvider, Wallet } from 'ethers'; const provider = new JsonRpcProvider(RPC_URL); const signer = new Wallet(PRIVATE_KEY, provider); const handleClient = await createEthersHandleClient(signer); ``` ::: ### With Viem ::: code-group ```ts twoslash [Browser] declare global { interface Window { ethereum: any; } } // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; const walletClient = createWalletClient({ transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); ``` ```ts twoslash [NodeJS] declare const RPC_URL: string; declare const PRIVATE_KEY: `0x${string}`; // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; const walletClient = createWalletClient({ account: privateKeyToAccount(PRIVATE_KEY), transport: http(RPC_URL), }); const handleClient = await createViemHandleClient(walletClient); ``` ::: ### With Viem Smart Account (ERC-4337) The SDK supports [viem Smart Accounts](https://viem.sh/account-abstraction) for account abstraction. Pass a `SmartAccount` instance directly to the factory. ```ts twoslash declare const RPC_URL: string; declare const PRIVATE_KEY: `0x${string}`; // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createPublicClient, http } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia import { toSimple7702SmartAccount } from 'viem/account-abstraction'; import { privateKeyToAccount } from 'viem/accounts'; const publicClient = createPublicClient({ chain: sepolia, // or arbitrumSepolia transport: http(RPC_URL), }); const smartAccount = await toSimple7702SmartAccount({ owner: privateKeyToAccount(PRIVATE_KEY), client: publicClient, }); const handleClient = await createViemHandleClient(smartAccount as any); ``` ::: tip Smart Account support enables ERC-4337 account abstraction workflows. The SDK handles EIP-712 signature generation through the Smart Account's `signTypedData` method, and the Handle Gateway verifies signatures using ERC-1271 `isValidSignature` on-chain. ::: ### With Auto-Detection `createHandleClient` inspects the provided client at runtime and delegates to the appropriate adapter. ::: warning **Bundle size:** This factory imports both the ethers and viem adapters. If you only use one library, prefer `createEthersHandleClient` or `createViemHandleClient` to keep your bundle smaller. ::: ::: code-group ```ts twoslash [Ethers] declare global { interface Window { ethereum: any; } } // ---cut--- import { createHandleClient } from '@iexec-nox/handle'; import { BrowserProvider } from 'ethers'; const provider = new BrowserProvider(window.ethereum); const handleClient = await createHandleClient(provider); ``` ```ts twoslash [Viem] declare global { interface Window { ethereum: any; } } // ---cut--- import { createHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; const walletClient = createWalletClient({ transport: custom(window.ethereum), }); const handleClient = await createHandleClient(walletClient); ``` ::: ## Next Steps * Learn about [encryptInput](/references/js-sdk/methods/encryptInput) — encrypt values and create handles for smart contracts * Explore [decrypt](/references/js-sdk/methods/decrypt) — retrieve plaintext from encrypted handles * Use [publicDecrypt](/references/js-sdk/methods/publicDecrypt) — decrypt publicly decryptable handles with a verifiable proof * Inspect permissions with [viewACL](/references/js-sdk/methods/viewACL) — view the Access Control List of a handle * Configure advanced options in [Advanced Configuration](/references/js-sdk/advanced-configuration) --- --- url: /references/js-sdk/methods/encryptInput.md description: Encrypt a value and create an on-chain handle --- # encryptInput Encrypts a plaintext value and registers it with the Handle Gateway. The latter stores the encrypted data and returns a **handle** — a 32-byte on-chain identifier — along with a **handleProof** that smart contracts can use to verify the handle was created by a legitimate Handle Gateway. ### What happens under the hood 1. The SDK encodes the value according to the given Solidity type. 2. It sends the encoded value plus the caller's address to the Handle Gateway over an attested HTTPS connection (the TLS certificate is bound to the enclave's remote attestation report). 3. The Handle Gateway — **running inside an Intel TDX enclave** — encrypts the value using ECIES with the KMS public key and stores the ciphertext in AWS S3. The plaintext never leaves the TEE memory. 4. The Gateway returns a deterministic handle and a signed EIP-712 proof. The handle can then be passed to a smart contract alongside the `handleProof` for on-chain verification. From that point, the contract works with the handle without ever seeing the plaintext. See [Handle Gateway](/protocol/handle-gateway) for the full encryption protocol. ::: warning Currently supported types `encryptInput` currently accepts only the following types: `bool`, `uint16`, `uint256`, `int16`, and `int256`. Support for additional types from the `SolidityType` union will be added in future releases. ::: ## Usage ```ts twoslash declare global { interface Window { ethereum: any; } } // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); const { handle, handleProof } = await handleClient.encryptInput( 100_000_000n, 'uint256', '0x123...abc' // applicationContract - the contract that will use this handle ); ``` ## Parameters ```ts twoslash import type { SolidityType } from '@iexec-nox/handle'; ``` ### value **Type:** `boolean | string | bigint` The plaintext value to encrypt. The expected JavaScript type depends on the `solidityType` parameter: | Solidity type | JavaScript type | Example | | ---------------------------- | --------------- | -------------------------------------- | | `bool` | `boolean` | `true` | | `string` | `string` | `"Hello, Nox!"` | | `address`, `bytes`, `bytesN` | `string` | `"0x742d…bEb0"` (hex with `0x` prefix) | | `uintN`, `intN` | `bigint` | `1000n` | ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const CONTRACT_ADDRESS: `0x${string}`; // ---cut--- // Encrypt a boolean flag await handleClient.encryptInput(true, 'bool', CONTRACT_ADDRESS); // [!code focus] // Encrypt a token amount await handleClient.encryptInput(1000n, 'uint256', CONTRACT_ADDRESS); // [!code focus] // Encrypt an Ethereum address await handleClient.encryptInput(// [!code focus] '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0', // [!code focus] 'address', // [!code focus] CONTRACT_ADDRESS // [!code focus] ); // [!code focus] ``` ### solidityType **Type:** `SolidityType` The Solidity type the value will be treated as on-chain. The type code is embedded in the handle (byte 30) so the Handle Gateway and contracts know how to interpret the encrypted data. Supported types: * **Boolean**: `bool` * **Address**: `address` *(coming soon)* * **Dynamic types**: `bytes` *(coming soon)*, `string` *(coming soon)* * **Unsigned integers**: `uint8` *(coming soon)*, `uint16`, `uint24` *(coming soon)*, ... , `uint256` * **Signed integers**: `int8` *(coming soon)*, `int16`, `int24` *(coming soon)*, ... , `int256` * **Fixed-size bytes**: `bytes1` *(coming soon)*, `bytes2` *(coming soon)*, ... , `bytes32` *(coming soon)* ::: tip Only `bool`, `uint16`, `uint256`, `int16`, and `int256` are currently supported at runtime. The remaining types listed above will be available in future releases. ::: ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); // ---cut--- await handleClient.encryptInput(true, 'bool', '0x123...abc'); // [!code focus] await handleClient.encryptInput(42n, 'uint64', '0x123...abc'); // [!code focus] await handleClient.encryptInput('Hello, Nox!', 'string', '0x123...abc'); // [!code focus] ``` ### applicationContract **Type:** `string` (Ethereum address) The address of the smart contract that will use this handle. The handle is bound to this contract: only the application contract can validate the `handleProof` on-chain. After successful validation, it receives transient access on the ACL for this handle. The contract must then explicitly persist that access and grant permissions to any address that needs to use or decrypt the handle. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); // ---cut--- // The handle will be used by contract at 0x742d...bEb0 await handleClient.encryptInput( 1000n, 'uint256', '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0' // [!code focus] ); ``` ## Return Value ```ts { handle: Handle; handleProof: `0x${string}`; } ``` ### handle **Type:** `Handle` (a `0x`-prefixed hex string, 32 bytes) A deterministic identifier pointing to the encrypted data. The handle encodes the chain ID (bytes 26–29), the Solidity type code (byte 30), and a protocol version (byte 31). Handles are immutable — updating a value produces a new handle. ### handleProof **Type:** `string` (`0x`-prefixed hex string) An EIP-712 signed proof from the Handle Gateway attesting that the handle was created legitimately. Pass this proof alongside the handle when calling smart contract functions that verify encrypted inputs. --- --- url: /references/js-sdk/methods/decrypt.md description: Retrieve the plaintext value from an encrypted handle --- # decrypt Requests the original plaintext value associated with an encrypted handle. The connected wallet must be **authorized by the on-chain ACL** to access the data — only handle owners or explicitly allowed addresses can decrypt. Decryption is **gasless**: the SDK authenticates the request with an EIP-712 signature, not an on-chain transaction. ### What happens under the hood 1. The SDK generates an ephemeral RSA keypair and builds an EIP-712 message. 2. Your wallet signs the message (no transaction, no gas). 3. The Handle Gateway verifies the signature and checks the on-chain ACL. 4. The KMS performs **decryption delegation**: it computes the ECIES shared secret and RSA-OAEP-encrypts **it** (not the plaintext) with your ephemeral RSA public key. The KMS never sees the plaintext. 5. The SDK RSA-decrypts the shared secret locally, derives the AES-256 key via HKDF, and decrypts the ciphertext — the plaintext never travels over the network. See [KMS — Decryption delegation](/protocol/kms#decryption-delegation-flow) for the full cryptographic flow. ## Usage ```ts twoslash declare global { interface Window { ethereum: any; } } import type { Handle } from '@iexec-nox/handle'; declare const handle: Handle<'uint256'>; // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); const { value, solidityType } = await handleClient.decrypt(handle); ``` ## Parameters ### handle **Type:** `Handle` (a `0x`-prefixed hex string, 32 bytes) The handle to decrypt. It must have been created on the **same chain** as the one the client is connected to. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import type { Handle } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const handle: Handle<'uint256'>; // ---cut--- const { value, solidityType } = await handleClient.decrypt(handle); // [!code focus] ``` ::: tip Gasless operation Decryption uses an EIP-712 signature for authentication — it does not submit an on-chain transaction and costs no gas. ::: ::: warning Access control The connected wallet must be authorized to decrypt the handle. Authorization is managed through the on-chain ACL: only the handle owner or addresses explicitly granted access can request decryption. ::: ::: tip Publicly decryptable handles For handles that have been marked as **publicly decryptable**, use [`publicDecrypt`](/references/js-sdk/methods/publicDecrypt) instead. It does not require ACL authorization or an EIP-712 signature. ::: ## Return Value ```ts { value: boolean | string | bigint; solidityType: SolidityType; } ``` ### value **Type:** `boolean | string | bigint` The decrypted plaintext. The JavaScript type depends on the Solidity type encoded in the handle: | Solidity type | JavaScript type | Example | | ---------------------------- | --------------- | --------------- | | `bool` | `boolean` | `true` | | `string` | `string` | `"Hello, Nox!"` | | `address`, `bytes`, `bytesN` | `string` | `"0x742d…bEb0"` | | `uintN`, `intN` | `bigint` | `1000n` | ### solidityType **Type:** `SolidityType` The Solidity type decoded from the handle (e.g. `"uint256"`, `"bool"`, `"address"`). Useful when you receive a handle without knowing its type ahead of time. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import type { Handle, SolidityType } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const handle: Handle; // ---cut--- const { value, solidityType } = await handleClient.decrypt(handle); console.log(`${solidityType}:`, value); // e.g. "uint256: 1000n" or "bool: true" ``` --- --- url: /references/js-sdk/methods/publicDecrypt.md description: Decrypt a publicly decryptable handle and get a verifiable proof --- # publicDecrypt Decrypts a handle that has been marked as **publicly decryptable** on-chain and returns the plaintext value along with a signed **decryption proof**. Unlike [`decrypt`](/references/js-sdk/methods/decrypt), this method does not require the caller to be in the ACL, anyone can call it as long as the handle is public. The decryption proof is a signed attestation returned by the Handle Gateway that can be verified in a smart contract to produce the plaintext value on-chain. Unlike [`decrypt`](/references/js-sdk/methods/decrypt), `publicDecrypt` does not involve EIP-712 signatures from the caller. ### What happens under the hood 1. The SDK checks on-chain that the handle is publicly decryptable (`isPubliclyDecryptable`). 2. It calls the Handle Gateway's public decryption endpoint. 3. The gateway verifies the on-chain flag, decrypts the value internally via KMS, and returns a signed `DecryptionProof`. 4. The SDK extracts the plaintext from the proof and decodes it according to the Solidity type embedded in the handle. ## Usage ```ts twoslash declare global { interface Window { ethereum: any; } } import type { Handle } from '@iexec-nox/handle'; declare const handle: Handle<'uint256'>; // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); const { value, solidityType, decryptionProof } = await handleClient.publicDecrypt(handle); ``` ## Parameters ### handle **Type:** `Handle` (a `0x`-prefixed hex string, 32 bytes) The handle to decrypt. It must be marked as publicly decryptable on-chain and created on the **same chain** as the one the client is connected to. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import type { Handle } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const handle: Handle<'uint256'>; // ---cut--- const { value, solidityType, decryptionProof } = await handleClient.publicDecrypt(handle); // [!code focus] ``` ::: warning The handle must be publicly decryptable. If it is not, the method throws an error. Use [`viewACL`](/references/js-sdk/methods/viewACL) to check the `isPublic` flag before calling `publicDecrypt`. ::: ## Return Value ```ts { value: boolean | string | bigint; solidityType: SolidityType; decryptionProof: `0x${string}`; } ``` ### value **Type:** `boolean | string | bigint` The decrypted plaintext. The JavaScript type depends on the Solidity type encoded in the handle: | Solidity type | JavaScript type | Example | | ---------------------------- | --------------- | --------------- | | `bool` | `boolean` | `true` | | `string` | `string` | `"Hello, Nox!"` | | `address`, `bytes`, `bytesN` | `string` | `"0x742d…bEb0"` | | `uintN`, `intN` | `bigint` | `1000n` | ### solidityType **Type:** `SolidityType` The Solidity type decoded from the handle (e.g. `"uint256"`, `"bool"`, `"address"`). ### decryptionProof **Type:** `string` (`0x`-prefixed hex string) A signed proof returned by the Handle Gateway. The proof contains the gateway signature (65 bytes) concatenated with the ABI-encoded decrypted value. This proof can be passed to a smart contract to verify the decryption and use the plaintext value on-chain. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import type { Handle, SolidityType } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const handle: Handle; // ---cut--- const { value, solidityType, decryptionProof } = await handleClient.publicDecrypt(handle); // Pass decryptionProof to a smart contract for on-chain verification console.log(`${solidityType}:`, value); console.log('proof:', decryptionProof); ``` --- --- url: /references/js-sdk/methods/viewACL.md description: View the Access Control List of a handle --- # viewACL Retrieves the **Access Control List (ACL)** for a handle. The ACL describes who can interact with the encrypted data: whether it is publicly decryptable, which addresses have admin permissions, and which addresses have viewer permissions. This method queries the protocol's subgraph, not the blockchain directly, so it is fast and does not require gas. ## Usage ```ts twoslash declare global { interface Window { ethereum: any; } } import type { Handle } from '@iexec-nox/handle'; declare const handle: Handle<'uint256'>; // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); const { isPublic, admins, viewers } = await handleClient.viewACL(handle); ``` ## Parameters ### handle **Type:** `Handle` (a `0x`-prefixed hex string, 32 bytes) The handle whose ACL you want to inspect. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import type { Handle } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const handle: Handle<'uint256'>; // ---cut--- const acl = await handleClient.viewACL(handle); // [!code focus] ``` ## Return Value ```ts twoslash import type { ACL } from '@iexec-nox/handle'; ``` ```ts { isPublic: boolean; admins: string[]; viewers: string[]; } ``` ### isPublic **Type:** `boolean` `true` if the handle is publicly decryptable, meaning anyone can call [`publicDecrypt`](/references/js-sdk/methods/publicDecrypt) on it. `false` if decryption is restricted to authorized addresses only. ### admins **Type:** `string[]` List of Ethereum addresses that have **admin** permissions on this handle. Admins can grant and revoke viewer access to other addresses. ### viewers **Type:** `string[]` List of Ethereum addresses that have **viewer** permissions on this handle. Viewers can call [`decrypt`](/references/js-sdk/methods/decrypt) to retrieve the plaintext value. ```ts twoslash declare global { interface Window { ethereum: any; } } import { createViemHandleClient } from '@iexec-nox/handle'; import type { Handle, SolidityType } from '@iexec-nox/handle'; import { createWalletClient, custom } from 'viem'; import { sepolia } from 'viem/chains'; // or arbitrumSepolia const walletClient = createWalletClient({ chain: sepolia, // or arbitrumSepolia transport: custom(window.ethereum), }); const handleClient = await createViemHandleClient(walletClient); declare const handle: Handle; // ---cut--- const { isPublic, admins, viewers } = await handleClient.viewACL(handle); if (isPublic) { console.log('Handle is publicly decryptable'); } else { console.log('Admins:', admins); console.log('Viewers:', viewers); } ``` --- --- url: /references/js-sdk/advanced-configuration.md description: Advanced configuration for Nox JS SDK --- # Advanced Configuration You can customize the SDK by passing a configuration object when creating the client. These options are for advanced use cases — you won't need them for standard usage on supported networks. ::: warning Custom / unsupported chains When targeting an unsupported chain, you must provide **all three** settings: `gatewayUrl`, `smartContractAddress`, and `subgraphUrl`. Omitting any of them will result in a non-functional client. ::: ## Usage ```ts twoslash declare global { interface Window { ethereum: any; } } // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { BrowserProvider } from 'ethers'; const signer = new BrowserProvider(window.ethereum); const handleClient = await createEthersHandleClient(signer, { gatewayUrl: 'https://nox-gateway.custom.example.com', smartContractAddress: '0xCustomNoxContractAddress', subgraphUrl: 'https://subgraph.custom.example.com', }); ``` ## Parameters ```ts twoslash import type { HandleClientConfig } from '@iexec-nox/handle'; ``` ### gatewayUrl **Type:** `string` (base URL without path or query parameters) The Nox Gateway endpoint URL. The SDK communicates with the Gateway for encryption and decryption operations. ```ts twoslash declare global { interface Window { ethereum: any; } } // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { BrowserProvider } from 'ethers'; const signer = new BrowserProvider(window.ethereum); const handleClient = await createEthersHandleClient(signer, { gatewayUrl: 'https://nox-gateway.custom.example.com', // [!code focus] }); ``` If not provided, the default Nox Gateway URL for the detected network will be used. ### smartContractAddress **Type:** `string` (Ethereum address) The address of the Nox protocol contract used for handle verification. ```ts twoslash declare global { interface Window { ethereum: any; } } // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { BrowserProvider } from 'ethers'; const signer = new BrowserProvider(window.ethereum); const handleClient = await createEthersHandleClient(signer, { smartContractAddress: '0x123...abc', // [!code focus] }); ``` If not provided, the default contract address for the detected network will be used. ### subgraphUrl **Type:** `string` (base URL without path or query parameters) The subgraph endpoint URL used by the SDK to query on-chain data such as Access Control Lists via [`viewACL`](/references/js-sdk/methods/viewACL). ```ts twoslash declare global { interface Window { ethereum: any; } } // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { BrowserProvider } from 'ethers'; const signer = new BrowserProvider(window.ethereum); const handleClient = await createEthersHandleClient(signer, { subgraphUrl: 'https://subgraph.custom.example.com', // [!code focus] }); ``` If not provided, the default subgraph URL for the detected network will be used. ## Supported Networks The SDK automatically resolves configuration for supported networks based on the chain ID detected from your provider: | Network | Chain ID | | ---------------- | ---------- | | Ethereum Sepolia | `11155111` | | Arbitrum Sepolia | `421614` | ::: info Ethereum Sepolia Code samples in these docs default to **Ethereum Sepolia** (`11155111`). Full SDK support for it ships with an upcoming `@iexec-nox/handle` release — until then the SDK auto-resolves configuration for **Arbitrum Sepolia** (`421614`). ::: To use an unsupported chain, you must provide all three settings: `gatewayUrl`, `smartContractAddress`, and `subgraphUrl`. Two is not enough for a working client (features like [`viewACL`](/references/js-sdk/methods/viewACL) require the subgraph). ## Advanced Usage Examples ::: code-group ```ts twoslash [Ethers.js] declare const RPC_URL: string; declare const PRIVATE_KEY: string; declare const NOX_GATEWAY_URL: `https://${string}`; declare const NOX_CONTRACT_ADDRESS: `0x${string}`; declare const NOX_SUBGRAPH_URL: `https://${string}`; // ---cut--- import { createEthersHandleClient } from '@iexec-nox/handle'; import { JsonRpcProvider, Wallet } from 'ethers'; const provider = new JsonRpcProvider(RPC_URL); const signer = new Wallet(PRIVATE_KEY, provider); const handleClient = await createEthersHandleClient(signer, { gatewayUrl: NOX_GATEWAY_URL, smartContractAddress: NOX_CONTRACT_ADDRESS, subgraphUrl: NOX_SUBGRAPH_URL, }); ``` ```ts twoslash [Viem] declare const RPC_URL: string; declare const PRIVATE_KEY: `0x${string}`; declare const NOX_GATEWAY_URL: `https://${string}`; declare const NOX_CONTRACT_ADDRESS: `0x${string}`; declare const NOX_SUBGRAPH_URL: `https://${string}`; // ---cut--- import { createViemHandleClient } from '@iexec-nox/handle'; import { createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; const walletClient = createWalletClient({ account: privateKeyToAccount(PRIVATE_KEY), transport: http(RPC_URL), }); const handleClient = await createViemHandleClient(walletClient, { gatewayUrl: NOX_GATEWAY_URL, smartContractAddress: NOX_CONTRACT_ADDRESS, subgraphUrl: NOX_SUBGRAPH_URL, }); ``` ::: --- --- url: /references/solidity-library.md description: >- Nox Solidity library for building confidential smart contracts with encrypted types and TEE-backed computation --- # Solidity Library The `Nox` library is the developer-facing Solidity SDK for building confidential smart contracts. It provides type-safe wrappers around encrypted values, handles proof validation, manages access control, and triggers off-chain TEE computation, all through a single `import`. ## Quick Overview ```solidity import {Nox, euint256, externalEuint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; contract ConfidentialVault { mapping(address => euint256) private _balances; function deposit(externalEuint256 encryptedAmount, bytes calldata proof) external { euint256 amount = Nox.fromExternal(encryptedAmount, proof); euint256 balance = _balances[msg.sender]; if (!Nox.isInitialized(balance)) { balance = Nox.toEuint256(0); Nox.allowThis(balance); } euint256 newBalance = Nox.add(balance, amount); Nox.allowThis(newBalance); Nox.allow(newBalance, msg.sender); _balances[msg.sender] = newBalance; } } ``` ## Confidential Functions The Nox library organizes its functions into three layers, each building on the previous one. ![Confidential Functions by Nox](../assets/images/confidential-primitive.png) ### Core Primitives The foundational building blocks for confidential computation. These low-level operations let you perform arithmetic, comparisons, and conditional logic directly on encrypted values. * [Wrap as Public Handle](/references/solidity-library/methods/core-primitives/wrap-as-public-handle): convert plaintext values to encrypted handles * [fromExternal](/references/solidity-library/methods/core-primitives/fromExternal): validate user-submitted handles with EIP-712 proofs * [Arithmetic](/references/solidity-library/methods/core-primitives/arithmetic): `add`, `sub`, `mul`, `div` with wrapping semantics * [Safe Arithmetic](/references/solidity-library/methods/core-primitives/safe-arithmetic): `safeAdd`, `safeSub`, `safeMul`, `safeDiv` with overflow detection * [Comparisons](/references/solidity-library/methods/core-primitives/comparisons): `eq`, `ne`, `lt`, `le`, `gt`, `ge` returning `ebool` * [select](/references/solidity-library/methods/core-primitives/select): encrypted conditional branching * [Access Control](/references/solidity-library/methods/core-primitives/access-control): `allow`, `allowThis`, `addViewer`, `allowPublicDecryption` and more ### Advanced Functions Higher-level operations composed from core primitives. These provide ready-to-use logic for common DeFi patterns with built-in safety guarantees (all-or-nothing semantics). * [Token Operations](/references/solidity-library/methods/advanced/token-operations): `transfer`, `mint`, `burn` for confidential token contracts ### Custom Functions ::: info Coming Soon Developers will be able to define their own confidential functions (e.g. `swap`, `borrow`, `repay`) by composing core primitives and advanced functions into custom on-chain logic executed inside TEEs. ::: ## Next Steps * [Getting Started](/references/solidity-library/getting-started): installation and project setup --- --- url: /references/solidity-library/getting-started.md description: >- Install and configure the Nox Solidity library for confidential smart contracts --- # Getting Started The Nox Solidity library lets you write smart contracts that operate on encrypted data. You import the `Nox` library, use encrypted types like `euint256` instead of `uint256`, and the protocol handles encryption and off-chain computation transparently. ## How It Works Working with confidential data in Solidity follows three steps: 1. **Receive encrypted inputs**: the user encrypts a value with the [JS SDK](/references/js-sdk) and sends a handle + proof to your contract. You call `Nox.fromExternal()` to validate the proof and get a typed encrypted handle. 2. **Compute on encrypted data**: use `Nox.add()`, `Nox.sub()`, `Nox.eq()`, `Nox.select()`, etc. These functions emit events that trigger off-chain TEE computation. The result is a new encrypted handle. 3. **Manage access**: grant permissions with `Nox.allow()` so handles can be reused in future transactions, and `Nox.addViewer()` so users can decrypt results off-chain via the JS SDK. ::: info Operations on encrypted handles are not executed on-chain. The contract emits events, and the off-chain [Runner](/protocol/runner) performs the actual computation asynchronously inside a TEE enclave. The encrypted result is stored in the [Handle Gateway](/protocol/handle-gateway) under the deterministic handle computed on-chain. ::: ## Prerequisites * **Solidity** ^0.8.0 * **Hardhat 3** or **Foundry** * **Node.js** 18+ with `npm`, `yarn`, `pnpm`, or `bun` ## Installation ::: code-group ```sh [npm] npm install @iexec-nox/nox-protocol-contracts ``` ```sh [yarn] yarn add @iexec-nox/nox-protocol-contracts ``` ```sh [pnpm] pnpm add @iexec-nox/nox-protocol-contracts ``` ```sh [bun] bun add @iexec-nox/nox-protocol-contracts ``` ::: The `encrypted-types` package (providing `euint256`, `ebool`, etc.) is installed automatically as a dependency. ## Imports ```solidity import {Nox, euint256, externalEuint256} from "@iexec-nox/nox-protocol-contracts/contracts/sdk/Nox.sol"; ``` Import the `Nox` library along with the encrypted types you need, all from a single path. The `Nox.sol` file re-exports every type from `EncryptedTypes.sol`, so there is no need for a separate import. ## Encrypted Types Encrypted types are custom defined value types backed by `bytes32`. Each type wraps a **handle**, a 32-byte identifier that references encrypted data stored off-chain in the Handle Gateway. | Category | Types | Example | | ----------------- | ------------------------------------ | --------------------------- | | Boolean | `ebool` | Encrypted true/false | | Unsigned integers | `euint8`, `euint16`, ..., `euint256` | Encrypted balances, amounts | | Signed integers | `eint8`, `eint16`, ..., `eint256` | Encrypted signed values | | Fixed bytes | `ebytes1`, ..., `ebytes32` | Encrypted raw bytes | Each type has a corresponding `external*` variant (e.g. `externalEuint256`) used for handles received from users that need proof validation before use. ## Next Steps * [Arithmetic](/references/solidity-library/methods/core-primitives/arithmetic): `add`, `sub`, `mul`, `div` with wrapping semantics * [Comparisons](/references/solidity-library/methods/core-primitives/comparisons): `eq`, `ne`, `lt`, `le`, `gt`, `ge` * [Token Operations](/references/solidity-library/methods/advanced/token-operations): `transfer`, `mint`, `burn` * [Access Control](/references/solidity-library/methods/core-primitives/access-control): permissions and viewer management * [JS SDK](/references/js-sdk): encrypt inputs and decrypt results from JavaScript --- --- url: /references/solidity-library/methods/core-primitives/wrap-as-public-handle.md description: Convert plaintext values to encrypted handles --- # Wrap as Public Handle Convert plaintext values to encrypted handles. The contract emits an event, and the [Runner](/protocol/runner) encrypts the value off-chain inside a TEE. The encrypted data is stored in the [Handle Gateway](/protocol/handle-gateway). ::: warning The value you pass here is visible in plain text on-chain — anyone can read it on a block explorer. This is intentional for constants, state initialization, or wrapping values that are already public into handles for use with Nox primitives (which only accept handles as inputs). If the value must stay private (e.g. a user-submitted amount, vote, or bid), it must be encrypted before the transaction is sent. → [Accepting private user inputs](/guides/accept-user-inputs) ::: ### Usage ```solidity // Initialize encrypted state variables euint256 initialBalance = Nox.toEuint256(0); Nox.allowThis(initialBalance); euint16 threshold = Nox.toEuint16(100); Nox.allowThis(threshold); ebool flag = Nox.toEbool(true); Nox.allowThis(flag); ``` ## toEbool ```solidity function toEbool(bool value) internal returns (ebool) ``` Converts a plaintext boolean to an encrypted boolean handle. ## toEuint16 ```solidity function toEuint16(uint16 value) internal returns (euint16) ``` Converts a plaintext `uint16` to an encrypted handle. ## toEuint256 ```solidity function toEuint256(uint256 value) internal returns (euint256) ``` Converts a plaintext `uint256` to an encrypted handle. ## toEint16 ```solidity function toEint16(int16 value) internal returns (eint16) ``` Converts a plaintext `int16` to an encrypted handle. ## toEint256 ```solidity function toEint256(int256 value) internal returns (eint256) ``` Converts a plaintext `int256` to an encrypted handle. --- --- url: /references/solidity-library/methods/core-primitives/fromExternal.md description: Validate encrypted handles submitted by users via the JS SDK --- # fromExternal Validates an EIP-712 proof attached to an encrypted handle submitted by a user. On success, returns a typed handle ready for computation. Reverts if the proof is invalid, expired, or was issued for a different contract. This is the entry point for all user-provided encrypted inputs. The user encrypts a value with the [JS SDK](/references/js-sdk/methods/encryptInput), and the contract validates the proof on-chain before using the handle. **Supported types:** `ebool`, `euint16`, `euint256`, `eint16`, `eint256` ::: tip The `external*` types enforce at the Solidity type level that unvalidated handles cannot be used in computations. You must call `fromExternal` first. For constants and state initialization (not user data), see [Wrap as Public Handle](/references/solidity-library/methods/core-primitives/wrap-as-public-handle). ::: ### Usage ```solidity function deposit(externalEuint256 encryptedAmount, bytes calldata proof) external { // Validate the user's encrypted input euint256 amount = Nox.fromExternal(encryptedAmount, proof); // Now use the validated handle in computations euint256 newBalance = Nox.add(_balances[msg.sender], amount); Nox.allowThis(newBalance); Nox.allow(newBalance, msg.sender); _balances[msg.sender] = newBalance; } ``` ## Signature ```solidity function fromExternal(externalEuint256 handle, bytes calldata proof) internal returns (euint256) ``` --- --- url: /references/solidity-library/methods/core-primitives/arithmetic.md description: Encrypted arithmetic operations with wrapping semantics --- # Arithmetic Arithmetic operations on two encrypted values of the same type. All arithmetic uses **wrapping semantics**, matching Solidity's `unchecked` behavior: on overflow or underflow, values wrap around the type boundary instead of reverting. For overflow-safe variants, see [Safe Arithmetic](/references/solidity-library/methods/core-primitives/safe-arithmetic). **Supported types:** `euint16`, `euint256`, `eint16`, `eint256` ### Usage ```solidity euint256 total = Nox.add(balance, deposit); euint256 remaining = Nox.sub(total, fee); euint256 reward = Nox.mul(remaining, rate); euint256 share = Nox.div(reward, participantCount); Nox.allowThis(share); ``` ## add ```solidity function add(euint256 a, euint256 b) internal returns (euint256) ``` Wrapping addition. On overflow, the result wraps around the type boundary. * **Unsigned:** `(a + b) mod 2^N` * **Signed:** `(a + b) mod 2^N`, interpreted in two's complement. For example on Int8, adding past `127` continues from `-128` | Example (Uint8) | Result | Reason | | --------------- | ------ | ----------------------- | | `200 + 100` | `44` | Overflows, wraps around | | `255 + 1` | `0` | Overflows to zero | | `100 + 50` | `150` | No overflow | | Example (Int8) | Result | Reason | | -------------- | ------ | ---------------------------- | | `100 + 100` | `-56` | Overflows, wraps to negative | | `127 + 1` | `-128` | Overflows to MIN | | `-50 + 30` | `-20` | No overflow | ## sub ```solidity function sub(euint256 a, euint256 b) internal returns (euint256) ``` Wrapping subtraction. On underflow, the result wraps around the type boundary. * **Unsigned:** when the result would be negative, it wraps around through MAX. For example on Uint8, `0 - 1` gives `255` * **Signed:** when the result goes below MIN, it wraps around through MAX. For example on Int8, `-128 - 1` gives `127` | Example (Uint8) | Result | Reason | | --------------- | ------ | ------------------------ | | `0 - 1` | `255` | Underflows, wraps to MAX | | `10 - 200` | `66` | Underflows, wraps around | | `100 - 30` | `70` | No underflow | | Example (Int8) | Result | Reason | | -------------- | ------ | ----------------------------- | | `-128 - 1` | `127` | Underflows, wraps to MAX | | `-100 - 100` | `56` | Underflows, wraps to positive | | `50 - 30` | `20` | No underflow | ## mul ```solidity function mul(euint256 a, euint256 b) internal returns (euint256) ``` Wrapping multiplication. On overflow, the result wraps around the type boundary. * **Unsigned:** `(a * b) mod 2^N` * **Signed:** `(a * b) mod 2^N`, interpreted in two's complement | Example (Uint8) | Result | Reason | | --------------- | ------ | ----------------------- | | `3 * 100` | `44` | Overflows, wraps around | | `16 * 16` | `0` | Overflows to zero | | `10 * 5` | `50` | No overflow | | Example (Int8) | Result | Reason | | -------------- | ------ | ---------------------------- | | `-1 * -128` | `-128` | Overflows, wraps back to MIN | | `10 * 20` | `-56` | Overflows, wraps to negative | | `-5 * 3` | `-15` | No overflow | ## div ```solidity function div(euint256 a, euint256 b) internal returns (euint256) ``` Integer division, truncated toward zero. Division by zero does not revert, it returns the maximum representable value of the type (saturates toward +infinity). * **Unsigned:** returns `2^N - 1` (MAX\_U). For example on Uint8, `10 / 0` gives `255` * **Signed:** returns `2^(N-1) - 1` (MAX\_I). For example on Int8, `10 / 0` gives `127`. Additionally, `MIN / -1` wraps back to `MIN` because the true result (`128`) exceeds MAX\_I (`127`) | Example (Uint8) | Result | Reason | | --------------- | ------ | ------------------------------- | | `10 / 0` | `255` | Division by zero, returns MAX\_U | | `7 / 2` | `3` | Truncated toward zero | | `1 / 2` | `0` | Truncated toward zero | | `0 / 5` | `0` | Zero numerator | | Example (Int8) | Result | Reason | | -------------- | ------ | ------------------------------- | | `10 / 0` | `127` | Division by zero, returns MAX\_I | | `-128 / -1` | `-128` | Overflow, wraps back to MIN | | `7 / 2` | `3` | Truncated toward zero | | `-7 / 2` | `-3` | Truncated toward zero | --- --- url: /references/solidity-library/methods/core-primitives/safe-arithmetic.md description: Encrypted arithmetic with overflow and underflow detection --- # Safe Arithmetic Same operations as [core arithmetic](/references/solidity-library/methods/core-primitives/arithmetic), but returning two handles: `(ebool success, result)`. When `success` is `false`, the `result` is always `0`. Use safe arithmetic when your contract needs to detect and handle overflow or underflow without leaking information through transaction reverts. **Supported types:** `euint16`, `euint256`, `eint16`, `eint256` ### Usage ```solidity (ebool ok, euint256 newBalance) = Nox.safeAdd(balance, amount); // Use select to keep the old balance on failure euint256 finalBalance = Nox.select(ok, newBalance, balance); Nox.allowThis(finalBalance); ``` ## safeAdd ```solidity function safeAdd(euint256 a, euint256 b) internal returns (ebool success, euint256 result) ``` Addition with overflow detection. Returns `success = false` and `result = 0` on overflow. * **Unsigned:** `success = false` when `a + b > MAX` * **Signed:** `success = false` when the result exceeds MAX or goes below MIN | Example (Uint8) | success | result | Reason | | ------------------- | ------- | ------ | ----------- | | `SafeAdd(200, 100)` | `false` | `0` | Overflow | | `SafeAdd(255, 1)` | `false` | `0` | Overflow | | `SafeAdd(200, 55)` | `true` | `255` | No overflow | | `SafeAdd(100, 50)` | `true` | `150` | No overflow | | `SafeAdd(0, 0)` | `true` | `0` | No overflow | | Example (Int8) | success | result | Reason | | ------------------- | ------- | ------ | ----------------- | | `SafeAdd(127, 1)` | `false` | `0` | Positive overflow | | `SafeAdd(-128, -1)` | `false` | `0` | Negative overflow | | `SafeAdd(100, 20)` | `true` | `120` | No overflow | | `SafeAdd(-50, -50)` | `true` | `-100` | No overflow | ## safeSub ```solidity function safeSub(euint256 a, euint256 b) internal returns (ebool success, euint256 result) ``` Subtraction with underflow detection. Returns `success = false` and `result = 0` on underflow. * **Unsigned:** `success = false` when `a - b < 0` * **Signed:** `success = false` when the result exceeds MAX or goes below MIN | Example (Uint8) | success | result | Reason | | ------------------ | ------- | ------ | ------------ | | `SafeSub(0, 1)` | `false` | `0` | Underflow | | `SafeSub(50, 100)` | `false` | `0` | Underflow | | `SafeSub(100, 50)` | `true` | `50` | No underflow | | `SafeSub(0, 0)` | `true` | `0` | No underflow | | Example (Int8) | success | result | Reason | | ------------------ | ------- | ------ | ------------------------------- | | `SafeSub(-128, 1)` | `false` | `0` | Signed underflow | | `SafeSub(127, -1)` | `false` | `0` | Equivalent to 127 + 1, overflow | | `SafeSub(0, 0)` | `true` | `0` | No underflow | ## safeMul ```solidity function safeMul(euint256 a, euint256 b) internal returns (ebool success, euint256 result) ``` Multiplication with overflow detection. Returns `success = false` and `result = 0` on overflow. * **Unsigned:** `success = false` when `a * b > MAX` * **Signed:** `success = false` when the result exceeds MAX or goes below MIN | Example (Uint8) | success | result | Reason | | ----------------- | ------- | ------ | --------------------- | | `SafeMul(16, 16)` | `false` | `0` | Overflow | | `SafeMul(15, 17)` | `false` | `0` | Overflow | | `SafeMul(15, 16)` | `true` | `240` | No overflow | | `SafeMul(0, x)` | `true` | `0` | Zero is absorbing | | `SafeMul(1, x)` | `true` | `x` | Identity, no overflow | | Example (Int8) | success | result | Reason | | ------------------- | ------- | ------ | -------------------- | | `SafeMul(-128, -1)` | `false` | `0` | Overflow (128 > MAX) | | `SafeMul(127, 2)` | `false` | `0` | Overflow | | `SafeMul(64, 2)` | `false` | `0` | Overflow (128 > MAX) | | `SafeMul(-1, -1)` | `true` | `1` | No overflow | | `SafeMul(63, 2)` | `true` | `126` | No overflow | ## safeDiv ```solidity function safeDiv(euint256 a, euint256 b) internal returns (ebool success, euint256 result) ``` Division with error detection. Returns `success = false` and `result = 0` on division by zero or signed overflow (`MIN / -1`). * **Unsigned:** `success = false` when dividing by zero * **Signed:** `success = false` when dividing by zero or `MIN / -1` | Example (Uint8) | success | result | Reason | | ----------------- | ------- | ------ | --------------------- | | `SafeDiv(255, 0)` | `false` | `0` | Division by zero | | `SafeDiv(0, 0)` | `false` | `0` | Division by zero | | `SafeDiv(100, 3)` | `true` | `33` | Normal division | | `SafeDiv(1, 2)` | `true` | `0` | Truncated toward zero | | `SafeDiv(0, 5)` | `true` | `0` | Zero numerator | | Example (Int8) | success | result | Reason | | ------------------- | ------- | ------ | --------------------------------- | | `SafeDiv(100, 0)` | `false` | `0` | Division by zero | | `SafeDiv(0, 0)` | `false` | `0` | Division by zero | | `SafeDiv(-128, -1)` | `false` | `0` | Signed overflow (MIN / -1) | | `SafeDiv(-7, 2)` | `true` | `-3` | Truncated toward zero (not floor) | | `SafeDiv(50, 5)` | `true` | `10` | Normal division | --- --- url: /references/solidity-library/methods/core-primitives/comparisons.md description: Compare two encrypted values and return an encrypted boolean --- # Comparisons Compare two encrypted values and return an encrypted boolean. Both operands must be of the same type. The result is always an `ebool`, regardless of the operand type. Comparison semantics depend on the type: unsigned for `euintN`, signed for `eintN`. Comparison results are typically used with [select](/references/solidity-library/methods/core-primitives/select) for encrypted conditional logic. **Supported types:** `euint16`, `euint256`, `eint16`, `eint256` ### Usage ```solidity // Check if balance is sufficient and conditionally apply a transfer ebool hasEnough = Nox.ge(balance, amount); euint256 newBalance = Nox.select(hasEnough, Nox.sub(balance, amount), balance); Nox.allowThis(newBalance); ``` ## eq ```solidity function eq(euint256 a, euint256 b) internal returns (ebool) ``` Returns encrypted `true` when `a == b`. | Example (Uint8) | Result | Reason | | --------------- | ------- | ---------------- | | `Eq(42, 42)` | `true` | Equal values | | `Eq(0, 255)` | `false` | Different values | ## ne ```solidity function ne(euint256 a, euint256 b) internal returns (ebool) ``` Returns encrypted `true` when `a != b`. | Example (Uint8) | Result | Reason | | --------------- | ------- | ---------------- | | `Ne(42, 42)` | `false` | Equal values | | `Ne(0, 255)` | `true` | Different values | ## lt ```solidity function lt(euint256 a, euint256 b) internal returns (ebool) ``` Returns encrypted `true` when `a < b`. | Example (Uint8) | Result | Reason | | --------------- | ------- | ------------------- | | `Lt(10, 200)` | `true` | 10 < 200 (unsigned) | | `Lt(200, 10)` | `false` | 200 < 10 is false | | Example (Int8) | Result | Reason | | --------------- | ------- | ------------------- | | `Lt(-56, 10)` | `true` | -56 < 10 (signed) | | `Lt(127, -128)` | `false` | 127 < -128 is false | ## le ```solidity function le(euint256 a, euint256 b) internal returns (ebool) ``` Returns encrypted `true` when `a <= b`. | Example (Uint8) | Result | Reason | | --------------- | ------- | ------------------ | | `Le(10, 10)` | `true` | Equal values | | `Le(200, 10)` | `false` | 200 <= 10 is false | ## gt ```solidity function gt(euint256 a, euint256 b) internal returns (ebool) ``` Returns encrypted `true` when `a > b`. | Example (Uint8) | Result | Reason | | --------------- | ------- | ------------------- | | `Gt(200, 10)` | `true` | 200 > 10 (unsigned) | | `Gt(10, 200)` | `false` | 10 > 200 is false | | Example (Int8) | Result | Reason | | --------------- | ------- | ------------------- | | `Gt(10, -56)` | `true` | 10 > -56 (signed) | | `Gt(-128, 127)` | `false` | -128 > 127 is false | ## ge ```solidity function ge(euint256 a, euint256 b) internal returns (ebool) ``` Returns encrypted `true` when `a >= b`. | Example (Uint8) | Result | Reason | | --------------- | ------- | ------------------ | | `Ge(10, 10)` | `true` | Equal values | | `Ge(10, 200)` | `false` | 10 >= 200 is false | --- --- url: /references/solidity-library/methods/core-primitives/select.md description: Encrypted conditional branching --- # select Returns `ifTrue` when `condition` is encrypted `true`, `ifFalse` otherwise. Since encrypted values cannot be branched on with `if`, `select` is the only way to implement conditional logic on encrypted data. No computation is performed on the values themselves, it is purely a selection. **Supported types:** `euint16`, `euint256`, `eint16`, `eint256` ::: tip `select` is the encrypted equivalent of a ternary operator (`condition ? a : b`). Since encrypted values cannot be branched on with `if`, use `select` for all conditional logic. ::: ### Usage ```solidity // Confidential max(a, b) ebool aIsGreater = Nox.gt(a, b); euint256 maxValue = Nox.select(aIsGreater, a, b); Nox.allowThis(maxValue); ``` ```solidity // Apply a fee only if balance exceeds threshold ebool exceedsThreshold = Nox.gt(balance, threshold); euint256 fee = Nox.select(exceedsThreshold, feeAmount, zeroAmount); euint256 finalBalance = Nox.sub(balance, fee); Nox.allowThis(fee); Nox.allowThis(finalBalance); ``` ## Signature ```solidity function select(ebool condition, euint256 ifTrue, euint256 ifFalse) internal returns (euint256) ``` | Example | Result | Reason | | ------------------------ | ------ | ------------------ | | `Select(true, 42, 100)` | `42` | Condition is true | | `Select(false, 42, 100)` | `100` | Condition is false | --- --- url: /references/solidity-library/methods/core-primitives/access-control.md description: 'Manage permissions on encrypted handles, control who can compute and decrypt' --- # Access Control Every new handle starts with **transient** access (valid only for the current transaction). You must explicitly persist access so handles can be reused in future transactions and decrypted by authorized users. For a detailed explanation of the permission model, see [Nox Smart Contracts: ACL](/protocol/nox-smart-contracts#acl-access-control-list). **Supported types:** `ebool`, `euint16`, `euint256`, `eint16`, `eint256` ### Usage ```solidity euint256 newBalance = Nox.add(balance, deposit); Nox.allowThis(newBalance); // contract keeps access Nox.allow(newBalance, msg.sender); // user can compute with this handle Nox.addViewer(newBalance, msg.sender); // user can decrypt their balance _balances[msg.sender] = newBalance; ``` ## isInitialized ```solidity function isInitialized(euint256 handle) internal pure returns (bool) ``` Returns `true` if the handle is non-zero (has been assigned a value). Use this to check whether a mapping entry has been set before performing operations on it. ## allow ```solidity function allow(euint256 value, address account) internal ``` Grants persistent **admin** access to `account` on the handle. The account can use the handle as input in future computations and manage its permissions. ::: warning The caller must already have access to the handle. You cannot grant access to a handle you do not control. ::: ::: warning This permission is permanent and cannot be revoked. See [Managing Admins](/guides/manage-handle-access/admins#isolating-access-via-a-new-handle) for the new-handle isolation pattern. ::: ## allowThis ```solidity function allowThis(euint256 value) internal ``` Shorthand for `allow(value, address(this))`. Grants persistent access to the calling contract so the handle can be reused in future transactions. ## allowTransient ```solidity function allowTransient(euint256 value, address account) internal ``` Grants **one-time** access to `account` for the current transaction only. Transient permissions are cleared at the end of the transaction. Useful for passing handles to another contract within the same transaction without giving permanent access. ## isAllowed ```solidity function isAllowed(euint256 handle, address account) internal view returns (bool) ``` Returns `true` if `account` has admin access (persistent or transient) to the handle. ## addViewer ```solidity function addViewer(euint256 value, address viewer) internal ``` Grants **viewer** permission to `viewer`. Viewers can decrypt the handle's data through the Handle Gateway using the [JS SDK](/references/js-sdk/methods/decrypt) but cannot use the handle as input in computations. ::: warning This permission is permanent and cannot be revoked. See [Managing Viewers](/guides/manage-handle-access/viewers#isolating-access-via-a-new-handle) for the new-handle isolation pattern. ::: ## isViewer ```solidity function isViewer(euint256 handle, address viewer) internal view returns (bool) ``` Returns `true` if `viewer` has viewer permission on the handle. ## allowPublicDecryption ```solidity function allowPublicDecryption(euint256 value) internal ``` Makes the handle's data decryptable by anyone. Use this for values that should be publicly visible (e.g. total supply, auction results, public counters). ::: warning This permission is permanent and cannot be revoked. Once public, anyone can decrypt and store the value. See [Manage Public Decryption](/guides/manage-handle-access/public-decryption) for details. ::: ## isPubliclyDecryptable ```solidity function isPubliclyDecryptable(euint256 handle) internal view returns (bool) ``` Returns `true` if the handle is marked as publicly decryptable. --- --- url: /references/solidity-library/methods/advanced/token-operations.md description: High-level confidential token operations with all-or-nothing semantics --- # Token Operations High-level operations for confidential token contracts. They **never revert** and follow **all-or-nothing** semantics: if the operation cannot complete (insufficient balance, overflow), nothing changes and `success` is set to `false`. The previous ciphertexts are reassigned to the new output handles. This prevents leaking balance information through transaction success/failure (which would create a binary oracle). These operations are composed from [core primitives](/references/solidity-library/methods/core-primitives/arithmetic) and executed atomically inside the TEE. ### Usage ```solidity // Confidential ERC-20 transfer (ebool ok, euint256 newFrom, euint256 newTo) = Nox.transfer( _balances[from], _balances[to], amount ); Nox.allowThis(ok); Nox.allowThis(newFrom); Nox.allowThis(newTo); Nox.allow(newFrom, from); Nox.allow(newTo, to); _balances[from] = newFrom; _balances[to] = newTo; ``` ## transfer ```solidity function transfer( euint256 balanceFrom, euint256 balanceTo, euint256 amount ) internal returns (ebool success, euint256 newBalanceFrom, euint256 newBalanceTo) ``` Moves tokens between two balances. If `amount > balanceFrom`, nothing is transferred and all output handles retain the original values. Overflow of `balanceTo` is impossible because the total supply is bounded by `MAX_U`, so `balanceTo + amount <= totalSupply <= MAX_U` is guaranteed. **Behavior:** ``` if amount > balanceFrom: success = false newBalanceFrom = balanceFrom // ciphertext reassigned to new handle newBalanceTo = balanceTo // ciphertext reassigned to new handle else: success = true newBalanceFrom = balanceFrom - amount newBalanceTo = balanceTo + amount ``` | balanceFrom | balanceTo | amount | success | newBalanceFrom | newBalanceTo | Reason | | ----------- | --------- | ------ | ------- | -------------- | ------------ | ------------------------------------- | | 1000 | 500 | 300 | `true` | 700 | 800 | Normal transfer | | 1000 | 500 | 1000 | `true` | 0 | 1500 | Full balance transferred | | 1000 | 500 | 2000 | `false` | 1000 | 500 | Amount > balance, nothing transferred | | 100 | 500 | 0 | `true` | 100 | 500 | Zero amount, no-op | ## mint ```solidity function mint( euint256 balanceTo, euint256 amount, euint256 totalSupply ) internal returns (ebool success, euint256 newBalanceTo, euint256 newTotalSupply) ``` Creates new tokens. If either `balanceTo + amount` or `totalSupply + amount` overflows, nothing is minted and `success` is `false`. **Behavior:** ``` if overflow(balanceTo + amount) or overflow(totalSupply + amount): success = false newBalanceTo = balanceTo // unchanged newTotalSupply = totalSupply // unchanged else: success = true newBalanceTo = balanceTo + amount newTotalSupply = totalSupply + amount ``` | balanceTo | amount | totalSupply | success | newBalanceTo | newTotalSupply | Reason | | --------- | ------ | ----------- | ------- | ------------ | -------------- | ----------------- | | 500 | 300 | 10000 | `true` | 800 | 10300 | Normal mint | | 0 | 1000 | 0 | `true` | 1000 | 1000 | Mint on empty | | 500 | 0 | 10000 | `true` | 500 | 10000 | Zero amount no-op | ## burn ```solidity function burn( euint256 balanceFrom, euint256 amount, euint256 totalSupply ) internal returns (ebool success, euint256 newBalanceFrom, euint256 newTotalSupply) ``` Destroys tokens. If `amount > balanceFrom` or `totalSupply - amount` underflows, nothing is burned and `success` is `false`. **Behavior:** ``` if amount > balanceFrom or underflow(totalSupply - amount): success = false newBalanceFrom = balanceFrom // ciphertext reassigned to new handle newTotalSupply = totalSupply // ciphertext reassigned to new handle else: success = true newBalanceFrom = balanceFrom - amount newTotalSupply = totalSupply - amount ``` | balanceFrom | amount | totalSupply | success | newBalanceFrom | newTotalSupply | Reason | | ----------- | ------ | ----------- | ------- | -------------- | -------------- | -------------------------------- | | 100 | 50 | 1000 | `true` | 50 | 950 | Normal burn | | 1000 | 1000 | 10000 | `true` | 0 | 9000 | Full balance burned | | 60 | 100 | 1000 | `false` | 60 | 1000 | Amount > balance, nothing burned | | 100 | 0 | 1000 | `true` | 100 | 1000 | Zero amount, no-op | --- --- url: /protocol/global-architecture-overview.md description: >- High-level overview of Nox protocol architecture, components, data flows, and how they interact to enable confidential computation on encrypted data --- # Global Architecture Overview Nox is a confidential computation protocol for DeFi. It allows smart contracts to operate on encrypted data without ever exposing plaintext on-chain. The protocol coordinates six components: on-chain smart contracts, an event listener (Ingestor), a message queue (NATS), a computation engine (Runner), an encrypted data store (Handle Gateway), and a key management service (KMS). ![Nox Protocol Architecture](../assets/images/nox_protocol.png) ## End-to-End Flow A confidential computation in Nox follows three phases: **input**, **compute**, and **output**. The diagram below shows the complete lifecycle of an encrypted operation, from user input to result retrieval. ```mermaid sequenceDiagram participant U as User (SDK) participant GW as Handle Gateway participant KMS as KMS participant SC as NoxCompute participant BC as Blockchain participant I as Ingestor participant NATS as NATS JetStream participant R as Runner rect rgb(59, 130, 246, 0.1) note right of U: Phase 1: Input U->>GW: POST /v0/secrets (plaintext, type, owner) GW->>KMS: GET /v0/public-key KMS-->>GW: pubkey_KMS GW->>GW: ECIES encrypt with pubkey_KMS GW-->>U: handle + EIP-712 proof U->>SC: Call operation (e.g. add(handleA, handleB)) SC->>SC: Validate handle proofs + types SC->>SC: Compute deterministic result handle SC->>SC: Grant transient ACL on result SC->>BC: Emit event (e.g. Add) end rect rgb(245, 158, 11, 0.1) note right of I: Phase 2: Compute I->>BC: Poll new blocks BC-->>I: Logs from NoxCompute I->>NATS: Publish TransactionMessage R->>NATS: Pull next message R->>GW: GET /v0/compute/operands (RSA pubkey) GW->>KMS: POST /v0/delegate (K, RSA pubkey) KMS-->>GW: encryptedSharedSecret GW-->>R: ciphertext + encryptedSharedSecret + nonce R->>R: Decrypt inputs locally R->>R: Execute computation R->>R: Re-encrypt result (ECIES) R->>GW: POST /v0/compute/results R->>NATS: Acknowledge message end rect rgb(16, 185, 129, 0.1) note right of U: Phase 3: Output U->>U: Generate ephemeral RSA keypair U->>GW: GET /v0/secrets/:handle (RSA pubkey + EIP-712 auth) GW->>GW: Verify signature + check on-chain ACL GW->>KMS: POST /v0/delegate (K, RSA pubkey) KMS-->>GW: encryptedSharedSecret GW-->>U: ciphertext + encryptedSharedSecret + nonce U->>U: RSA decrypt, HKDF, AES-GCM decrypt end ``` ### Phase 1: Input The user encrypts sensitive data off-chain and submits it to a smart contract using opaque **handles** (32-byte identifiers that reference the encrypted data stored in the Handle Gateway). 1. The user sends a plaintext value to the **Handle Gateway** via the SDK 2. The Handle Gateway encrypts it with [ECIES](/protocol/kms#ecies-encryption-scheme) using the KMS public key and stores the ciphertext in an **AWS S3 bucket**. 3. The Handle Gateway returns a **handle** and an **EIP-712 signed proof** attesting the handle's validity. 4. The user calls a function on a smart contract that uses the **NoxCompute** library (e.g. `Nox.add(handleA, handleB)`) 5. NoxCompute validates handle proofs and type compatibility, computes a deterministic result handle, grants transient ACL access on it, and **emits an event** containing the input and output handles. At this point, the on-chain transaction is complete. The encrypted result does not exist yet: it will be computed off-chain. ### Phase 2: Compute The off-chain pipeline picks up the event and executes the computation inside a secure environment. 1. The **Ingestor** polls new blocks from the blockchain RPC, filters NoxCompute logs, groups events by transaction, and publishes a `TransactionMessage` to **NATS JetStream**. 2. The **Runner** pulls the next message from the queue. 3. The Runner requests the encrypted operands from the **Handle Gateway**, providing an ephemeral RSA public key. 4. The Handle Gateway retrieves the object from its **AWS S3 bucket** and coordinates with the **KMS** to perform [decryption delegation](/protocol/kms): the KMS computes the ECDH shared secret and encrypts it with the Runner's RSA key. 5. The Runner decrypts the inputs locally (RSA decrypt the shared secret, HKDF key derivation, AES-GCM decrypt), executes the computation, and encrypts the result with ECIES using the KMS public key. 6. The Runner submits the encrypted result to the Handle Gateway and acknowledges the `TransactionMessage` received from **NATS JetStream**. ### Phase 3: Output When the user wants to read a result, the protocol performs decryption delegation so that only the authorized user can recover the plaintext. 1. The user generates an ephemeral RSA keypair and sends a decryption request to the Handle Gateway, authenticated with an EIP-712 signature. 2. The Handle Gateway verifies the signature and checks the **on-chain ACL** to confirm the user has viewer permission on the handle. 3. The Handle Gateway requests decryption delegation from the KMS: the KMS computes the shared secret and encrypts it with the user's RSA public key 4. The user receives the ciphertext, encrypted shared secret, and nonce, then decrypts locally: RSA decrypt the shared secret, derive the AES key via HKDF, and AES-GCM decrypt the ciphertext The KMS never sees plaintext data. Only the final recipient (user or Runner) performs the actual decryption. ## Handles A **handle** is a 32-byte identifier that references an encrypted value stored in the Handle Gateway. It does not contain the ciphertext itself. ``` [0] [1------4] [5] [6] [7---------------------31] Version Chain ID Type Attrs prehandle (25 bytes) ``` | Segment | Size | Description | | ---------- | -------- | ---------------------------------------------------------------------------------------- | | Version | 1 byte | Handle format version (currently `0x00`) | | Chain ID | 4 bytes | Chain ID encoded as uint32; binds the handle to a specific blockchain | | Type | 1 byte | Solidity type of the encrypted value (uint8, bool, address, etc.) | | Attributes | 1 byte | Handle attributes/properties (e.g. uniqueness) | | Prehandle | 25 bytes | Truncated keccak256 hash (deterministic for computation results, random for user inputs) | For computation results, the prehandle is derived from the operator, input handles, contract address, caller, timestamp, and output index. This makes handles **deterministic**: the same operation on the same inputs in the same transaction always produces the same handle. For user inputs created via the Handle Gateway, the prehandle is a random value. The handle is validated on-chain via an EIP-712 signed proof from the Handle Gateway. For the full handle specification, see [Nox Smart Contracts](/protocol/nox-smart-contracts#handle-structure). ## Access Control The **ACL** contract manages who can use a handle as a computation input and who can decrypt the underlying data. | Permission | Granted by | Capability | | ------------- | ----------------------------- | ---------------------------------------------------------- | | **Admin** | `ACL.allow()` | Use handle as computation input, manage permissions | | **Transient** | NoxCompute (automatic) | One-time use within the current transaction, cleared after | | **Viewer** | `ACL.addViewer()` | Decrypt the associated data via the Handle Gateway | | **Public** | `ACL.allowPublicDecryption()` | Anyone can decrypt | When NoxCompute creates a result handle, it automatically grants **transient** access to the calling contract. The contract must persist this permission with `ACL.allow()` if the handle needs to be used in subsequent transactions. The Handle Gateway checks ACL permissions on-chain (via `isViewer`) before serving decryption material to a user. ## Encryption All encrypted data in Nox is protected with **ECIES** (Elliptic Curve Integrated Encryption Scheme) on secp256k1, using the KMS public key as the encryption target. ```mermaid flowchart LR A[Plaintext] --> B[Generate ephemeral key k] B --> C["ECDH: S = k * pubkey_KMS"] C --> D["HKDF-SHA256 → AES-256 key"] D --> E["AES-256-GCM encrypt"] E --> F["Store: ciphertext + K + nonce"] style A fill:#4a9eff,color:#fff style F fill:#7c3aed,color:#fff ``` The KMS holds the corresponding private key but **never decrypts data directly**. Instead, it performs **decryption delegation**: it computes the ECDH shared secret and encrypts it with the requester's RSA public key. The requester then derives the AES key locally and decrypts the ciphertext. For the full cryptographic protocol, see [KMS](/protocol/kms). ## Learn More * [Protocol Vision](/protocol/protocol-vision): long-term architecture and design principles * [Nox Smart Contracts](/protocol/nox-smart-contracts): on-chain contracts, handle structure, and ACL * [Ingestor](/protocol/ingestor): blockchain monitoring and event pipeline * [Runner](/protocol/runner): computation engine and supported operations * [Handle Gateway](/protocol/handle-gateway): encryption, storage, and API reference * [KMS](/protocol/kms): cryptographic protocol and decryption delegation * [Solidity Library](/references/solidity-library): full reference of all operations --- --- url: /protocol/protocol-vision.md description: >- Long-term architecture of Nox, including distributed KMS, omnichain support, decentralized operations, multi-privacy technologies, DeFi composability, and open computation primitives --- # Protocol Vision Public-by-default transparency was a feature in early DeFi. At scale, it has become a limitation. On fully transparent blockchains, strategies, positions, and capital flows are publicly exposed. Professional allocators suffer from copy-trading, MEV extraction, and poor execution. Institutions and RWA issuers cannot operate under full on-chain disclosure. DeFi lacks a confidential asset primitive that preserves composability while meeting real-world financial constraints. Nox fills this gap: a privacy layer that brings confidentiality to DeFi without sacrificing the composability and decentralization that make it powerful. The long-term vision for Nox evolves the protocol along six axes. This page reflects the current direction of the protocol: the architecture and priorities described here are subject to change as the technology and ecosystem mature. * **Privacy by convergence**: combine TEE, threshold cryptography, MPC, and zero-knowledge proofs, each applied where it offers the best tradeoff * **Trust distribution**: progressively reduce trust assumptions by distributing and decentralizing components * **Omnichain expansion**: extend Nox to any blockchain, sharing a single backend across all supported networks * **Horizontal scalability**: support multiple Runners and growing computation throughput * **Composability**: enable confidential tokens to interact with the entire DeFi ecosystem, confidential or not * **Developer openness**: allow anyone to create, deploy, and monetize new confidential computation primitives * **Developer experience**: make confidential computing as accessible as standard Solidity development ## Combining Privacy Technologies ```mermaid block-beta columns 3 block:onchain:3 columns 3 zk["ZK Proofs"] space onlabel["On-chain verification"] end block:exec:3 columns 3 tee["TEE (Intel TDX)"] space execlabel["Confidential execution"] end block:keys:3 columns 3 threshold["Threshold + MPC"] space keyslabel["Key management"] end style onchain fill:#4a9eff,color:#fff style exec fill:#7c3aed,color:#fff style keys fill:#059669,color:#fff ``` No single privacy technology solves confidential DeFi alone. TEEs are fast but rely on hardware trust. Threshold cryptography distributes trust but cannot execute arbitrary computation. MPC enables collaborative computation but does not scale to complex workloads. Zero-knowledge proofs offer mathematical guarantees but are too expensive for general-purpose execution. Each technology excels in a specific domain and fails in others. The only path to a privacy layer that is simultaneously fast, trustless, and scalable is to combine them, using each where it is strongest: | Technology | Where it applies | Strength | | --------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------- | | **TEE (Intel TDX)** | Runner, Handle Gateway, Ingestor, KMS | Fast computation on encrypted data inside hardware-isolated enclaves | | **Threshold cryptography** | KMS | Distributed key management with no single point of trust | | **MPC (Multi-Party Computation)** | KMS | Collaborative computation across multiple nodes without reconstructing secrets | | **ZK (Zero-Knowledge Proofs)** | On-chain verification | Gas-efficient proof verification, replacing expensive on-chain verification logic | Together, these technologies cover each other's weaknesses: * **TEE** provides the execution environment: Runners decrypt, compute, and re-encrypt data inside hardware enclaves, ensuring that plaintext never leaves protected memory * **Threshold cryptography and MPC** remove the hardware single point of trust: the protocol's private key is distributed across KMS nodes, and decryption delegation happens without ever reconstructing the complete key, so even a TEE compromise does not expose all secrets * **ZK proofs** make it scalable on-chain: instead of performing expensive verification logic on-chain, the protocol can submit compact proofs that are cheap to verify, allowing throughput to grow without proportionally increasing gas fees ## Distributed Key Management ### Quantum-Resistant Cryptography The current implementation uses ECIES on secp256k1. The target architecture plans to migrate toward **quantum-resistant algorithms**, ensuring long-term security of encrypted handles. ### Threshold Distribution ```mermaid flowchart TB subgraph split ["Key Split across n nodes"] direction LR S1["KMS Node 1"] S2["KMS Node 2"] S3["KMS Node 3"] SN["KMS Node n"] end Client["Client (Runner, Handle Gateway...)"] Client --> |request| S1 Client --> |request| S2 Client --> |request| S3 S1 --> |partial result| Client S2 --> |partial result| Client S3 --> |partial result| Client Client --> OUT["Recombine partial results
Key never reconstructed"] style split fill:#1e293b,color:#fff style Client fill:#7c3aed,color:#fff style OUT fill:#059669,color:#fff ``` The protocol's private key is Nox's most sensitive asset: whoever holds it can decrypt every handle in the system. The target architecture eliminates this single point of trust through **threshold cryptography**: the key is split across **n KMS nodes**, and at least **t nodes** must collaborate to perform any cryptographic operation. The full key is never reconstructed anywhere, not on any node, not in any message. ### Key Rotation A threshold architecture also enables **safe key rotation** without service interruption: key shares can be refreshed across nodes without ever exposing the current private key, and existing ciphertexts are re-encrypted under the new key as part of the process. ## Every Component Is Verified Before the protocol can allow third parties to operate components, it must guarantee that each component runs legitimate code, inside a genuine hardware TEE, and that this trust persists over time. The Nox chain of trust rests on three pillars: **code integrity verification**, **physical infrastructure verification**, and **controlled code evolution**. ### Code Integrity Each component (Runner, KMS node, Handle Gateway, Ingestor) runs inside an **Intel TDX TEE**. Before joining the protocol, each component goes through four verification steps: 1. **Code hash stored on-chain**: the exact hash of the authorized binary is recorded in the on-chain Registry. Only code whose hash matches can be accepted by the protocol 2. **Remote Attestation (RA)**: the TDX hardware generates a signed attestation report, proving that the execution environment is genuine, that the running code matches the expected hash, and that the TEE state has not been tampered with 3. **On-chain registration**: the attestation report is verified and the component's identity (public key + attestation hash) is recorded in the on-chain **Registry** contract 4. **Runtime authentication**: components communicate by signing every message with the attested private key. The receiving component verifies the signature against the on-chain Registry, confirming that the sender has been properly attested ```mermaid sequenceDiagram participant HW as TDX Hardware participant C as Component participant BC as On-chain Registry participant R as Receiving Component C->>HW: Request attestation report HW-->>C: Signed attestation (code hash + HW signature) C->>BC: Register (public key + attestation) BC->>BC: Verify attestation and hash, store identity C->>R: Signed request (attested key) R->>BC: Verify sender's key in Registry BC-->>R: Confirmed R->>R: Process request ``` ### Proof of Cloud: Physical Location Verification TEE attestation proves **what** code is running, but not **where** it is running. An operator with physical access to the hardware could attempt attacks that fall outside the TEE's threat model (side-channel attacks, voltage glitching, cold-boot attacks). Proof of Cloud closes this gap by cryptographically verifying that the hardware is located in a certified cloud provider's data center where the operator has no physical access. The mechanism binds TEE attestation to the physical platform through two independent roots of trust: 1. **TEE root of trust** (CPU-level): Intel TDX provides attestation of the confidential VM's code and data 2. **Platform root of trust** (TPM-level): a Trusted Platform Module on the physical server seals attestation keys to specific platform measurements, creating a cryptographic binding between the workload and the physical machine it runs on A public, append-only registry maps hardware identifiers to verified cloud facilities. If an operator attempted to relocate a workload to hardware they physically control, the attestation binding would break: the TPM measurements would mismatch, and the registry lookup would reveal that the hardware does not belong to a verified data center. The result: even a fully malicious operator is limited to a software-only adversary role. They control the host OS and hypervisor, but they have no path to plaintext data because they lack physical access to the TEE hardware, and this physical separation is cryptographically verifiable rather than merely assumed. ### Governed Upgrades The protocol must be able to evolve (bug fixes, new features, optimizations) while keeping the chain of trust intact. The target architecture governs upgrades through three mechanisms: * **On-chain governance**: any update to the authorized code hash goes through an on-chain governance process, ensuring that no individual operator can unilaterally change the code running on the network * **Reproducible builds**: component binaries are built deterministically (reproducible builds), allowing anyone to verify that the on-chain hash matches the public source code * **Transition period**: during an upgrade, the old and new hashes coexist for a defined period, giving operators time to migrate their nodes without service interruption This creates an unbroken **chain of trust**: from hardware attestation, through on-chain registration, through verified physical location, to runtime communication. No component can participate in the protocol without first proving its integrity and its physical environment, and every message between components is cryptographically tied to that proof. ## Open and Permissionless ```mermaid flowchart TB subgraph operators ["Operate"] direction TB O1["Stake RLC"] --> O2["Pass Attestation"] --> O3["Run a Node"] end subgraph developers ["Build"] direction TB D1["Write Primitive"] --> D2["Deploy on Network"] --> D3["Earn Fees"] end operators --> N["Nox Network"] developers --> N style operators fill:#059669,color:#fff style developers fill:#4a9eff,color:#fff style N fill:#7c3aed,color:#fff ``` With the chain of trust in place, the protocol no longer needs a single trusted operator. Nox is designed to be permissionless at every level: anyone can operate infrastructure, and anyone can extend the protocol with new functionality. ### Run the Network Any party can run any type of component (Runner, Ingestor, KMS node, Handle Gateway), provided they: 1. Pass remote attestation (proving they run legitimate code inside a genuine TEE) 2. Stake **RLC tokens** as economic collateral Staking ensures that operators have skin in the game. Malicious or negligent behavior (submitting incorrect results, going offline, attempting to forge attestations) triggers **slashing**: partial or total loss of staked tokens. Every component type can be operated by independent parties, making the protocol progressively decentralized as new operators join the network. ### Extend the Protocol The protocol is not meant to implement every possible confidential operation. The target architecture opens the development of **computation primitives** to the community: any developer can create new operations on encrypted data, deploy them on the network, and monetize them. A computation primitive is an operation executed by Runners inside the TEE: it receives encrypted handles as input, performs a computation on the plaintext inside the TEE, and produces new encrypted handles as output. The developer defines the logic, the protocol guarantees confidentiality and execution integrity. This openness applies to both infrastructure and innovation: operators earn by running the network, developers earn by extending it. ## One Privacy Layer, Every Chain ```mermaid flowchart TB subgraph chains ["Any Blockchain"] direction LR C1["Ethereum"] C2["Arbitrum"] C3["Base"] CN["Chain N"] end subgraph nox ["Shared Nox Backend"] direction LR KMS["KMS"] Runners["Runners"] GW["Handle Gateway"] end C1 --> nox C2 --> nox C3 --> nox CN --> nox style chains fill:#1e293b,color:#fff style nox fill:#7c3aed,color:#fff ``` The target architecture extends Nox to any blockchain through a single shared backend: the same KMS, Runners, and Handle Gateway serve all supported networks. Adding a new chain requires only deploying the on-chain contracts on the target network. The protocol core (encryption, key management, computation) remains shared and chain-agnostic. Throughput scales horizontally: multiple Runners operated by independent operators process computation requests in parallel, allowing capacity to grow with demand without any single point of bottleneck. ## Developer Experience Confidential computing should feel like standard Solidity development. The target architecture invests in tooling and SDK improvements to remove friction at every step of the developer workflow. ### Solidity Library Developers interact with encrypted values through opaque handles (`euint256`, `ebool`, etc.) without ever manipulating ciphertexts directly. The current model requires all operands to be handles, including plaintext constants (which must first be converted via `wrapAsPublicHandle`). A planned evolution is to support **mixed operand operations** natively, allowing a plaintext value to be passed directly alongside an encrypted handle without a prior conversion step. This removes boilerplate and makes confidential arithmetic feel as natural as standard Solidity. ### Hardhat and Foundry Plugins The target architecture provides first-class plugin support for the two dominant Solidity development frameworks. Developers will be able to write, test, and debug contracts using encrypted types directly within their existing Hardhat or Foundry workflows, without setting up a separate environment or relying on manual mocking of encrypted values. ## Learn More * [Global Architecture Overview](/protocol/global-architecture-overview) - Full component descriptions and data flows * [Runner](/protocol/runner) - Computation engine * [KMS](/protocol/kms) - Cryptographic protocol and threshold architecture --- --- url: /protocol/nox-smart-contracts.md description: >- On-chain contracts for confidential computation, access control and component registry --- # Nox Smart Contracts The on-chain layer of Nox consists of several Solidity contracts that manage computation requests, access control, and protocol component registration. ## Role in the Protocol Smart contracts are the on-chain entry point for all confidential operations. Users call the `Nox` library from their contracts, which routes through the `NoxCompute` contract. It validates handle proofs, verifies type compatibility between operands, grants transient access to result handles, and emits events to the computation queue for off-chain execution. At this point, the transaction is complete on-chain but the encrypted result does not yet exist: it will be computed by the [Runner](/protocol/runner) after the [Ingestor](/protocol/ingestor) picks up the event. ## How It Works ```mermaid sequenceDiagram participant U as User Contract participant TEE as NoxCompute participant ACL as ACL participant I as Ingestor (off-chain) U->>TEE: Call via Nox (e.g. Nox.add(a, b)) TEE->>TEE: Validate handle proofs TEE->>TEE: Verify type compatibility TEE->>ACL: Grant transient access on result handle TEE->>TEE: Emit computation event (e.g. Add) Note over TEE: On-chain execution ends here.
Result handle exists but has no ciphertext yet. I->>TEE: Poll events (off-chain) I->>I: Forward to Runner via NATS ``` ::: info Handles are deferred pointers A handle returned by `Nox.add(a, b)` is a deterministic identifier — not the encrypted result. The actual computation happens off-chain after the [Ingestor](/protocol/ingestor) picks up the event, and is processed sequentially by a [Runner](/protocol/runner). The ciphertext will be available in the [Handle Gateway](/protocol/handle-gateway) once the Runner has processed it. ::: ## NoxCompute The main entry point for confidential computations. It receives requests from user contracts, validates handle proofs, emits events that trigger off-chain computation, and manages the handle lifecycle. ### Handle Proof Validation When a user submits a handle created off-chain, the contract verifies its authenticity via an EIP-712 signed proof: ``` HandleProof(bytes32 handle, address owner, address app, uint256 createdAt) ``` **EIP-712 domain:** | Field | Value | | ----------------- | --------------------------- | | name | `"NoxCompute"` | | version | `"1"` | | chainId | Deployment chain ID | | verifyingContract | NoxCompute contract address | ### KMS Public Key `NoxCompute` stores the KMS public key used by the protocol to encrypt computation inputs for the Runner. The public key is set at deployment, can be updated by a protocol administrator, and can be retrieved from the contract by clients to encrypt inputs before submission. ```solidity function kmsPublicKey() external view returns (bytes memory); ``` ### Nox Library Smart contracts use the `Nox` library to interact with encrypted values. All functions emit events that the [Ingestor](/protocol/ingestor) monitors to trigger off-chain computation by the [Runner](/protocol/runner). **Core functions:** ```solidity // Convert plaintext to encrypted handle function toEuint256(uint256 value) returns (euint256); function toEbool(bool value) returns (ebool); // Arithmetic function add(euint256 lhs, euint256 rhs) returns (euint256); function sub(euint256 lhs, euint256 rhs) returns (euint256); function mul(euint256 lhs, euint256 rhs) returns (euint256); function div(euint256 lhs, euint256 rhs) returns (euint256); // Safe arithmetic (returns overflow flag + result) function safeAdd(euint256 lhs, euint256 rhs) returns (ebool, euint256); function safeSub(euint256 lhs, euint256 rhs) returns (ebool, euint256); function safeMul(euint256 lhs, euint256 rhs) returns (ebool, euint256); function safeDiv(euint256 lhs, euint256 rhs) returns (ebool, euint256); // Comparisons (return ebool) function eq(euint256 lhs, euint256 rhs) returns (ebool); function ne(euint256 lhs, euint256 rhs) returns (ebool); function lt(euint256 lhs, euint256 rhs) returns (ebool); function le(euint256 lhs, euint256 rhs) returns (ebool); function gt(euint256 lhs, euint256 rhs) returns (ebool); function ge(euint256 lhs, euint256 rhs) returns (ebool); // Conditional selection function select(ebool cond, euint256 ifTrue, euint256 ifFalse) returns (euint256); ``` **Safe arithmetic:** The `safe*` variants (`safeAdd`, `safeSub`, `safeMul`, `safeDiv`) protect against overflow and underflow in confidential arithmetic. Each returns two values: * An `ebool` flag: `true` if the operation completed without overflow or underflow, `false` otherwise * A `euint256` result: the computed value when the operation succeeded This is necessary because the protocol cannot revert on overflow — the operands are encrypted, so their values are not visible on-chain. The `ebool` flag lets the calling contract handle the overflow case conditionally using `Nox.select`: ```solidity (ebool ok, euint256 result) = Nox.safeAdd(balanceA, balanceB); // Apply the result only if there was no overflow euint256 finalBalance = Nox.select(ok, result, balanceA); ``` **Advanced functions:** Advanced functions are aggregations of multiple arithmetic operations bundled into a single call. Instead of chaining individual operations (each emitting a separate event and triggering a dedicated off-chain computation job), these functions perform the full operation atomically: one event is emitted and the Runner executes the entire sequence in a single pass. This reduces both on-chain gas costs and off-chain processing overhead. ```solidity // Confidential token transfer function transfer(euint256 balanceFrom, euint256 balanceTo, euint256 amount) returns (ebool success, euint256 newBalanceFrom, euint256 newBalanceTo); // Confidential mint function mint(euint256 balanceTo, euint256 amount, euint256 totalSupply) returns (ebool success, euint256 newBalanceTo, euint256 newTotalSupply); // Confidential burn function burn(euint256 balanceFrom, euint256 amount, euint256 totalSupply) returns (ebool success, euint256 newBalanceFrom, euint256 newTotalSupply); ``` ### Mixing Plaintext and Encrypted Values All Nox operations require **both operands to be handles**. To combine an encrypted value with a plaintext constant, first convert the plaintext to a handle using `toEuint256` (or the matching `to*` function for the target type): ```solidity // Goal: compute encryptedBalance + 100 euint256 handleA = ...; // existing encrypted handle // Step 1: wrap the plaintext constant into a handle euint256 handleB = Nox.toEuint256(100); // Step 2: now both operands are handles euint256 result = Nox.add(handleA, handleB); ``` `toEuint256` emits its own event. The Runner encrypts the value off-chain and stores it in the Handle Gateway before it can be used as an operand. ::: info **Roadmap:** Native support for mixed operands (plaintext alongside encrypted handles, without a prior conversion) is planned. See [Protocol Vision — Solidity Library](/protocol/protocol-vision#solidity-library). ::: ### Encrypted Types The protocol supports all standard Solidity types in encrypted form. The full type mapping is described in the `NoxType` enum in [TypeUtils.sol](https://github.com/iExec-Nox/nox-contracts/blob/main/contracts/shared/TypeUtils.sol#L8). ## ACL (Access Control List) Manages permissions for encrypted handles. Determines who can use a handle as input to a computation and who can decrypt the associated data. ### Roles | Role | Capability | | ---------- | --------------------------------------------- | | **Admin** | Use handle as computation input, manage perms | | **Viewer** | Decrypt the associated data | | **Public** | Anyone can decrypt (if explicitly set) | ### Key Functions ```solidity // Grant persistent access function allow(bytes32 handle, address account) external; // Grant one-time access (cleared after use) function allowTransient(bytes32 handle, address account) external; // Check access function isAllowed(bytes32 handle, address account) view returns (bool); // Make handle publicly decryptable function allowPublicDecryption(bytes32 handle) external; ``` ### Transient vs. Persistent Access When `NoxCompute` creates a result handle, it grants only **transient** access to the calling contract. This is intentional for two reasons: * **Gas efficiency**: writing persistent ACL entries for every intermediate result handle would be expensive. Transient access requires no storage write. * **Separation of concerns**: `NoxCompute` has no knowledge of the application's access model. It is the responsibility of the calling contract to decide which handles need to persist and who should be allowed to use or decrypt them. ::: warning Persist the ACL or lose the handle If your contract needs to reuse a result handle in a future transaction (store it in state, pass it to another function, or allow a user to decrypt it), you must explicitly grant persistent access before the transaction ends ::: ```solidity euint256 result = Nox.add(a, b); ACL.allow(euint256.unwrap(result), address(this)); // persist for your contract ACL.addViewer(euint256.unwrap(result), user); // allow user to decrypt ``` Without this, the handle reference will exist in state but nobody will have permission to use it. ## Handle Structure A handle is a unique 32-byte identifier that references an encrypted value in the protocol. It does not contain the ciphertext itself, but allows locating it in the off-chain database and extracting its properties in O(1). A handle is **deterministic**: the same operation on the same inputs always produces the same handle. ``` [0] [1------4] [5] [6] [7---------------------31] Version Chain ID Type Attrs prehandle (truncated) ``` | Segment | Size | Description | | ---------- | -------- | --------------------------------------------------------------------- | | Version | 1 byte | Handle format version (currently `0x00`) | | Chain ID | 4 bytes | Chain ID encoded as uint32; binds the handle to a specific blockchain | | Type | 1 byte | Solidity type of the encrypted value | | Attributes | 1 byte | Handle attributes/properties (e.g. uniqueness) | | Prehandle | 25 bytes | Truncated keccak256 hash that uniquely identifies the ciphertext | The full type mapping (byte 5) is described in the `NoxType` enum in [TypeUtils.sol](https://github.com/iExec-Nox/nox-contracts/blob/main/contracts/shared/TypeUtils.sol#L8). ### Required Properties * **Determinism**: same operation + same inputs + same chain = same handle * **Cross-chain uniqueness**: the chain ID prevents handle reuse across chains * **Deployment isolation**: the NoxCompute address is included in the hash, binding the handle to a specific protocol instance * **Verifiable type**: the type can be extracted in O(1) without external calls * **Versioning**: allows format evolution while maintaining backward compatibility ### Prehandle Construction The prehandle segment is derived from a keccak256 hash (32 bytes); only 25 bytes of it are stored in the handle. Its computation differs depending on the handle origin: **Computation result** (on-chain, e.g. add, sub, transfer): ```solidity bytes32 prehandle = keccak256(abi.encodePacked( operator, // Operator enum (Add, Sub, Div, ...) operands, // Array of input handles address(this), // NoxCompute address msg.sender, // Calling contract block.timestamp, outputIndex // For multi-output operations (0, 1, ...) )); ``` **User input** (off-chain, via Handle Gateway `POST /v0/secrets`): ```solidity bytes32 prehandle = randomValue; ``` For user inputs, the prehandle is a random value generated by the Handle Gateway. The handle is then validated on-chain via an EIP-712 signed `HandleProof`. ## Learn More * [Runner](/protocol/runner) - Executes the off-chain computations * [Handle Gateway](/protocol/handle-gateway) - Manages encrypted handle data * [Ingestor](/protocol/ingestor) - Monitors contract events * [Global Architecture Overview](/protocol/global-architecture-overview) --- --- url: /protocol/ingestor.md description: >- Blockchain monitoring service that bridges on-chain events to the off-chain computation pipeline --- # Ingestor The Ingestor is a Rust service running in Intel TDX that continuously monitors the blockchain for events emitted by the `NoxCompute` contract and publishes them to a [NATS JetStream](https://nats.io) queue for processing by the [Runner](/protocol/runner). ## Role in the Protocol The Ingestor bridges on-chain and off-chain worlds: it detects computation requests emitted as Solidity events and forwards them as structured messages to the message queue. It is the starting point of every off-chain computation. ## How It Works ```mermaid sequenceDiagram participant BC as Blockchain participant I as Ingestor participant NATS as NATS JetStream BC->>I: New block produced I->>I: Filter NoxCompute logs I->>I: Group events by tx hash I->>NATS: Publish TransactionMessage I->>I: Persist block number (crash recovery) ``` 1. **Poll blocks**: the Ingestor reads new blocks from the blockchain RPC in batches, with a configurable polling interval. 2. **Parse events**: contract logs are parsed using Alloy's `sol!` macro for compile-time type safety. Each event is mapped to a strongly-typed Rust struct. 3. **Group by transaction**: events from the same transaction are grouped into a single `TransactionMessage` and ordered by `log_index`, preserving on-chain execution order. 4. **Publish to NATS**: the message is published to NATS JetStream with a checksum-based message ID for deduplication (prevents duplicate processing on restarts). 5. **Persist state**: the last processed block number is saved to disk, allowing the Ingestor to resume from where it left off after a restart. ## Monitored Events The Ingestor listens for **all events** emitted by the `NoxCompute` contract. Each event corresponds to a computation primitive (arithmetic, comparisons, token operations, etc.). See [Solidity Library](/references/solidity-library) for the full list. ## Key Design Choices * **Optimistic processing**: blocks are processed as soon as they appear, without waiting for confirmations. This enables low-latency event detection. If a processed event belongs to a block that is later reorganized (fork), the corresponding handle is removed from the canonical chain. * **Deduplication**: NATS JetStream message IDs based on content checksums prevent duplicate processing when the Ingestor restarts and re-reads recent blocks. * **Stateless scaling**: multiple Ingestor instances can run in parallel for redundancy. NATS JetStream deduplication ensures each event is processed only once. ## Learn More * [Runner](/protocol/runner) - Processes the computation requests * [Handle Gateway](/protocol/handle-gateway) - Stores encrypted handle data * [Nox Smart Contracts](/protocol/nox-smart-contracts) - Emits the monitored events * [Global Architecture Overview](/protocol/global-architecture-overview) --- --- url: /protocol/runner.md description: Off-chain computation engine for confidential operations on encrypted data --- # Runner The Runner is a Rust service running in Intel TDX that executes confidential computations on encrypted data. It pulls computation requests from a [NATS JetStream](https://nats.io) queue — populated by the [Ingestor](/protocol/ingestor) from on-chain events — decrypts the input handles, performs the operation, encrypts the results, and stores them back in the [Handle Gateway](/protocol/handle-gateway). ## Role in the Protocol The Runner is the computation engine of the Nox protocol. It is the only component that manipulates plaintext values (in memory, never on disk). For supported Solidity types, it produces results identical to their on-chain equivalents. The off-chain execution model also opens the door to operations not natively supported by the EVM (e.g. larger integer types). ::: info Current Implementation The current implementation runs a **single Runner**. In the long-term architecture, multiple Runners will operate in parallel, coordinated by a TDX orchestrator that assigns tasks and supervises execution. ::: ## How It Works ```mermaid sequenceDiagram participant NATS as NATS Queue participant R as Runner participant GW as Handle Gateway participant KMS R->>NATS: Pull next event NATS-->>R: TransactionMessage R->>GW: GET /v0/compute/operands (RSA pubkey) GW->>KMS: POST /v0/delegate (K, RSA pubkey) KMS-->>GW: encryptedSharedSecret GW-->>R: ciphertext + encryptedSharedSecret + nonce R->>R: Decrypt inputs (RSA + HKDF + AES-GCM) R->>R: Execute computation R->>R: Encrypt results (ECIES) R->>GW: POST /v0/compute/results R->>NATS: Acknowledge (delete event) ``` 1. **Pull event** from NATS: the Runner fetches the next `TransactionMessage` containing input handles, output handles, and the operation to perform. 2. **Fetch operands** from the Handle Gateway: the Runner sends its RSA public key, and the Handle Gateway handles KMS delegation internally, returning the ciphertext, encrypted shared secret, and nonce for each input handle. 3. **Decrypt** inputs locally (RSA decrypt shared secret, HKDF, AES-GCM). 4. **Execute** the computation primitive. 5. **Encrypt** results with ECIES using the protocol public key. 6. **Submit** encrypted results to the Handle Gateway. 7. **Acknowledge** the event in NATS (removes it from the queue) and pull the next one. ### WrapAsPublicHandle (Special Case) This operation has no input handles and does not need to call the Handle Gateway to fetch operands. The plaintext value and target type of the data to encrypt are embedded directly in the NATS event. The Runner performs the ECIES encryption, then forwards encrypted data and crypto materials to the Handle Gateway. ```mermaid sequenceDiagram participant NATS as NATS Queue participant R as Runner participant GW as Handle Gateway R->>NATS: Pull next event NATS-->>R: TransactionMessage (plaintext + type) R->>R: Encrypt plaintext (ECIES) R->>GW: POST /v0/compute/results R->>NATS: Acknowledge (delete event) ``` ## Handle Requirements Each operation defines its input/output handles: | Operation | Inputs | Outputs | Description | | ------------------ | ------ | ------- | ---------------------------------- | | WrapAsPublicHandle | 0 | 1 | Encrypt a plaintext value | | Arithmetic (add…) | 2 | 1 | Two operands → one result | | Safe arithmetic | 2 | 2 | Two operands → (success, result) | | Comparisons | 2 | 1 | Two operands → bool result | | Select | 3 | 1 | (condition, ifTrue, ifFalse) → one | | Transfer | 3 | 3 | (amount, balFrom, balTo) → 3 | | Mint | 3 | 3 | (amount, balTo, supply) → 3 | | Burn | 3 | 3 | (amount, balFrom, supply) → 3 | **Validation rules:** * All input handles **must exist** in the Gateway database * All output handles **must not exist** in the Gateway database ## Computation Primitives All arithmetic uses **wrapping semantics**, matching Solidity's `unchecked` behavior. On overflow or underflow, values wrap around the type boundary instead of reverting. Token operations (Transfer, Mint, Burn) never revert either: they silently cap at available balances to prevent leaking information. For the complete reference of all operations, including Solidity signatures, edge cases and examples, see the [Solidity Library](/references/solidity-library). ## Learn More * [Solidity Library](/references/solidity-library) - Full reference of all operations with signatures and edge cases * [Handle Gateway](/protocol/handle-gateway) - Handle storage and encryption * [KMS](/protocol/kms) - Key management and decryption delegation * [Nox Smart Contracts](/protocol/nox-smart-contracts) - On-chain computation requests * [Global Architecture Overview](/protocol/global-architecture-overview) --- --- url: /protocol/handle-gateway.md description: 'Handle Gateway for encryption, storage and access to encrypted data' --- # Handle Gateway The Handle Gateway is a Rust service running in Intel TDX. It is the single entry point for creating, storing and accessing encrypted handle data. It encrypts plaintext values using the KMS public key, interacts with an **AWS S3 bucket** of handles, and coordinates decryption delegation with the [KMS](/protocol/kms). ## Role in the Protocol The Handle Gateway sits between users/Runners and the encrypted data store. It has four main responsibilities: 1. **Encrypt** plaintext values submitted by users (via SDK), then **store** them as handles. 2. **Verify** access control by checking the on-chain ACLs. 3. **Delegate decryption** by coordinating with the KMS and serving the cryptographic material to authorized users and Runners. 4. **Store** encrypted computation results submitted by Runners as handles. ## How It Works ### Handle Creation ```mermaid sequenceDiagram participant U as User (SDK) participant GW as Handle Gateway participant SC as NoxCompute GW->>SC: ETH call kmsPublicKey() during startup SC-->>GW: protocol public encryption key U->>GW: POST /v0/secrets (plaintext, type, owner) GW->>GW: ECIES encryption with KMS public key GW->>GW: Store (handle, ciphertext, K, nonce) in AWS S3 bucket GW->>GW: Sign HandleProof (EIP-712) GW-->>U: handle + proof ``` ### Encryption Process ```mermaid flowchart LR A[Plaintext] --> B[Generate ephemeral key k] B --> C["Compute shared secret: k * pubkey_KMS"] C --> D[Derive AES-256 key via HKDF] D --> E[AES-256-GCM encrypt] E --> F["Store (ciphertext, K=k*G, nonce) in AWS S3 bucket"] ``` ### User Decryption ```mermaid sequenceDiagram participant U as User (SDK) participant GW as Handle Gateway participant KMS U->>U: Generate ephemeral RSA keypair U->>GW: GET /v0/secrets/:handle (RSA pubkey, EIP-712 auth) GW->>GW: Verify signature + check on-chain ACL GW->>GW: Retrieve (ciphertext, K, nonce) from AWS S3 bucket GW->>KMS: POST /v0/delegate (K, RSA pubkey) KMS-->>GW: encryptedSharedSecret GW-->>U: ciphertext + encryptedSharedSecret + nonce U->>U: RSA decrypt, HKDF, AES-GCM decrypt → plaintext ``` ## Storage ::: info Current Implementation The current implementation uses an **AWS S3 bucket** without finance-grade certifications. The production environment will target a **S3 bucket service** with finance-grade certifications to provide regulatory compliance and auditability guarantees for encrypted data at rest. ::: ## Exchange Format Conventions * Data exchanged in **JSON** format * Object keys use **camelCase** * Binary data encoded as **hexadecimal strings with `0x` prefix** ## Learn More * [KMS](/protocol/kms) - Key Management Service * [Runner](/protocol/runner) - Computation execution * [Nox Smart Contracts](/protocol/nox-smart-contracts) - On-chain contracts * [Global Architecture Overview](/protocol/global-architecture-overview) --- --- url: /protocol/kms.md description: Key Management Service for protocol encryption and decryption delegation --- # Key Management Service (KMS) The KMS is a Rust service running in Intel TDX that manages the protocol's cryptographic keys. It **never decrypts data itself**. It only delegates decryption by providing the cryptographic material (shared secret) needed for authorized parties to decrypt locally. ## Role in the Protocol The KMS holds the **protocol private key** (secp256k1). All data stored in the Handle Gateway is encrypted under the corresponding public key. When a user or Runner needs to decrypt, the KMS performs a **decryption delegation**: it computes a shared secret and encrypts it with the requester's RSA public key, so only the requester can recover it. ## How It Works ### Decryption Delegation Flow ```mermaid sequenceDiagram participant R as Requester (User/Runner) participant GW as Handle Gateway participant KMS R->>R: Generate ephemeral RSA keypair R->>GW: Request decryption (handle, RSA pubkey) GW->>GW: Retrieve (ciphertext, K, nonce) from DB GW->>KMS: POST /v0/delegate (K, RSA pubkey) KMS->>KMS: Compute S = K * privkey_KMS KMS->>KMS: RSA-OAEP encrypt S with RSA pubkey KMS-->>GW: encryptedSharedSecret GW-->>R: ciphertext + encryptedSharedSecret + nonce R->>R: RSA decrypt → S R->>R: HKDF(S) → AES-256 key R->>R: AES-GCM decrypt(ciphertext, nonce) → plaintext ``` ::: warning Security The KMS never sees plaintext data. Only the RSA-encrypted shared secret travels over the network. The requester performs the final decryption locally. ::: ### ECIES Encryption Scheme The protocol uses **ECIES** (Elliptic Curve Integrated Encryption Scheme) on secp256k1: 1. Generate a random ephemeral scalar `k` 2. Compute the ephemeral public key `K = k * G` 3. Compute the shared secret `S = k * pubkey_KMS` 4. Derive an AES-256 key from `S` using HKDF-SHA256 5. Encrypt plaintext with AES-256-GCM (random 12-byte nonce) 6. Store: `(ciphertext, K, nonce)` alongside the handle ### Key Derivation (HKDF) | Parameter | Value | | --------- | ------------------------ | | Hash | SHA-256 | | IKM | Shared secret (32 bytes) | | Salt | 32 bytes of zeros | | Info | `"ECIES:AES_GCM:v1"` | | Output | 32 bytes (AES-256 key) | ## Threshold Architecture The long-term design uses **threshold cryptography** based on [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing) (SSS): * The private key is split into **n shares** using a random polynomial of degree `t-1`, where `P(0) = privkey_KMS` * Each KMS node `i` holds `share_i = P(i)`, one point on the polynomial * At least **t shares** are needed to reconstruct `P(0)` via **Lagrange interpolation**. Fewer than `t` shares reveal nothing * In practice, the private key is **never reconstructed**. Each node computes a partial result `K * share_i`, and the client recombines them using Lagrange coefficients: `Σ λ_i * (K * share_i) = K * privkey_KMS` This eliminates single points of failure: compromising fewer than `t` nodes reveals nothing about the private key. ::: info Current Implementation The MVP runs a **single KMS node** that holds the full private key. The threshold protocol (t/n with distributed key generation) is the target architecture for production. ::: ## Learn More * [Global Architecture Overview](/protocol/global-architecture-overview) * [Handle Gateway](/protocol/handle-gateway) * [Runner](/protocol/runner) --- --- url: /references/glossary.md description: Glossary of terms used in the Nox protocol --- # Glossary Definitions of technical terms used in the Nox protocol. Use the search bar to find a term by name or definition, or filter by category. --- --- url: /guides/welcome.md description: Step-by-step guides for building with Nox --- # Guides Welcome to the Nox guides section. Here you'll find step-by-step tutorials for building confidential smart contracts and tokens. ## Build Confidential Smart Contracts Learn how to create privacy-preserving smart contracts: * [Introduction](/guides/build-confidential-smart-contracts/intro) - Get started with confidential contracts * [Hardhat](/guides/build-confidential-smart-contracts/hardhat) - Build with Hardhat * [Foundry](/guides/build-confidential-smart-contracts/foundry) - Build with Foundry ## Build Confidential Tokens Create ERC-7984 compliant confidential tokens: * [Introduction](/guides/build-confidential-tokens/intro) - Get started with confidential tokens * [ERC7984 Token](/guides/build-confidential-tokens/erc7984-token) - Create native confidential tokens * [ERC20 to ERC7984](/guides/build-confidential-tokens/erc20-to-erc7984-wrapper) - Wrap existing ERC20 tokens * [Live Demo](/guides/build-confidential-tokens/demo) - Explore a live application showcasing confidential tokens ## Manage Handle Access Control access to encrypted data with Nox: * [Introduction](/guides/manage-handle-access/intro) - Get started with managing handle access * [Viewers](/guides/manage-handle-access/viewers) - Manage viewers for handles * [Admins](/guides/manage-handle-access/admins) - Manage admins for handles * [Public Decryption](/guides/manage-handle-access/public-decryption) - Manage public decryption for handles * [Transient Access](/guides/manage-handle-access/transient-access) - Grant single-use access to handles