<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: rayQu</title>
    <description>The latest articles on DEV Community by rayQu (@rollingindo).</description>
    <link>https://dev.to/rollingindo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2700932%2Ff08a1667-4508-436f-82f9-75f307242ab5.png</url>
      <title>DEV Community: rayQu</title>
      <link>https://dev.to/rollingindo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rollingindo"/>
    <language>en</language>
    <item>
      <title>Building a Confidential OTC Settlement Protocol with Oasis Sapphire &amp; Base</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Mon, 20 Jul 2026 07:06:17 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-otc-settlement-protocol-with-oasis-sapphire-base-23ki</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-otc-settlement-protocol-with-oasis-sapphire-base-23ki</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Learn how to build a privacy-preserving OTC trading protocol where counterparties negotiate confidentially using Oasis Sapphire while settling trades transparently on Base.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Large cryptocurrency trades rarely happen on decentralized exchanges.&lt;/p&gt;

&lt;p&gt;When someone wants to swap a few hundred dollars worth of ETH for USDC, an automated market maker like Uniswap works exceptionally well. Liquidity is abundant, settlement is immediate, and the entire process can be completed in a single transaction.&lt;/p&gt;

&lt;p&gt;The picture changes completely once the trade size reaches institutional territory.&lt;/p&gt;

&lt;p&gt;Imagine a DAO treasury planning to exchange &lt;strong&gt;500,000 USDC&lt;/strong&gt; for ETH.&lt;/p&gt;

&lt;p&gt;Executing that transaction through a public liquidity pool immediately reveals valuable information to the entire market. Arbitrage bots begin simulating profitable routes before the transaction is finalized. Market makers widen spreads. Searchers compete to extract value from the pending order, while other traders infer the treasury's strategy simply by observing the transaction.&lt;/p&gt;

&lt;p&gt;The protocol itself is functioning exactly as designed.&lt;/p&gt;

&lt;p&gt;The problem is that transparency and negotiation don't always belong together.&lt;/p&gt;

&lt;p&gt;This is precisely why Over-the-Counter (OTC) trading exists.&lt;/p&gt;

&lt;p&gt;Rather than exposing an order to an entire market, two counterparties negotiate privately before agreeing on a final settlement price. Only after both parties reach an agreement is the trade executed.&lt;/p&gt;

&lt;p&gt;Traditional financial institutions have relied on this model for decades because confidentiality reduces information leakage and allows large transactions to occur without unnecessarily moving markets.&lt;/p&gt;

&lt;p&gt;Public blockchains introduce an interesting challenge.&lt;/p&gt;

&lt;p&gt;How do you preserve confidential negotiations while still benefiting from decentralized settlement?&lt;/p&gt;

&lt;p&gt;This is where Oasis Sapphire becomes particularly interesting.&lt;/p&gt;

&lt;p&gt;Instead of treating confidentiality as an application-layer concern, Sapphire provides confidential smart contract execution directly within an EVM-compatible environment. Sensitive contract state remains protected while developers continue writing familiar Solidity code.&lt;/p&gt;

&lt;p&gt;In this tutorial we'll build a simplified OTC trading protocol that combines confidential negotiation on Oasis Sapphire with transparent settlement on Base.&lt;/p&gt;

&lt;p&gt;Rather than building another decentralized exchange, we'll focus on the negotiation process itself.&lt;/p&gt;

&lt;p&gt;By the end of this guide you'll have built a protocol capable of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating confidential OTC trade offers&lt;/li&gt;
&lt;li&gt;Receiving private counterparty quotes&lt;/li&gt;
&lt;li&gt;Accepting offers without revealing negotiations publicly&lt;/li&gt;
&lt;li&gt;Escrowing assets before settlement&lt;/li&gt;
&lt;li&gt;Executing the final settlement on Base&lt;/li&gt;
&lt;li&gt;Cancelling expired orders safely&lt;/li&gt;
&lt;li&gt;Preventing replay attacks through signed messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although the protocol we'll build is intentionally simplified for educational purposes, its architecture mirrors patterns commonly found in production financial systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding the Problem
&lt;/h1&gt;

&lt;p&gt;Before writing a single line of Solidity, it's worth asking an important question.&lt;/p&gt;

&lt;p&gt;Why don't institutions simply use AMMs for everything?&lt;/p&gt;

&lt;p&gt;After all, decentralized exchanges already provide liquidity, transparent pricing, and permissionless access.&lt;/p&gt;

&lt;p&gt;The answer comes down to market impact.&lt;/p&gt;

&lt;p&gt;Suppose Alice wants to purchase &lt;strong&gt;400 ETH&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If that trade is submitted directly to a public liquidity pool, several things happen almost immediately.&lt;/p&gt;

&lt;p&gt;First, the pending transaction becomes visible to sophisticated searchers monitoring the network.&lt;/p&gt;

&lt;p&gt;Next, those searchers estimate the price impact by simulating the trade against current liquidity.&lt;/p&gt;

&lt;p&gt;If profitable opportunities exist, competing transactions are constructed before Alice's order has even been finalized.&lt;/p&gt;

&lt;p&gt;Even if nobody explicitly frontruns the transaction, the market itself now possesses information that previously existed only inside Alice's trading strategy.&lt;/p&gt;

&lt;p&gt;That information has value.&lt;/p&gt;

&lt;p&gt;The larger the order becomes, the more valuable it becomes.&lt;/p&gt;

&lt;p&gt;The sequence below illustrates what happens inside a typical public execution environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram

participant Alice
participant Ethereum
participant Searcher
participant LiquidityPool

Alice-&amp;gt;&amp;gt;Ethereum: Submit Swap

Ethereum--&amp;gt;&amp;gt;Searcher: Pending Transaction

Searcher-&amp;gt;&amp;gt;Searcher: Simulate Trade

Searcher-&amp;gt;&amp;gt;LiquidityPool: Competing Transactions

LiquidityPool--&amp;gt;&amp;gt;Alice: Final Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing malicious has occurred.&lt;/p&gt;

&lt;p&gt;Every participant simply acted upon publicly available information.&lt;/p&gt;

&lt;p&gt;For large trades, however, that transparency introduces unnecessary information leakage.&lt;/p&gt;

&lt;p&gt;OTC trading avoids this problem entirely by moving negotiation away from the public market.&lt;/p&gt;

&lt;p&gt;Only the final agreement becomes visible.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Oasis Sapphire?
&lt;/h1&gt;

&lt;p&gt;One obvious question is why we need another blockchain at all.&lt;/p&gt;

&lt;p&gt;Couldn't we simply negotiate off-chain using encrypted messages?&lt;/p&gt;

&lt;p&gt;In some situations, yes.&lt;/p&gt;

&lt;p&gt;However, purely off-chain negotiation introduces several new trust assumptions.&lt;/p&gt;

&lt;p&gt;Who stores the offers?&lt;/p&gt;

&lt;p&gt;Who guarantees neither party modified the terms?&lt;/p&gt;

&lt;p&gt;How do we prove a particular offer existed if negotiations break down?&lt;/p&gt;

&lt;p&gt;How do multiple market makers compete fairly?&lt;/p&gt;

&lt;p&gt;A confidential smart contract solves these coordination problems.&lt;/p&gt;

&lt;p&gt;Instead of trusting a centralized OTC desk, participants submit encrypted offers into a shared protocol.&lt;/p&gt;

&lt;p&gt;The blockchain enforces protocol rules while keeping sensitive trading information confidential.&lt;/p&gt;

&lt;p&gt;From the outside, observers know an offer exists.&lt;/p&gt;

&lt;p&gt;They do &lt;strong&gt;not&lt;/strong&gt; learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which asset is being traded,&lt;/li&gt;
&lt;li&gt;the quoted price,&lt;/li&gt;
&lt;li&gt;the requested amount,&lt;/li&gt;
&lt;li&gt;acceptable settlement conditions,&lt;/li&gt;
&lt;li&gt;or which counterparty accepted the offer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only the final settlement transaction becomes public.&lt;/p&gt;

&lt;p&gt;That distinction dramatically reduces information leakage while preserving decentralized execution.&lt;/p&gt;




&lt;h1&gt;
  
  
  System Overview
&lt;/h1&gt;

&lt;p&gt;Our protocol consists of five independent components.&lt;/p&gt;

&lt;p&gt;Each has a single responsibility.&lt;/p&gt;

&lt;p&gt;Separating these concerns keeps the contracts significantly easier to audit and makes future upgrades less risky.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR

User["Trader"]

Frontend["Next.js Frontend"]

SDK["TypeScript SDK"]

Registry["OTC Registry"]

Escrow["Escrow Contract"]

Coordinator["Settlement Coordinator"]

Base["Base Network"]

User --&amp;gt; Frontend

Frontend --&amp;gt; SDK

SDK --&amp;gt; Registry

Registry --&amp;gt; Coordinator

Coordinator --&amp;gt; Escrow

Escrow --&amp;gt; Base
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's briefly examine the role of each component.&lt;/p&gt;

&lt;h3&gt;
  
  
  OTC Registry
&lt;/h3&gt;

&lt;p&gt;The registry acts as the confidential marketplace.&lt;/p&gt;

&lt;p&gt;Every new offer enters the protocol through this contract.&lt;/p&gt;

&lt;p&gt;Rather than immediately transferring assets, users first describe the trade they would like to perform.&lt;/p&gt;

&lt;p&gt;Those details remain confidential until the offer is accepted.&lt;/p&gt;




&lt;h3&gt;
  
  
  Escrow Contract
&lt;/h3&gt;

&lt;p&gt;Once two counterparties agree on a trade, both assets must be secured before settlement begins.&lt;/p&gt;

&lt;p&gt;Instead of relying on trust between participants, the escrow contract temporarily locks both sides of the trade.&lt;/p&gt;

&lt;p&gt;Neither participant can withdraw funds unilaterally after acceptance.&lt;/p&gt;

&lt;p&gt;Only successful settlement or cancellation releases escrowed assets.&lt;/p&gt;




&lt;h3&gt;
  
  
  Settlement Coordinator
&lt;/h3&gt;

&lt;p&gt;The final contract coordinates settlement on Base.&lt;/p&gt;

&lt;p&gt;Once escrow conditions are satisfied, settlement becomes deterministic.&lt;/p&gt;

&lt;p&gt;This separation keeps negotiation logic isolated from execution logic, significantly simplifying auditing.&lt;/p&gt;




&lt;h1&gt;
  
  
  Project Architecture
&lt;/h1&gt;

&lt;p&gt;We'll build the project as a monorepo.&lt;/p&gt;

&lt;p&gt;Although our example remains relatively small, organizing the repository this way closely resembles production Solidity projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;confidential-otc-protocol/

├── contracts/
│
│   OTCRegistry.sol
│
│   Escrow.sol
│
│   SettlementCoordinator.sol
│
│   libraries/
│
│      SignatureVerifier.sol
│
│      OfferHasher.sol
│
│
├── sdk/
│
│   otcClient.ts
│
│   signer.ts
│
│   quotes.ts
│
├── backend/
│
│   api.ts
│
│   relayer.ts
│
│
├── frontend/
│
│   app/
│
├── script/
│
├── test/
│
└── docs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Throughout this tutorial we'll implement each directory individually before integrating everything into a complete protocol.&lt;/p&gt;

&lt;p&gt;By the end, you'll have a repository structured much more like a production codebase than a simple Solidity example.&lt;/p&gt;




&lt;h1&gt;
  
  
  Protocol Lifecycle
&lt;/h1&gt;

&lt;p&gt;Before diving into the implementation, it's useful to visualize the complete lifecycle of an OTC trade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stateDiagram-v2

[*] --&amp;gt; Draft

Draft --&amp;gt; Submitted

Submitted --&amp;gt; Quoted

Quoted --&amp;gt; Accepted

Accepted --&amp;gt; Escrowed

Escrowed --&amp;gt; Settled

Submitted --&amp;gt; Cancelled

Quoted --&amp;gt; Cancelled

Accepted --&amp;gt; Expired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that settlement is only one stage of the protocol.&lt;/p&gt;

&lt;p&gt;Most of the protocol's complexity actually happens &lt;strong&gt;before&lt;/strong&gt; assets move.&lt;/p&gt;

&lt;p&gt;Negotiation, verification, escrow, and acceptance each introduce their own state transitions.&lt;/p&gt;

&lt;p&gt;Designing these transitions carefully is one of the easiest ways to eliminate entire classes of smart contract bugs before they ever appear.&lt;/p&gt;

&lt;p&gt;In the next section we'll begin implementing the protocol's core contract: &lt;strong&gt;OTCRegistry.sol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than jumping directly into business logic, we'll first design the underlying data model and explain why each field exists before writing any functions.&lt;/p&gt;




&lt;h1&gt;
  
  
  Setting Up the Project
&lt;/h1&gt;

&lt;p&gt;Before writing any Solidity, let's create the project structure.&lt;/p&gt;

&lt;p&gt;We'll use &lt;strong&gt;Foundry&lt;/strong&gt; because it has become the de facto standard for professional Solidity development. It provides fast compilation, excellent testing utilities, built-in fuzzing, and straightforward deployment scripts.&lt;/p&gt;

&lt;p&gt;If you haven't already installed Foundry, you can do so with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://foundry.paradigm.xyz | bash
foundryup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a new project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;forge init confidential-otc-protocol

&lt;span class="nb"&gt;cd &lt;/span&gt;confidential-otc-protocol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also install OpenZeppelin contracts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;forge &lt;span class="nb"&gt;install &lt;/span&gt;OpenZeppelin/openzeppelin-contracts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our project now looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;confidential-otc-protocol/

├── lib/
│
├── script/
│
├── src/
│
├── test/
│
├── foundry.toml
│
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Throughout the tutorial we'll gradually replace the default template with our protocol implementation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Foundry?
&lt;/h1&gt;

&lt;p&gt;Hardhat remains an excellent framework, but Foundry offers several advantages for protocol development.&lt;/p&gt;

&lt;p&gt;First, tests execute considerably faster because they're written directly in Solidity.&lt;/p&gt;

&lt;p&gt;Second, fuzz testing is built into the framework instead of requiring external plugins.&lt;/p&gt;

&lt;p&gt;Finally, cheatcodes make it straightforward to simulate different users, timestamps, balances, and blockchain conditions.&lt;/p&gt;

&lt;p&gt;Since we're building financial infrastructure, confidence in our testing environment matters just as much as confidence in the contracts themselves.&lt;/p&gt;




&lt;h1&gt;
  
  
  Designing the Registry
&lt;/h1&gt;

&lt;p&gt;Most blockchain tutorials begin with functions.&lt;/p&gt;

&lt;p&gt;Production protocols begin with data.&lt;/p&gt;

&lt;p&gt;The quality of a smart contract is largely determined by how well its state is modeled.&lt;/p&gt;

&lt;p&gt;Changing storage layouts after deployment is difficult, especially once external integrations exist.&lt;/p&gt;

&lt;p&gt;Before writing a single function, we should understand exactly what information our registry needs to remember.&lt;/p&gt;

&lt;p&gt;An OTC offer answers a simple question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is one party willing to trade?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounds simple, but the protocol needs considerably more information than just two token addresses.&lt;/p&gt;

&lt;p&gt;Our registry must remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who created the offer,&lt;/li&gt;
&lt;li&gt;which asset they're selling,&lt;/li&gt;
&lt;li&gt;which asset they expect,&lt;/li&gt;
&lt;li&gt;the amount offered,&lt;/li&gt;
&lt;li&gt;the minimum amount they'll accept,&lt;/li&gt;
&lt;li&gt;when the offer expires,&lt;/li&gt;
&lt;li&gt;whether it's still active,&lt;/li&gt;
&lt;li&gt;and whether somebody has already accepted it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Representing these pieces of information explicitly makes later contract logic much easier to understand.&lt;/p&gt;




&lt;h1&gt;
  
  
  Creating the Offer Model
&lt;/h1&gt;

&lt;p&gt;Inside &lt;code&gt;src/&lt;/code&gt;, create a file named &lt;code&gt;OTCRegistry.sol&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We'll start with the data model before implementing any business logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract OTCRegistry {

    enum OfferStatus {
        Open,
        Accepted,
        Cancelled,
        Settled,
        Expired
    }

    struct Offer {

        address maker;

        address tokenOffered;

        address tokenRequested;

        uint256 amountOffered;

        uint256 minimumAmountRequested;

        uint256 expiry;

        uint256 nonce;

        OfferStatus status;

    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this contract contains only a few lines of Solidity, there's already quite a bit happening.&lt;/p&gt;

&lt;p&gt;Instead of storing offers as loosely connected mappings, we've grouped related information into a single structure.&lt;/p&gt;

&lt;p&gt;This makes the protocol easier to reason about because every offer becomes a self-contained object.&lt;/p&gt;

&lt;p&gt;Let's examine each field.&lt;/p&gt;




&lt;h2&gt;
  
  
  Maker
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address maker;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;maker&lt;/code&gt; is the wallet that created the offer.&lt;/p&gt;

&lt;p&gt;This address becomes important throughout the protocol.&lt;/p&gt;

&lt;p&gt;Only the maker can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cancel an open offer,&lt;/li&gt;
&lt;li&gt;update certain parameters (before acceptance),&lt;/li&gt;
&lt;li&gt;or withdraw escrow if settlement fails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than checking ownership through complex access-control mechanisms, we'll simply compare &lt;code&gt;msg.sender&lt;/code&gt; against this stored address.&lt;/p&gt;




&lt;h2&gt;
  
  
  Offered Token
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address tokenOffered;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This represents the asset currently owned by the maker.&lt;/p&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The registry itself won't hold these tokens.&lt;/p&gt;

&lt;p&gt;Instead, it records what the maker intends to escrow later in the protocol.&lt;/p&gt;

&lt;p&gt;Keeping negotiation separate from asset custody makes the protocol significantly easier to audit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requested Token
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address tokenRequested;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the asset the maker expects in return.&lt;/p&gt;

&lt;p&gt;Together,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tokenOffered

↓

tokenRequested
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;define the trading pair.&lt;/p&gt;

&lt;p&gt;Unlike AMMs, we're not computing prices algorithmically.&lt;/p&gt;

&lt;p&gt;The maker decides exactly what exchange they're willing to accept.&lt;/p&gt;




&lt;h2&gt;
  
  
  Offered Amount
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 amountOffered;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This field specifies how many units of the offered token the maker wishes to trade.&lt;/p&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;500,000 USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The protocol intentionally avoids floating-point arithmetic.&lt;/p&gt;

&lt;p&gt;Every amount is stored using the token's smallest denomination.&lt;/p&gt;

&lt;p&gt;For ERC-20 tokens, that typically means accounting for decimals within the frontend or SDK.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimum Requested Amount
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 minimumAmountRequested;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This field acts as protection against unfavorable execution.&lt;/p&gt;

&lt;p&gt;Suppose Alice wants to exchange&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;500,000 USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for at least&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;165 ETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a counterparty proposes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;160 ETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the protocol should reject the trade automatically.&lt;/p&gt;

&lt;p&gt;Encoding these constraints directly into the offer ensures every accepted trade satisfies the maker's original conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Expiry
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 expiry;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No financial offer should remain valid forever.&lt;/p&gt;

&lt;p&gt;Market conditions change.&lt;/p&gt;

&lt;p&gt;Token prices fluctuate.&lt;/p&gt;

&lt;p&gt;Liquidity disappears.&lt;/p&gt;

&lt;p&gt;Rather than relying on users to manually cancel stale offers, we'll associate every offer with an expiration timestamp.&lt;/p&gt;

&lt;p&gt;Any interaction after that timestamp becomes invalid.&lt;/p&gt;

&lt;p&gt;This single field eliminates an entire class of replay attacks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nonce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 nonce;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One subtle problem appears once signatures enter the picture.&lt;/p&gt;

&lt;p&gt;Suppose Alice signs an offer today.&lt;/p&gt;

&lt;p&gt;What's stopping somebody from submitting the exact same signed payload next month?&lt;/p&gt;

&lt;p&gt;The answer is nonces.&lt;/p&gt;

&lt;p&gt;Each new offer consumes a unique value.&lt;/p&gt;

&lt;p&gt;Once that value has been used, the protocol refuses to accept another offer carrying the same nonce.&lt;/p&gt;

&lt;p&gt;We'll implement this protection shortly.&lt;/p&gt;




&lt;h1&gt;
  
  
  Organizing Storage
&lt;/h1&gt;

&lt;p&gt;With the data model complete, we can decide how offers will actually be stored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(uint256 =&amp;gt; Offer) private offers;

uint256 public offerCounter;

mapping(address =&amp;gt; uint256) public nonces;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each mapping serves a different purpose.&lt;/p&gt;

&lt;p&gt;The first stores every offer ever created.&lt;/p&gt;

&lt;p&gt;The second generates unique identifiers.&lt;/p&gt;

&lt;p&gt;Finally, the third keeps track of each user's latest nonce.&lt;/p&gt;

&lt;p&gt;Separating these concerns keeps lookups straightforward while avoiding nested mappings that quickly become difficult to maintain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Sequential IDs?
&lt;/h1&gt;

&lt;p&gt;Earlier we discussed deterministic hashes.&lt;/p&gt;

&lt;p&gt;Why switch to numeric identifiers?&lt;/p&gt;

&lt;p&gt;Because this protocol has different requirements.&lt;/p&gt;

&lt;p&gt;Unlike signed intents that may exist off-chain before submission, OTC offers originate inside the protocol itself.&lt;/p&gt;

&lt;p&gt;Sequential identifiers offer several practical advantages.&lt;/p&gt;

&lt;p&gt;They make indexing simpler.&lt;/p&gt;

&lt;p&gt;They're cheaper to generate than hashes.&lt;/p&gt;

&lt;p&gt;And they're considerably easier to reference from frontends and block explorers.&lt;/p&gt;

&lt;p&gt;Offer #421 is much easier for developers to work with than&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0xb7e13b8ecb5e...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, we'll still use cryptographic hashes later for signatures.&lt;/p&gt;

&lt;p&gt;But externally, sequential identifiers make the protocol much friendlier to use.&lt;/p&gt;




&lt;h1&gt;
  
  
  Creating Offers
&lt;/h1&gt;

&lt;p&gt;With storage in place, we can finally implement our first state-changing function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function createOffer(

    address tokenOffered,

    address tokenRequested,

    uint256 amountOffered,

    uint256 minimumAmountRequested,

    uint256 expiry

) external returns (uint256 offerId) {

    require(expiry &amp;gt; block.timestamp, "Offer already expired");

    offerId = ++offerCounter;

    offers[offerId] = Offer({

        maker: msg.sender,

        tokenOffered: tokenOffered,

        tokenRequested: tokenRequested,

        amountOffered: amountOffered,

        minimumAmountRequested: minimumAmountRequested,

        expiry: expiry,

        nonce: nonces[msg.sender]++,

        status: OfferStatus.Open

    });

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although concise, this function already demonstrates several useful design principles.&lt;/p&gt;

&lt;p&gt;First, validation occurs before any state modification.&lt;/p&gt;

&lt;p&gt;Second, every newly created offer immediately receives a unique identifier.&lt;/p&gt;

&lt;p&gt;Finally, we increment the maker's nonce at creation time, ensuring every offer remains uniquely identifiable even if all other parameters are identical.&lt;/p&gt;

&lt;p&gt;In the next section we'll improve this function by replacing &lt;code&gt;require&lt;/code&gt; strings with &lt;strong&gt;custom errors&lt;/strong&gt;, adding &lt;strong&gt;events&lt;/strong&gt; for off-chain indexing, and integrating &lt;strong&gt;EIP-712 signatures&lt;/strong&gt; so offers can be created through signed messages rather than direct transactions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding Production-Grade Solidity Patterns
&lt;/h1&gt;

&lt;p&gt;The first version of &lt;code&gt;OTCRegistry.sol&lt;/code&gt; works, but it still looks closer to a prototype than production infrastructure.&lt;/p&gt;

&lt;p&gt;Before moving deeper into confidential execution and cross-chain settlement, we need to improve the contract foundations.&lt;/p&gt;

&lt;p&gt;Financial protocols are rarely compromised because the developer forgot how to write a function.&lt;/p&gt;

&lt;p&gt;They fail because small design decisions accumulate.&lt;/p&gt;

&lt;p&gt;Gas inefficiencies become expensive at scale.&lt;/p&gt;

&lt;p&gt;Missing events break indexing systems.&lt;/p&gt;

&lt;p&gt;Weak signature handling creates replay vulnerabilities.&lt;/p&gt;

&lt;p&gt;Poorly defined state transitions make auditing significantly harder.&lt;/p&gt;

&lt;p&gt;The next iteration of the registry focuses on those details.&lt;/p&gt;




&lt;h1&gt;
  
  
  Replacing require Strings with Custom Errors
&lt;/h1&gt;

&lt;p&gt;The first improvement is replacing traditional revert messages.&lt;/p&gt;

&lt;p&gt;A common Solidity pattern looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require(
    expiry &amp;gt; block.timestamp,
    "Offer already expired"
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but it is unnecessarily expensive.&lt;/p&gt;

&lt;p&gt;Every revert string must be stored inside the contract bytecode.&lt;/p&gt;

&lt;p&gt;For frequently triggered errors, this increases deployment size and runtime gas costs.&lt;/p&gt;

&lt;p&gt;Modern Solidity provides custom errors.&lt;/p&gt;

&lt;p&gt;Instead of storing text, we define an error selector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error OfferExpired();

error InvalidToken();

error InvalidAmount();

error Unauthorized();

error OfferNotOpen();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now validation becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (expiry &amp;lt;= block.timestamp) {
    revert OfferExpired();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference may seem small.&lt;/p&gt;

&lt;p&gt;For a single transaction, the savings are minimal.&lt;/p&gt;

&lt;p&gt;For a protocol processing thousands of interactions, these optimizations compound.&lt;/p&gt;

&lt;p&gt;More importantly, custom errors create structured failure conditions.&lt;/p&gt;

&lt;p&gt;Frontend applications can identify the exact failure type instead of parsing human-readable strings.&lt;/p&gt;




&lt;h1&gt;
  
  
  Updating the Registry Contract
&lt;/h1&gt;

&lt;p&gt;Our contract now begins with clearer protocol-level failures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract OTCRegistry {

    error OfferExpired();
    error InvalidAmount();
    error Unauthorized();
    error OfferNotOpen();

    enum OfferStatus {
        Open,
        Accepted,
        Cancelled,
        Settled,
        Expired
    }

    struct Offer {

        address maker;

        address tokenOffered;

        address tokenRequested;

        uint256 amountOffered;

        uint256 minimumAmountRequested;

        uint256 expiry;

        uint256 nonce;

        OfferStatus status;

    }

    mapping(uint256 =&amp;gt; Offer) private offers;

    uint256 public offerCounter;

    mapping(address =&amp;gt; uint256) public nonces;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The storage layout remains unchanged.&lt;/p&gt;

&lt;p&gt;This is intentional.&lt;/p&gt;

&lt;p&gt;Changing storage unnecessarily creates migration complexity.&lt;/p&gt;

&lt;p&gt;A well-designed contract evolves by adding capabilities around stable foundations.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Events Matter
&lt;/h1&gt;

&lt;p&gt;Smart contracts do not push information to applications.&lt;/p&gt;

&lt;p&gt;They simply modify state.&lt;/p&gt;

&lt;p&gt;If a frontend wants to display active OTC offers, it needs an efficient way to discover changes.&lt;/p&gt;

&lt;p&gt;Reading every offer ID and querying storage directly does not scale.&lt;/p&gt;

&lt;p&gt;Events solve this problem.&lt;/p&gt;

&lt;p&gt;When emitted, events become part of transaction logs that external systems can index.&lt;/p&gt;

&lt;p&gt;Popular indexing systems such as The Graph and custom backend services rely heavily on this pattern.&lt;/p&gt;

&lt;p&gt;For our registry, we need events for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;offer creation,&lt;/li&gt;
&lt;li&gt;offer acceptance,&lt;/li&gt;
&lt;li&gt;cancellation,&lt;/li&gt;
&lt;li&gt;settlement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's add the first one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event OfferCreated(
    uint256 indexed offerId,
    address indexed maker,
    address tokenOffered,
    address tokenRequested,
    uint256 amountOffered,
    uint256 minimumAmountRequested,
    uint256 expiry
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;indexed&lt;/code&gt; keyword is important.&lt;/p&gt;

&lt;p&gt;Indexed fields become searchable topics.&lt;/p&gt;

&lt;p&gt;For example, a frontend can efficiently query:&lt;/p&gt;

&lt;p&gt;"Show me every offer created by this address."&lt;/p&gt;

&lt;p&gt;without scanning the entire blockchain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Emitting Offer Creation Events
&lt;/h1&gt;

&lt;p&gt;Now we update our creation function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function createOffer(

    address tokenOffered,

    address tokenRequested,

    uint256 amountOffered,

    uint256 minimumAmountRequested,

    uint256 expiry

) external returns (uint256 offerId) {


    if (expiry &amp;lt;= block.timestamp) {
        revert OfferExpired();
    }


    if (
        amountOffered == 0 ||
        minimumAmountRequested == 0
    ) {
        revert InvalidAmount();
    }


    offerId = ++offerCounter;


    offers[offerId] = Offer({

        maker: msg.sender,

        tokenOffered: tokenOffered,

        tokenRequested: tokenRequested,

        amountOffered: amountOffered,

        minimumAmountRequested: minimumAmountRequested,

        expiry: expiry,

        nonce: nonces[msg.sender]++,

        status: OfferStatus.Open

    });


    emit OfferCreated(
        offerId,
        msg.sender,
        tokenOffered,
        tokenRequested,
        amountOffered,
        minimumAmountRequested,
        expiry
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function now has three clear responsibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate the offer.&lt;/li&gt;
&lt;li&gt;Store protocol state.&lt;/li&gt;
&lt;li&gt;Notify external systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping those responsibilities separated makes later auditing much easier.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introducing EIP-712 Typed Data
&lt;/h1&gt;

&lt;p&gt;Direct transactions are not always ideal for OTC workflows.&lt;/p&gt;

&lt;p&gt;Institutional traders frequently negotiate away from the blockchain.&lt;/p&gt;

&lt;p&gt;A market maker might send a quote through a private channel.&lt;/p&gt;

&lt;p&gt;A treasury manager might approve a trade through an internal signing system.&lt;/p&gt;

&lt;p&gt;Broadcasting every negotiation step on-chain defeats the purpose of confidentiality.&lt;/p&gt;

&lt;p&gt;Instead, users should be able to sign trade intents.&lt;/p&gt;

&lt;p&gt;The blockchain only verifies the final agreement.&lt;/p&gt;

&lt;p&gt;This is where EIP-712 becomes important.&lt;/p&gt;

&lt;p&gt;EIP-712 defines a standard format for signing structured data.&lt;/p&gt;

&lt;p&gt;Instead of signing an unreadable blob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x72af91bc...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;users sign something understandable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sell:

500000 USDC

Receive:

165 ETH

Expiry:

July 20 2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wallets can display these fields clearly before approval.&lt;/p&gt;




&lt;h1&gt;
  
  
  Designing the Signed Offer
&lt;/h1&gt;

&lt;p&gt;Our signed message needs a deterministic structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct OfferMessage {

    address maker;

    address tokenOffered;

    address tokenRequested;

    uint256 amountOffered;

    uint256 minimumAmountRequested;

    uint256 expiry;

    uint256 nonce;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every field matters.&lt;/p&gt;

&lt;p&gt;Removing a field creates ambiguity.&lt;/p&gt;

&lt;p&gt;For example, if &lt;code&gt;expiry&lt;/code&gt; is not included, an attacker could reuse a valid signature after market conditions changed.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;nonce&lt;/code&gt; is missing, replay attacks become possible.&lt;/p&gt;

&lt;p&gt;Cryptographic signatures are only as strong as the data they protect.&lt;/p&gt;




&lt;h1&gt;
  
  
  Adding OpenZeppelin EIP712
&lt;/h1&gt;

&lt;p&gt;Rather than implementing hashing manually, we'll use OpenZeppelin's audited implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
    EIP712
} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";


import {
    ECDSA
} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our contract now inherits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract OTCRegistry is EIP712 {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The constructor initializes the signing domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor()
    EIP712(
        "Confidential OTC Protocol",
        "1"
    )
{}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The domain separates signatures from other applications.&lt;/p&gt;

&lt;p&gt;A signature created for our OTC protocol cannot accidentally be reused inside another application with different rules.&lt;/p&gt;




&lt;h1&gt;
  
  
  Hashing the Offer
&lt;/h1&gt;

&lt;p&gt;EIP-712 requires two stages.&lt;/p&gt;

&lt;p&gt;First, we hash the structured message.&lt;/p&gt;

&lt;p&gt;Then we hash that result with the domain separator.&lt;/p&gt;

&lt;p&gt;The final digest is what gets signed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bytes32 private constant OFFER_TYPEHASH =
keccak256(
    "OfferMessage(address maker,address tokenOffered,address tokenRequested,uint256 amountOffered,uint256 minimumAmountRequested,uint256 expiry,uint256 nonce)"
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type hash describes the exact structure.&lt;/p&gt;

&lt;p&gt;Any modification changes the resulting hash.&lt;/p&gt;

&lt;p&gt;This prevents attackers from altering individual fields without invalidating the signature.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Matters for Confidential OTC
&lt;/h1&gt;

&lt;p&gt;At this point, we have an important architectural improvement.&lt;/p&gt;

&lt;p&gt;The negotiation process no longer requires every participant to submit transactions.&lt;/p&gt;

&lt;p&gt;A trader can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an offer locally.&lt;/li&gt;
&lt;li&gt;Sign it.&lt;/li&gt;
&lt;li&gt;Share it privately.&lt;/li&gt;
&lt;li&gt;Receive counterparty acceptance.&lt;/li&gt;
&lt;li&gt;Submit only the final agreed trade.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This changes the privacy model dramatically.&lt;/p&gt;

&lt;p&gt;The blockchain no longer observes every negotiation step.&lt;/p&gt;

&lt;p&gt;It only verifies the final commitment.&lt;/p&gt;

&lt;p&gt;This pattern is common in modern trading infrastructure.&lt;/p&gt;

&lt;p&gt;The chain becomes a settlement layer rather than a communication layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Connecting This Back to Oasis Sapphire
&lt;/h1&gt;

&lt;p&gt;This is where Oasis Sapphire becomes useful.&lt;/p&gt;

&lt;p&gt;Sapphire allows the negotiation layer itself to become confidential while remaining EVM-compatible.&lt;/p&gt;

&lt;p&gt;Instead of exposing signed offers and quotes publicly, sensitive state can remain inside confidential contract execution.&lt;/p&gt;

&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR

TraderA --&amp;gt; Sapphire

TraderB --&amp;gt; Sapphire

Sapphire --&amp;gt; SignedSettlement

SignedSettlement --&amp;gt; Base

Base --&amp;gt; FinalSettlement

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sapphire handles the private coordination.&lt;/p&gt;

&lt;p&gt;Base provides transparent final settlement.&lt;/p&gt;

&lt;p&gt;Neither chain is replacing the other.&lt;/p&gt;

&lt;p&gt;Each handles the part it is best suited for.&lt;/p&gt;




&lt;h1&gt;
  
  
  Next Section
&lt;/h1&gt;

&lt;p&gt;The registry now has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structured storage,&lt;/li&gt;
&lt;li&gt;custom errors,&lt;/li&gt;
&lt;li&gt;event indexing,&lt;/li&gt;
&lt;li&gt;typed signatures,&lt;/li&gt;
&lt;li&gt;replay protection foundations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step is implementing the acceptance flow.&lt;/p&gt;

&lt;p&gt;We will build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;counterparty quote submission,&lt;/li&gt;
&lt;li&gt;signature verification,&lt;/li&gt;
&lt;li&gt;offer acceptance,&lt;/li&gt;
&lt;li&gt;escrow state transitions,&lt;/li&gt;
&lt;li&gt;settlement preparation,&lt;/li&gt;
&lt;li&gt;and the bridge between confidential negotiation and public Base settlement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Building the Acceptance Flow
&lt;/h1&gt;

&lt;p&gt;The registry can now create offers and verify signed trade intents. However, an OTC protocol is not complete when an offer exists.&lt;/p&gt;

&lt;p&gt;An offer represents only one side of the agreement.&lt;/p&gt;

&lt;p&gt;The critical moment happens when another participant decides:&lt;/p&gt;

&lt;p&gt;"I accept these terms, and I am willing to proceed toward settlement."&lt;/p&gt;

&lt;p&gt;This transition is where many financial protocols become complicated.&lt;/p&gt;

&lt;p&gt;A naive implementation might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function acceptOffer(uint256 offerId)
    external
{
    offers[offerId].status = OfferStatus.Accepted;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This appears simple, but it introduces several problems.&lt;/p&gt;

&lt;p&gt;Who is accepting?&lt;/p&gt;

&lt;p&gt;Can anyone accept?&lt;/p&gt;

&lt;p&gt;Was the offer already cancelled?&lt;/p&gt;

&lt;p&gt;Has the offer expired?&lt;/p&gt;

&lt;p&gt;Did the counterparty actually provide the requested asset?&lt;/p&gt;

&lt;p&gt;Can the same offer be accepted twice?&lt;/p&gt;

&lt;p&gt;In financial infrastructure, changing a single enum value is not the operation. The operation is a state transition with strict conditions.&lt;/p&gt;

&lt;p&gt;Our goal is to make every transition explicit.&lt;/p&gt;




&lt;h1&gt;
  
  
  Designing the Acceptance Lifecycle
&lt;/h1&gt;

&lt;p&gt;Before writing code, let's define what acceptance actually means.&lt;/p&gt;

&lt;p&gt;The lifecycle currently looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open
 |
 |
 v
Accepted
 |
 |
 v
Escrowed
 |
 |
 v
Settled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also failure paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open
 |
 +----&amp;gt; Cancelled
 |
 +----&amp;gt; Expired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important observation is that acceptance is not settlement.&lt;/p&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;A counterparty agreeing to trade does not mean assets have moved.&lt;/p&gt;

&lt;p&gt;The protocol must first verify:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The offer still exists.&lt;/li&gt;
&lt;li&gt;The offer is still open.&lt;/li&gt;
&lt;li&gt;The acceptance satisfies the original conditions.&lt;/li&gt;
&lt;li&gt;Both parties can proceed to escrow.&lt;/li&gt;
&lt;li&gt;Settlement can happen deterministically later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Separating these phases prevents a common class of bugs where negotiation state and asset movement become tightly coupled.&lt;/p&gt;




&lt;h1&gt;
  
  
  Reading Offers Safely
&lt;/h1&gt;

&lt;p&gt;Before adding acceptance logic, we need a way for external applications to inspect offers.&lt;/p&gt;

&lt;p&gt;Currently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(uint256 =&amp;gt; Offer) private offers;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The storage is private.&lt;/p&gt;

&lt;p&gt;This is intentional.&lt;/p&gt;

&lt;p&gt;In a confidential environment, exposing raw storage defeats the purpose of keeping negotiation details protected.&lt;/p&gt;

&lt;p&gt;However, applications still need controlled access.&lt;/p&gt;

&lt;p&gt;We can create a view function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getOffer(uint256 offerId)
    external
    view
    returns (Offer memory)
{
    return offers[offerId];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for a public chain.&lt;/p&gt;

&lt;p&gt;However, because our final architecture uses Oasis Sapphire for confidential negotiation, this function becomes an important design decision.&lt;/p&gt;

&lt;p&gt;On Sapphire, the contract can expose information selectively.&lt;/p&gt;

&lt;p&gt;The difference is subtle but important:&lt;/p&gt;

&lt;p&gt;A public Ethereum contract assumes:&lt;/p&gt;

&lt;p&gt;"Everything is visible unless encrypted externally."&lt;/p&gt;

&lt;p&gt;A confidential execution environment assumes:&lt;/p&gt;

&lt;p&gt;"The contract decides what information leaves the private state."&lt;/p&gt;

&lt;p&gt;The same Solidity pattern behaves differently depending on where it executes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Validating State Transitions
&lt;/h1&gt;

&lt;p&gt;Let's add a helper function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _requireOpenOffer(
    Offer storage offer
)
    internal
    view
{
    if (offer.status != OfferStatus.Open) {
        revert OfferNotOpen();
    }

    if (block.timestamp &amp;gt; offer.expiry) {
        revert OfferExpired();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function centralizes the rules for an active offer.&lt;/p&gt;

&lt;p&gt;Why not repeat these checks everywhere?&lt;/p&gt;

&lt;p&gt;Because duplicated state validation creates drift.&lt;/p&gt;

&lt;p&gt;Imagine one function checks expiry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;block.timestamp &amp;gt;= expiry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while another checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;block.timestamp &amp;gt; expiry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one-character difference creates inconsistent behavior.&lt;/p&gt;

&lt;p&gt;Financial protocols should have one definition of validity.&lt;/p&gt;




&lt;h1&gt;
  
  
  Accepting an Offer
&lt;/h1&gt;

&lt;p&gt;Now we can implement the acceptance transition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function acceptOffer(
    uint256 offerId
)
    external
{
    Offer storage offer = offers[offerId];

    _requireOpenOffer(offer);

    offer.status = OfferStatus.Accepted;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function is intentionally simple.&lt;/p&gt;

&lt;p&gt;The complexity comes from the guarantees around it.&lt;/p&gt;

&lt;p&gt;Before changing state, the contract proves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the offer exists,&lt;/li&gt;
&lt;li&gt;the offer is active,&lt;/li&gt;
&lt;li&gt;the offer has not expired.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only then does the transition occur.&lt;/p&gt;




&lt;h1&gt;
  
  
  But Who Can Accept?
&lt;/h1&gt;

&lt;p&gt;This is the first major design decision.&lt;/p&gt;

&lt;p&gt;There are several possible models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Acceptance
&lt;/h2&gt;

&lt;p&gt;Anyone can accept an offer.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;Alice creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sell:
500,000 USDC

Receive:
165 ETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any participant can accept.&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple,&lt;/li&gt;
&lt;li&gt;permissionless,&lt;/li&gt;
&lt;li&gt;similar to public exchange orders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;poor fit for institutional OTC,&lt;/li&gt;
&lt;li&gt;no counterparty negotiation,&lt;/li&gt;
&lt;li&gt;creates competition around acceptance.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Whitelisted Counterparties
&lt;/h2&gt;

&lt;p&gt;The maker specifies who can accept.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maker:
DAO Treasury

Allowed Counterparty:
Market Maker X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;closer to institutional OTC,&lt;/li&gt;
&lt;li&gt;predictable counterparties.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;requires identity management.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Signed Acceptance
&lt;/h2&gt;

&lt;p&gt;The most flexible approach is requiring both sides to sign the agreement.&lt;/p&gt;

&lt;p&gt;The final transaction contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maker Signature

+

Counterparty Signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract verifies both signatures before allowing settlement.&lt;/p&gt;

&lt;p&gt;This is the approach we will move toward.&lt;/p&gt;




&lt;h1&gt;
  
  
  Adding Counterparty Information
&lt;/h1&gt;

&lt;p&gt;Our current Offer struct only knows the maker.&lt;/p&gt;

&lt;p&gt;For bilateral OTC trading, we need another participant.&lt;/p&gt;

&lt;p&gt;Update the structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Offer {

    address maker;

    address taker;

    address tokenOffered;

    address tokenRequested;

    uint256 amountOffered;

    uint256 minimumAmountRequested;

    uint256 expiry;

    uint256 nonce;

    OfferStatus status;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address taker;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;represents the counterparty who accepted the trade.&lt;/p&gt;

&lt;p&gt;Before acceptance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;maker = Alice

taker = address(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After acceptance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;maker = Alice

taker = Bob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the protocol an explicit record of both parties.&lt;/p&gt;




&lt;h1&gt;
  
  
  Updating Acceptance
&lt;/h1&gt;

&lt;p&gt;Now acceptance records the counterparty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function acceptOffer(
    uint256 offerId
)
    external
{
    Offer storage offer = offers[offerId];

    _requireOpenOffer(offer);

    offer.taker = msg.sender;

    offer.status = OfferStatus.Accepted;

    emit OfferAccepted(
        offerId,
        msg.sender
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The state transition is now clearer.&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open

maker:
Alice

taker:
none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accepted

maker:
Alice

taker:
Bob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract now knows exactly who negotiated the trade.&lt;/p&gt;




&lt;h1&gt;
  
  
  Adding Acceptance Events
&lt;/h1&gt;

&lt;p&gt;Just like offer creation, acceptance needs an event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event OfferAccepted(
    uint256 indexed offerId,
    address indexed taker
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows external systems to react.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;A backend service can listen for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OfferAccepted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and automatically begin escrow preparation.&lt;/p&gt;

&lt;p&gt;A frontend can update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status:
Accepted
Waiting for escrow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Events become the communication layer between blockchain state and applications.&lt;/p&gt;




&lt;h1&gt;
  
  
  Preventing Double Acceptance
&lt;/h1&gt;

&lt;p&gt;Consider this sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction 1:
Bob accepts offer #10

Transaction 2:
Charlie accepts offer #10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only one should succeed.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;_requireOpenOffer&lt;/code&gt; check already prevents this.&lt;/p&gt;

&lt;p&gt;After Bob's transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;offer.status = OfferStatus.Accepted;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second transaction sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OfferStatus.Accepted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and reverts.&lt;/p&gt;

&lt;p&gt;This is why explicit state machines are powerful.&lt;/p&gt;

&lt;p&gt;The contract does not need complicated logic.&lt;/p&gt;

&lt;p&gt;The state itself prevents invalid actions.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Matters for Confidential OTC
&lt;/h1&gt;

&lt;p&gt;At first glance, acceptance looks like a simple update.&lt;/p&gt;

&lt;p&gt;It is not.&lt;/p&gt;

&lt;p&gt;Acceptance is the point where private negotiation becomes a binding agreement.&lt;/p&gt;

&lt;p&gt;In traditional OTC markets, this moment happens after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;price discovery,&lt;/li&gt;
&lt;li&gt;counterparty verification,&lt;/li&gt;
&lt;li&gt;risk checks,&lt;/li&gt;
&lt;li&gt;legal confirmation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On-chain, the smart contract replaces much of that coordination.&lt;/p&gt;

&lt;p&gt;The contract becomes the neutral agreement layer.&lt;/p&gt;

&lt;p&gt;With Sapphire, this process can remain confidential:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trader A
   |
   |
Private Offer
   |
   v

Oasis Sapphire

   |
   |
Accepted Agreement
   |
   v

Base Settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The public chain does not need to observe every negotiation step.&lt;/p&gt;

&lt;p&gt;It only needs proof that settlement conditions were satisfied.&lt;/p&gt;




&lt;h1&gt;
  
  
  Preparing for Escrow
&lt;/h1&gt;

&lt;p&gt;Acceptance alone is still not enough.&lt;/p&gt;

&lt;p&gt;A malicious counterparty could accept an offer and then disappear.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;Alice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Locks:
500,000 USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accepts trade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then Bob never provides ETH.&lt;/p&gt;

&lt;p&gt;The protocol must solve this problem.&lt;/p&gt;

&lt;p&gt;The next stage introduces escrow.&lt;/p&gt;

&lt;p&gt;We will build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ERC-20 token transfers,&lt;/li&gt;
&lt;li&gt;secure custody,&lt;/li&gt;
&lt;li&gt;allowance handling,&lt;/li&gt;
&lt;li&gt;escrow state tracking,&lt;/li&gt;
&lt;li&gt;settlement authorization,&lt;/li&gt;
&lt;li&gt;withdrawal rules,&lt;/li&gt;
&lt;li&gt;failure recovery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the protocol starts becoming an actual OTC settlement system rather than a negotiation registry.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the Escrow Layer
&lt;/h1&gt;

&lt;p&gt;The acceptance flow gives us something important.&lt;/p&gt;

&lt;p&gt;We now have agreement.&lt;/p&gt;

&lt;p&gt;But agreement is not settlement.&lt;/p&gt;

&lt;p&gt;This distinction is fundamental in financial protocols.&lt;/p&gt;

&lt;p&gt;A traditional OTC desk does not send millions of dollars simply because two traders verbally agree on a price. There is usually a custody layer, a clearing process, or a settlement mechanism that guarantees both parties perform.&lt;/p&gt;

&lt;p&gt;Smart contracts need the same separation.&lt;/p&gt;

&lt;p&gt;The registry answers:&lt;/p&gt;

&lt;p&gt;"Who agreed to trade?"&lt;/p&gt;

&lt;p&gt;The escrow contract answers:&lt;/p&gt;

&lt;p&gt;"Are the assets actually secured?"&lt;/p&gt;

&lt;p&gt;Only after both questions have a valid answer should settlement begin.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Escrow Exists
&lt;/h1&gt;

&lt;p&gt;Consider a simple failure scenario.&lt;/p&gt;

&lt;p&gt;Alice creates an offer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sell:
500,000 USDC

Receive:
165 ETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bob accepts.&lt;/p&gt;

&lt;p&gt;The registry now contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = Accepted;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But nothing forces Bob to provide ETH.&lt;/p&gt;

&lt;p&gt;The protocol has created an agreement without enforcement.&lt;/p&gt;

&lt;p&gt;This is the difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Negotiation Layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settlement Layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The escrow contract connects them.&lt;/p&gt;




&lt;h1&gt;
  
  
  Separating Escrow From the Registry
&lt;/h1&gt;

&lt;p&gt;A common beginner design is putting everything into one contract.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract OTCProtocol {

    createOffer()

    acceptOffer()

    deposit()

    settle()

    cancel()

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks convenient.&lt;/p&gt;

&lt;p&gt;However, it creates several problems.&lt;/p&gt;

&lt;p&gt;The contract now handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;offer management,&lt;/li&gt;
&lt;li&gt;token custody,&lt;/li&gt;
&lt;li&gt;settlement,&lt;/li&gt;
&lt;li&gt;permissions,&lt;/li&gt;
&lt;li&gt;refunds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every additional responsibility increases audit complexity.&lt;/p&gt;

&lt;p&gt;Instead, we separate concerns.&lt;/p&gt;

&lt;p&gt;Our architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 OTCRegistry

              "What was agreed?"
                    |
                    |
                    v

                  Escrow

              "Are assets locked?"
                    |
                    |
                    v

        SettlementCoordinator

              "Execute final trade"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each contract has one job.&lt;/p&gt;




&lt;h1&gt;
  
  
  Creating Escrow.sol
&lt;/h1&gt;

&lt;p&gt;Inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/Escrow.sol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we create our custody contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {
    IERC20
} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";


contract Escrow {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first dependency is ERC-20 support.&lt;/p&gt;

&lt;p&gt;The escrow contract does not care whether the token is USDC, WETH, or another asset.&lt;/p&gt;

&lt;p&gt;It only needs the standard interface.&lt;/p&gt;




&lt;h1&gt;
  
  
  Modeling Escrow Positions
&lt;/h1&gt;

&lt;p&gt;An escrow position represents one side of a trade.&lt;/p&gt;

&lt;p&gt;We need to store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who deposited,&lt;/li&gt;
&lt;li&gt;which token,&lt;/li&gt;
&lt;li&gt;how much,&lt;/li&gt;
&lt;li&gt;which offer it belongs to,&lt;/li&gt;
&lt;li&gt;current state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's define it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract Escrow {

    enum EscrowStatus {
        Pending,
        Deposited,
        Released,
        Refunded
    }


    struct Deposit {

        address depositor;

        address token;

        uint256 amount;

        uint256 offerId;

        EscrowStatus status;

    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure is intentionally small.&lt;/p&gt;

&lt;p&gt;Escrow contracts should avoid unnecessary state.&lt;/p&gt;

&lt;p&gt;Every stored variable creates another invariant that must remain correct forever.&lt;/p&gt;




&lt;h1&gt;
  
  
  Tracking Deposits
&lt;/h1&gt;

&lt;p&gt;Now we need storage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(uint256 =&amp;gt; Deposit) public deposits;

uint256 public depositCounter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each deposit receives an identifier.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deposit #42

Offer:
#100

Depositor:
Alice

Token:
USDC

Amount:
500000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This identifier becomes useful during settlement.&lt;/p&gt;




&lt;h1&gt;
  
  
  Depositing Assets
&lt;/h1&gt;

&lt;p&gt;Now we can implement the first important function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function deposit(
    uint256 offerId,
    address token,
    uint256 amount
)
    external
    returns (uint256 depositId)
{

    require(
        amount &amp;gt; 0,
        "Invalid amount"
    );


    IERC20(token).transferFrom(
        msg.sender,
        address(this),
        amount
    );


    depositId = ++depositCounter;


    deposits[depositId] = Deposit({

        depositor: msg.sender,

        token: token,

        amount: amount,

        offerId: offerId,

        status: EscrowStatus.Deposited

    });

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User approves the escrow contract.&lt;/li&gt;
&lt;li&gt;Escrow pulls tokens using &lt;code&gt;transferFrom&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A deposit record is created.&lt;/li&gt;
&lt;li&gt;The protocol now knows assets are secured.&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Why Approval Comes First
&lt;/h1&gt;

&lt;p&gt;ERC-20 tokens do not allow arbitrary contracts to move funds.&lt;/p&gt;

&lt;p&gt;The user must first approve spending.&lt;/p&gt;

&lt;p&gt;The sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Wallet

approve()

      |

      v

Escrow Contract

transferFrom()

      |

      v

Escrow Balance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is standard ERC-20 behavior.&lt;/p&gt;

&lt;p&gt;For production systems, frontends usually combine these steps into a single user experience.&lt;/p&gt;

&lt;p&gt;The user sees:&lt;/p&gt;

&lt;p&gt;"Deposit 500,000 USDC"&lt;/p&gt;

&lt;p&gt;rather than manually performing approval and transfer operations.&lt;/p&gt;




&lt;h1&gt;
  
  
  Safe Transfer Handling
&lt;/h1&gt;

&lt;p&gt;The previous example uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IERC20(token).transferFrom()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, production contracts should handle tokens that do not properly return boolean values.&lt;/p&gt;

&lt;p&gt;OpenZeppelin provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeERC20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's update the contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
    SafeERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";


contract Escrow {

    using SafeERC20 for IERC20;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now deposits become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IERC20(token).safeTransferFrom(
    msg.sender,
    address(this),
    amount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This protects against non-standard ERC-20 implementations.&lt;/p&gt;




&lt;h1&gt;
  
  
  Adding Events
&lt;/h1&gt;

&lt;p&gt;Like the registry, escrow needs events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event Deposited(
    uint256 indexed depositId,
    uint256 indexed offerId,
    address indexed depositor,
    address token,
    uint256 amount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then after creating the deposit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;emit Deposited(
    depositId,
    offerId,
    msg.sender,
    token,
    amount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;External systems can now monitor escrow activity.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offer Accepted

        |

        v

Waiting for Deposits

        |

        v

Both Sides Funded

        |

        v

Settlement Ready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  The Escrow Invariant
&lt;/h1&gt;

&lt;p&gt;The most important property of this contract is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Escrowed funds
=
Funds controlled by protocol rules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users should never be able to bypass the settlement process.&lt;/p&gt;

&lt;p&gt;After depositing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice
 |
 | 500,000 USDC
 v

Escrow Contract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alice cannot simply withdraw.&lt;/p&gt;

&lt;p&gt;Bob cannot withdraw.&lt;/p&gt;

&lt;p&gt;The only valid paths are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successful Settlement

or

Authorized Refund
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the core security property.&lt;/p&gt;




&lt;h1&gt;
  
  
  Connecting Escrow to Acceptance
&lt;/h1&gt;

&lt;p&gt;Right now the escrow contract does not know whether an offer was actually accepted.&lt;/p&gt;

&lt;p&gt;That is intentional.&lt;/p&gt;

&lt;p&gt;Remember our separation of responsibilities.&lt;/p&gt;

&lt;p&gt;The registry knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offer #100

Maker:
Alice

Taker:
Bob

Status:
Accepted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The escrow knows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deposit #55

Offer:
#100

Amount:
500,000 USDC

Status:
Deposited
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The settlement coordinator will connect these pieces.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introducing the Settlement Coordinator
&lt;/h1&gt;

&lt;p&gt;The final architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Sapphire

              OTCRegistry

        Negotiation + Agreement


                    |

                    v


                  Escrow

          Asset Security Layer


                    |

                    v


        SettlementCoordinator

             Cross-chain Execution


                    |

                    v


                  Base

          Public Settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinator becomes the bridge between confidential negotiation and public execution.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Architecture Fits Sapphire + Base
&lt;/h1&gt;

&lt;p&gt;This separation lets each network do what it does best.&lt;/p&gt;

&lt;p&gt;Sapphire handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;private offers,&lt;/li&gt;
&lt;li&gt;confidential quotes,&lt;/li&gt;
&lt;li&gt;negotiation state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Escrow handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;asset custody,&lt;/li&gt;
&lt;li&gt;enforcement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Base handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transparent settlement,&lt;/li&gt;
&lt;li&gt;final ownership changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The protocol does not attempt to make one chain solve every problem.&lt;/p&gt;

&lt;p&gt;Instead, it assigns responsibilities.&lt;/p&gt;




&lt;h1&gt;
  
  
  Next Section
&lt;/h1&gt;

&lt;p&gt;The escrow layer now provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ERC-20 custody,&lt;/li&gt;
&lt;li&gt;deposit tracking,&lt;/li&gt;
&lt;li&gt;safe token transfers,&lt;/li&gt;
&lt;li&gt;event indexing,&lt;/li&gt;
&lt;li&gt;clear ownership boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next part will implement the final settlement path.&lt;/p&gt;

&lt;p&gt;We will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SettlementCoordinator.sol&lt;/li&gt;
&lt;li&gt;cross-chain settlement design&lt;/li&gt;
&lt;li&gt;proving escrow conditions&lt;/li&gt;
&lt;li&gt;releasing funds safely&lt;/li&gt;
&lt;li&gt;Base integration patterns&lt;/li&gt;
&lt;li&gt;replay protection across chains&lt;/li&gt;
&lt;li&gt;handling failures and cancellations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the complete OTC protocol architecture comes together.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the Settlement Coordinator
&lt;/h1&gt;

&lt;p&gt;At this point, the protocol has three important capabilities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The registry can create and accept agreements.&lt;/li&gt;
&lt;li&gt;The escrow contract can lock assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What remains is the most important operation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moving from a private agreement into final settlement.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where the architecture becomes interesting.&lt;/p&gt;

&lt;p&gt;The protocol intentionally separates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Private negotiation

        |

        v

Verified agreement

        |

        v

Public settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The negotiation does not happen on Base.&lt;/p&gt;

&lt;p&gt;The final ownership transfer does.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Settlement Should Be Separate
&lt;/h1&gt;

&lt;p&gt;A common mistake is allowing the escrow contract to directly perform settlement.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function settle()
external
{
    transferTokens();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This couples custody and execution.&lt;/p&gt;

&lt;p&gt;The escrow contract now needs to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who receives funds,&lt;/li&gt;
&lt;li&gt;which chain settlement happens on,&lt;/li&gt;
&lt;li&gt;whether signatures are valid,&lt;/li&gt;
&lt;li&gt;whether external messages are trusted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That creates unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Instead, escrow only answers:&lt;/p&gt;

&lt;p&gt;"Are the required assets secured?"&lt;/p&gt;

&lt;p&gt;The settlement coordinator answers:&lt;/p&gt;

&lt;p&gt;"Should the trade execute?"&lt;/p&gt;




&lt;h1&gt;
  
  
  Creating SettlementCoordinator.sol
&lt;/h1&gt;

&lt;p&gt;Inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/SettlementCoordinator.sol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;


contract SettlementCoordinator {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinator will eventually communicate with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OTCRegistry

        |

        |

Escrow

        |

        |

Base Settlement Layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Defining Settlement State
&lt;/h1&gt;

&lt;p&gt;A settlement has its own lifecycle.&lt;/p&gt;

&lt;p&gt;It is different from an offer lifecycle.&lt;/p&gt;

&lt;p&gt;The offer lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open

 |

 v

Accepted

 |

 v

Escrowed

 |

 v

Settled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The settlement lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created

 |

 v

Verified

 |

 v

Executed

 |

 v

Completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's model this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enum SettlementStatus {

    Pending,

    Ready,

    Executed,

    Failed

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents ambiguous states.&lt;/p&gt;




&lt;h1&gt;
  
  
  Settlement Records
&lt;/h1&gt;

&lt;p&gt;The coordinator needs to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which offer is being settled,&lt;/li&gt;
&lt;li&gt;which escrow positions are involved,&lt;/li&gt;
&lt;li&gt;who receives what,&lt;/li&gt;
&lt;li&gt;whether execution happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create the structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Settlement {

    uint256 offerId;

    uint256 makerDeposit;

    uint256 takerDeposit;

    address maker;

    address taker;

    SettlementStatus status;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A settlement is not an offer.&lt;/p&gt;

&lt;p&gt;An offer describes intention.&lt;/p&gt;

&lt;p&gt;A settlement describes execution.&lt;/p&gt;

&lt;p&gt;Keeping those separate avoids mixing negotiation state with financial state.&lt;/p&gt;




&lt;h1&gt;
  
  
  Storing Settlements
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(uint256 =&amp;gt; Settlement)
public settlements;


uint256 public settlementCounter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settlement #7

Offer:
#421


Maker:
Alice


Taker:
Bob


Maker Deposit:
USDC


Taker Deposit:
ETH


Status:
Ready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Creating a Settlement
&lt;/h1&gt;

&lt;p&gt;Once both sides have escrowed funds, we create a settlement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function createSettlement(
    uint256 offerId,
    uint256 makerDeposit,
    uint256 takerDeposit,
    address maker,
    address taker
)
external
returns(uint256 settlementId)
{

    settlementId = ++settlementCounter;


    settlements[settlementId] =
        Settlement({

            offerId: offerId,

            makerDeposit: makerDeposit,

            takerDeposit: takerDeposit,

            maker: maker,

            taker: taker,

            status: SettlementStatus.Pending

        });

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function does not move funds.&lt;/p&gt;

&lt;p&gt;That is intentional.&lt;/p&gt;

&lt;p&gt;Settlement creation is a coordination step.&lt;/p&gt;




&lt;h1&gt;
  
  
  Verifying Escrow Conditions
&lt;/h1&gt;

&lt;p&gt;Before execution, we need proof that both sides funded the trade.&lt;/p&gt;

&lt;p&gt;The coordinator should never trust user input.&lt;/p&gt;

&lt;p&gt;A malicious user could attempt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;makerDeposit = fakeDepositId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinator must verify directly with escrow.&lt;/p&gt;

&lt;p&gt;The pattern becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Escrow

"Is deposit X valid?"

        |

        v

SettlementCoordinator

"Proceed or reject"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Escrow Interface
&lt;/h1&gt;

&lt;p&gt;Instead of importing the entire escrow contract, define a small interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface IEscrow {

    function getDeposit(
        uint256 depositId
    )
    external
    view
    returns(
        address depositor,
        address token,
        uint256 amount,
        uint256 offerId,
        uint8 status
    );

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinator only knows what it needs.&lt;/p&gt;

&lt;p&gt;This reduces coupling.&lt;/p&gt;




&lt;h1&gt;
  
  
  Making Settlement Ready
&lt;/h1&gt;

&lt;p&gt;Now we can verify both deposits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function markReady(
    uint256 settlementId
)
external
{

    Settlement storage settlement =
        settlements[settlementId];


    require(
        settlement.status ==
        SettlementStatus.Pending,
        "Invalid state"
    );


    settlement.status =
        SettlementStatus.Ready;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, this function would perform full escrow validation.&lt;/p&gt;

&lt;p&gt;The simplified version demonstrates the state machine.&lt;/p&gt;




&lt;h1&gt;
  
  
  Executing Settlement
&lt;/h1&gt;

&lt;p&gt;Execution is where assets finally move.&lt;/p&gt;

&lt;p&gt;The coordinator instructs escrow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Release maker asset

        +

Release taker asset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important rule:&lt;/p&gt;

&lt;p&gt;Both sides execute atomically.&lt;/p&gt;

&lt;p&gt;Either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice receives ETH

Bob receives USDC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nothing happens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partial settlement is dangerous.&lt;/p&gt;




&lt;h1&gt;
  
  
  Atomic Settlement Pattern
&lt;/h1&gt;

&lt;p&gt;A bad implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;releaseAliceFunds();

releaseBobFunds();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the second operation fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice receives funds

Bob receives nothing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A production system requires transactional atomicity.&lt;/p&gt;

&lt;p&gt;The better pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function settle(
    uint256 settlementId
)
external
{

    Settlement storage settlement =
        settlements[settlementId];


    require(
        settlement.status ==
        SettlementStatus.Ready,
        "Not ready"
    );


    // execute both transfers


    settlement.status =
        SettlementStatus.Executed;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Solidity transactions revert entirely on failure, the operation is atomic.&lt;/p&gt;




&lt;h1&gt;
  
  
  Cross-Chain Settlement Design
&lt;/h1&gt;

&lt;p&gt;Now we reach the interesting architectural question.&lt;/p&gt;

&lt;p&gt;How does Sapphire communicate with Base?&lt;/p&gt;

&lt;p&gt;The answer is not:&lt;/p&gt;

&lt;p&gt;"Move the whole application to Base."&lt;/p&gt;

&lt;p&gt;The answer is:&lt;/p&gt;

&lt;p&gt;Create a settlement message.&lt;/p&gt;

&lt;p&gt;The flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sapphire

Private Trade Agreement

        |

        v

Settlement Message

        |

        v

Base

Public Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The settlement message contains only what is necessary.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct SettlementMessage {

    bytes32 tradeId;

    address receiver;

    address token;

    uint256 amount;

    uint256 nonce;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what is missing.&lt;/p&gt;

&lt;p&gt;No negotiation history.&lt;/p&gt;

&lt;p&gt;No quotes.&lt;/p&gt;

&lt;p&gt;No private strategy.&lt;/p&gt;

&lt;p&gt;Only the information required for final execution.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Matters
&lt;/h1&gt;

&lt;p&gt;The privacy boundary becomes very clear.&lt;/p&gt;

&lt;p&gt;Before settlement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Confidential
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After settlement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mirrors traditional finance.&lt;/p&gt;

&lt;p&gt;A hedge fund may privately negotiate a block trade.&lt;/p&gt;

&lt;p&gt;But once settlement occurs, ownership changes become visible.&lt;/p&gt;

&lt;p&gt;The blockchain is not replacing OTC markets.&lt;/p&gt;

&lt;p&gt;It is improving the settlement layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Adding Replay Protection
&lt;/h1&gt;

&lt;p&gt;Cross-chain systems introduce another risk.&lt;/p&gt;

&lt;p&gt;A valid settlement message could potentially be submitted twice.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message:

Pay Alice 500 ETH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executed once.&lt;/p&gt;

&lt;p&gt;Then replayed.&lt;/p&gt;

&lt;p&gt;Executed again.&lt;/p&gt;

&lt;p&gt;This is why every settlement message needs uniqueness.&lt;/p&gt;

&lt;p&gt;The simplest mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(bytes32 =&amp;gt; bool)
processedMessages;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require(
    !processedMessages[id],
    "Already executed"
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;processedMessages[id] = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One small mapping prevents catastrophic duplication.&lt;/p&gt;




&lt;h1&gt;
  
  
  Current Architecture
&lt;/h1&gt;

&lt;p&gt;We now have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Oasis Sapphire


          Confidential Negotiation


                    |

                    v


              Settlement Message


                    |

                    v


                   Base


          Transparent Final Settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has a clear responsibility.&lt;/p&gt;




&lt;h1&gt;
  
  
  Integrating Oasis Sapphire Confidential Execution
&lt;/h1&gt;

&lt;p&gt;The settlement architecture is now complete.&lt;/p&gt;

&lt;p&gt;However, we have not yet fully answered the original question:&lt;/p&gt;

&lt;p&gt;How do we actually make OTC negotiation confidential?&lt;/p&gt;

&lt;p&gt;So far, we have built a conventional Solidity protocol:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;offers,&lt;/li&gt;
&lt;li&gt;signatures,&lt;/li&gt;
&lt;li&gt;escrow,&lt;/li&gt;
&lt;li&gt;settlement coordination.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those components work on any EVM-compatible chain.&lt;/p&gt;

&lt;p&gt;The reason Oasis Sapphire is interesting is that it changes the privacy model of the application.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;"How do we encrypt everything before putting it on-chain?"&lt;/p&gt;

&lt;p&gt;we can ask:&lt;/p&gt;

&lt;p&gt;"Which parts of the application should execute privately?"&lt;/p&gt;

&lt;p&gt;That distinction is extremely important.&lt;/p&gt;




&lt;h1&gt;
  
  
  Public Blockchains and Information Leakage
&lt;/h1&gt;

&lt;p&gt;On a traditional EVM chain, contract state is transparent.&lt;/p&gt;

&lt;p&gt;If a contract stores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Offer {

    address maker;

    address tokenOffered;

    uint256 amount;

    uint256 price;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then anyone can eventually inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who created the offer,&lt;/li&gt;
&lt;li&gt;what asset they want,&lt;/li&gt;
&lt;li&gt;how much they are trading,&lt;/li&gt;
&lt;li&gt;the conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if the frontend hides this information, the blockchain does not.&lt;/p&gt;

&lt;p&gt;For OTC trading, that is a problem.&lt;/p&gt;

&lt;p&gt;The negotiation itself is valuable information.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Sapphire Changes
&lt;/h1&gt;

&lt;p&gt;Oasis Sapphire provides confidential EVM execution.&lt;/p&gt;

&lt;p&gt;The developer experience remains familiar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity ^0.8.24;

contract OTCRegistry {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but execution happens inside a confidential environment.&lt;/p&gt;

&lt;p&gt;Sensitive state can remain protected while the contract continues using Solidity patterns.&lt;/p&gt;

&lt;p&gt;The important architectural shift is:&lt;/p&gt;

&lt;p&gt;Before Sapphire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User

 |
 |
Encrypted Data

 |
 |
Public Smart Contract

 |
 |
Everyone Observes State
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After Sapphire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User

 |
 |
Confidential Contract Execution

 |
 |
Protected State

 |
 |
Only Authorized Outputs Revealed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Moving the Registry to Sapphire
&lt;/h1&gt;

&lt;p&gt;The OTC Registry is the natural candidate for Sapphire.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because the registry contains negotiation information.&lt;/p&gt;

&lt;p&gt;The escrow and final settlement layer do not need the same privacy guarantees.&lt;/p&gt;

&lt;p&gt;A better architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Oasis Sapphire


          OTCRegistry

     Private Offers + Quotes


                 |

                 |

          Settlement Proof


                 |

                 |

                  Base


          Final Asset Settlement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The registry becomes the private negotiation engine.&lt;/p&gt;




&lt;h1&gt;
  
  
  Confidential Offer Storage
&lt;/h1&gt;

&lt;p&gt;Previously we stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(uint256 =&amp;gt; Offer)
private offers;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a public chain, private storage is only private by convention.&lt;/p&gt;

&lt;p&gt;On Sapphire, confidential state is enforced by the execution environment.&lt;/p&gt;

&lt;p&gt;This means the contract can safely maintain information such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct PrivateOffer {

    address maker;

    address requestedCounterparty;

    uint256 amount;

    uint256 price;

    bytes encryptedTerms;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without exposing every field publicly.&lt;/p&gt;




&lt;h1&gt;
  
  
  Private Quotes
&lt;/h1&gt;

&lt;p&gt;Institutional OTC trading rarely happens with a single offer.&lt;/p&gt;

&lt;p&gt;Usually the process looks like:&lt;/p&gt;

&lt;p&gt;Trader:&lt;/p&gt;

&lt;p&gt;"I want to sell 10 million USDC."&lt;/p&gt;

&lt;p&gt;Market makers:&lt;/p&gt;

&lt;p&gt;"Here are three quotes."&lt;/p&gt;

&lt;p&gt;Trader:&lt;/p&gt;

&lt;p&gt;"I accept quote #2."&lt;/p&gt;

&lt;p&gt;A public blockchain would reveal every quote.&lt;/p&gt;

&lt;p&gt;That creates unnecessary information leakage.&lt;/p&gt;

&lt;p&gt;Sapphire allows this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trader

 |
 |
Private Request

 |
 |
Sapphire Contract

 |
 |
Market Makers Submit Quotes

 |
 |
Trader Accepts Best Quote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the final agreement needs to leave the confidential environment.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Sapphire Does Not Hide
&lt;/h1&gt;

&lt;p&gt;It is important not to overstate confidentiality.&lt;/p&gt;

&lt;p&gt;Sapphire does not magically make everything invisible.&lt;/p&gt;

&lt;p&gt;The final settlement on Base is public.&lt;/p&gt;

&lt;p&gt;Observers can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;settlement transactions,&lt;/li&gt;
&lt;li&gt;transferred assets,&lt;/li&gt;
&lt;li&gt;destination addresses,&lt;/li&gt;
&lt;li&gt;execution timing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The privacy boundary is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Negotiation

Private


Settlement

Public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly the same separation used in traditional OTC markets.&lt;/p&gt;




&lt;h1&gt;
  
  
  Confidential Signatures
&lt;/h1&gt;

&lt;p&gt;Our earlier EIP-712 flow becomes even more useful here.&lt;/p&gt;

&lt;p&gt;Instead of broadcasting every signed message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Offer Created

Quote Received

Counter Offer

Revised Quote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the application can keep those interactions confidential.&lt;/p&gt;

&lt;p&gt;Only the final signed commitment becomes relevant.&lt;/p&gt;

&lt;p&gt;The protocol effectively changes from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every negotiation step is public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Only the final agreement is verifiable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Sapphire Design Principles
&lt;/h1&gt;

&lt;p&gt;When building confidential contracts, the same engineering principles still apply.&lt;/p&gt;

&lt;p&gt;Privacy does not replace good architecture.&lt;/p&gt;

&lt;p&gt;We still need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;explicit state machines,&lt;/li&gt;
&lt;li&gt;replay protection,&lt;/li&gt;
&lt;li&gt;authorization checks,&lt;/li&gt;
&lt;li&gt;safe token handling,&lt;/li&gt;
&lt;li&gt;deterministic settlement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confidential execution protects information.&lt;/p&gt;

&lt;p&gt;It does not protect incorrect logic.&lt;/p&gt;




&lt;h1&gt;
  
  
  Building the SDK and Production Workflow
&lt;/h1&gt;

&lt;p&gt;The smart contracts are complete.&lt;/p&gt;

&lt;p&gt;Now we need users to interact with them.&lt;/p&gt;

&lt;p&gt;A professional protocol rarely exposes raw contract calls directly.&lt;/p&gt;

&lt;p&gt;Instead, applications use an SDK layer.&lt;/p&gt;

&lt;p&gt;Our repository already contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdk/

    otcClient.ts

    signer.ts

    quotes.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This layer handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wallet connections,&lt;/li&gt;
&lt;li&gt;typed messages,&lt;/li&gt;
&lt;li&gt;signatures,&lt;/li&gt;
&lt;li&gt;contract calls,&lt;/li&gt;
&lt;li&gt;settlement tracking.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Creating the SDK Client
&lt;/h1&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OTCClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;registryContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createOffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registryContract&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createOffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenOffered&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenRequested&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiry&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend should not know Solidity details.&lt;/p&gt;

&lt;p&gt;It should think in business operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;createOffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;acceptOffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;settle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Signing Offers
&lt;/h1&gt;

&lt;p&gt;The SDK also handles EIP-712.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signTypedData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;offer&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sell:

500,000 USDC

Receive:

165 ETH

Expires:

Tomorrow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x83ab29ff....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Readable signatures reduce user mistakes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Frontend Flow
&lt;/h1&gt;

&lt;p&gt;A complete user journey looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trader Opens App

        |

Creates OTC Request

        |

Signs Private Intent

        |

Market Makers Submit Quotes

        |

Trader Accepts Quote

        |

Assets Escrowed

        |

Settlement Message Created

        |

Base Transaction Executed

        |

Trade Complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Testing the Protocol
&lt;/h1&gt;

&lt;p&gt;Financial protocols require more than happy-path testing.&lt;/p&gt;

&lt;p&gt;We should test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid signatures,&lt;/li&gt;
&lt;li&gt;expired offers,&lt;/li&gt;
&lt;li&gt;double acceptance,&lt;/li&gt;
&lt;li&gt;failed deposits,&lt;/li&gt;
&lt;li&gt;replayed settlement messages,&lt;/li&gt;
&lt;li&gt;unauthorized withdrawals.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Example Foundry Test
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testCannotAcceptExpiredOffer()
    public
{

    vm.warp(block.timestamp + 2 days);


    vm.expectRevert(
        OfferExpired.selector
    );


    registry.acceptOffer(
        offerId
    );

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is not simply proving success.&lt;/p&gt;

&lt;p&gt;The goal is proving invalid behavior is impossible.&lt;/p&gt;




&lt;h1&gt;
  
  
  Fuzz Testing
&lt;/h1&gt;

&lt;p&gt;OTC protocols have many numeric inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;amounts,&lt;/li&gt;
&lt;li&gt;timestamps,&lt;/li&gt;
&lt;li&gt;nonces,&lt;/li&gt;
&lt;li&gt;token quantities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fuzz testing helps discover unexpected edge cases.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testAmounts(
    uint256 amount
)
public
{

    vm.assume(amount &amp;gt; 0);


    createOffer(amount);

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The framework automatically explores many values.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security Checklist
&lt;/h1&gt;

&lt;p&gt;Before deploying a protocol handling valuable assets:&lt;/p&gt;

&lt;h2&gt;
  
  
  Access Control
&lt;/h2&gt;

&lt;p&gt;Verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only makers cancel offers,&lt;/li&gt;
&lt;li&gt;only accepted counterparties settle,&lt;/li&gt;
&lt;li&gt;only authorized coordinators release escrow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Replay Protection
&lt;/h2&gt;

&lt;p&gt;Verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;signatures cannot be reused,&lt;/li&gt;
&lt;li&gt;settlement messages cannot execute twice,&lt;/li&gt;
&lt;li&gt;nonces increment correctly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  State Transitions
&lt;/h2&gt;

&lt;p&gt;Verify:&lt;/p&gt;

&lt;p&gt;Invalid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open → Settled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Open
 |
Accepted
 |
Escrowed
 |
Settled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Token Safety
&lt;/h2&gt;

&lt;p&gt;Use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SafeERC20,&lt;/li&gt;
&lt;li&gt;allowance checks,&lt;/li&gt;
&lt;li&gt;zero address validation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Failure Recovery
&lt;/h2&gt;

&lt;p&gt;Every financial protocol needs failure paths.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expired offers,&lt;/li&gt;
&lt;li&gt;abandoned settlements,&lt;/li&gt;
&lt;li&gt;cancelled negotiations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A protocol is not defined only by successful trades.&lt;/p&gt;

&lt;p&gt;It is defined by how safely it handles failure.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Architecture
&lt;/h1&gt;

&lt;p&gt;The completed system looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Trader A


                       |

                       |

              Oasis Sapphire


        Confidential OTC Registry

        - Private Offers

        - Private Quotes

        - Signed Agreements


                       |

                       |

              Settlement Message


                       |

                       |

                    Base


        Public Settlement Layer

        - Asset Transfer

        - Final Ownership


                       |

                       |

                    Trader B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Large trades require different infrastructure from ordinary swaps.&lt;/p&gt;

&lt;p&gt;Automated market makers are excellent for transparent liquidity.&lt;/p&gt;

&lt;p&gt;They are not designed for confidential institutional negotiation.&lt;/p&gt;

&lt;p&gt;An OTC protocol needs a different approach.&lt;/p&gt;

&lt;p&gt;The architecture we built separates the problem into three layers:&lt;/p&gt;

&lt;h2&gt;
  
  
  Confidential Negotiation
&lt;/h2&gt;

&lt;p&gt;Handled by Oasis Sapphire.&lt;/p&gt;

&lt;p&gt;This protects sensitive trading information while preserving smart contract coordination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asset Security
&lt;/h2&gt;

&lt;p&gt;Handled through escrow.&lt;/p&gt;

&lt;p&gt;This ensures both parties commit funds before execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public Settlement
&lt;/h2&gt;

&lt;p&gt;Handled by Base.&lt;/p&gt;

&lt;p&gt;This provides transparent final ownership transfer.&lt;/p&gt;

&lt;p&gt;The result is not a replacement for decentralized exchanges.&lt;/p&gt;

&lt;p&gt;It is a complementary system designed for a different class of trading.&lt;/p&gt;

&lt;p&gt;Large financial transactions require both privacy and trust minimization.&lt;/p&gt;

&lt;p&gt;Confidential execution provides the privacy.&lt;/p&gt;

&lt;p&gt;Smart contracts provide the enforcement.&lt;/p&gt;

&lt;p&gt;Public settlement provides the transparency.&lt;/p&gt;

&lt;p&gt;Together, they create a practical foundation for the next generation of decentralized OTC trading systems.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>privacy</category>
      <category>cryptocurrency</category>
      <category>confidentiality</category>
    </item>
    <item>
      <title>Building a Confidential Cross-Chain Identity Protocol with Oasis Sapphire, ENS &amp; Base</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Thu, 16 Jul 2026 07:15:44 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-cross-chain-identity-protocol-with-oasis-sapphire-ens-base-249o</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-cross-chain-identity-protocol-with-oasis-sapphire-ens-base-249o</guid>
      <description>&lt;p&gt;The biggest paradox in Web3 identity is that while we demand privacy in the real world, our on-chain infrastructure defaults to absolute transparency. If you attach a KYC credential, credit score, or real name to your ENS domain, it is immediately indexable by anyone.&lt;/p&gt;

&lt;p&gt;But what if you could store your sensitive Personally Identifiable Information (PII) inside a secure enclave, and only expose cryptographic proofs of that data to smart contracts on other chains?&lt;/p&gt;

&lt;p&gt;In this advanced tutorial, we are building a &lt;strong&gt;Confidential Cross-Chain Identity Layer&lt;/strong&gt;. We will use &lt;strong&gt;Oasis Sapphire&lt;/strong&gt; (the first confidential EVM) as our secure vault, &lt;strong&gt;Base&lt;/strong&gt; (Ethereum L2) as our execution environment, and the &lt;strong&gt;Oasis Privacy Layer (OPL) via Celer IM&lt;/strong&gt; to bridge the two. &lt;/p&gt;




&lt;h2&gt;
  
  
  The Real-World Use Case: Compliant DeFi on Base
&lt;/h2&gt;

&lt;p&gt;Imagine a DeFi lending protocol on Base that requires users to be non-US citizens and over 18 to access leverage. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; Storing "Country" and "Date of Birth" directly on Base violates privacy and data protection laws (like GDPR).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Solution:&lt;/strong&gt; The user mints an ENS name (&lt;code&gt;alice.base.eth&lt;/code&gt;) on Base. They store their private KYC data in a smart contract on Oasis Sapphire. When the Base DeFi protocol queries the ENS resolver, the resolver triggers a cross-chain message via OPL to Sapphire. Sapphire computes the logic inside a Trusted Execution Environment (TEE), checking if &lt;code&gt;age &amp;gt; 18 &amp;amp;&amp;amp; country != US&lt;/code&gt;, and returns a simple &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; back to Base.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No PII is ever leaked, the public RPCs see nothing but ciphertext, and the DeFi protocol remains fully compliant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture &amp;amp; Interaction Flow
&lt;/h2&gt;

&lt;p&gt;We will utilize &lt;strong&gt;EIP-3668 (CCIP-Read)&lt;/strong&gt; for off-chain data resolution and &lt;strong&gt;Celer Inter-Chain Messaging (IM)&lt;/strong&gt; for state synchronization between Base and Sapphire.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant User
    participant DeFi as Base DeFi Protocol
    participant Resolver as Base ENS Resolver
    participant Relayer as Celer IM / OPL
    participant Sapphire as Oasis Sapphire (TEE)

    User-&amp;gt;&amp;gt;Sapphire: 1. Encrypt &amp;amp; Store KYC (Age, Country)
    Note over Sapphire: State is encrypted end-to-end
    User-&amp;gt;&amp;gt;DeFi: 2. Request Undercollateralized Loan
    DeFi-&amp;gt;&amp;gt;Resolver: 3. Verify compliance for `alice.base.eth`
    Resolver-&amp;gt;&amp;gt;Relayer: 4. Dispatch Cross-Chain Request
    Relayer-&amp;gt;&amp;gt;Sapphire: 5. Execute query inside TEE
    Sapphire--&amp;gt;&amp;gt;Sapphire: 6. Decrypt -&amp;gt; Check logic -&amp;gt; Re-encrypt
    Sapphire-&amp;gt;&amp;gt;Relayer: 7. Emit boolean compliance proof
    Relayer-&amp;gt;&amp;gt;Resolver: 8. CCIP-Read Callback with Proof
    Resolver--&amp;gt;&amp;gt;DeFi: 9. Returns `true` (Compliant)
    DeFi--&amp;gt;&amp;gt;User: 10. Loan Approved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repository Structure
&lt;/h2&gt;

&lt;p&gt;For this advanced stack, we will use a monorepo built with Hardhat/Foundry to manage multi-chain deployments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cross-chain-identity/
├── contracts/
│   ├── base/
│   │   ├── ENSIdentityResolver.sol     # Custom ENS Resolver on Base
│   │   └── IDeFiProtocol.sol           # Mock DeFi contract
│   ├── sapphire/
│   │   ├── ConfidentialVault.sol       # TEE-encrypted storage contract
│   │   └── VerificationEngine.sol      # Logic computation 
│   └── interfaces/
│       ├── ICelerMessageBus.sol        # Celer IM interface
│       └── IOasisPrivacyLayer.sol
├── scripts/
│   ├── deploy-base.js
│   ├── deploy-sapphire.js
│   └── bridge-setup.js
├── hardhat.config.js                   # Configured for Base &amp;amp; Sapphire networks
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step-by-Step Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: The Confidential Vault on Oasis Sapphire&lt;br&gt;
Oasis Sapphire is EVM-compatible but runs inside secure enclaves. State variables are encrypted by default. We will write ConfidentialVault.sol to store user data securely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;ConfidentialVault&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This state is encrypted inside the Sapphire TEE&lt;/span&gt;
    &lt;span class="nx"&gt;struct&lt;/span&gt; &lt;span class="nx"&gt;IdentityRecord&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;dateOfBirth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nx"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;isVerified&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;IdentityRecord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;privateIdentities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Address of the Celer Message Bus on Sapphire&lt;/span&gt;
    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;messageBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_messageBus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;messageBus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_messageBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @dev User updates their private data. Transactions are end-to-end encrypted.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;_dob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nx"&gt;calldata&lt;/span&gt; &lt;span class="nx"&gt;_countryCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;privateIdentities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IdentityRecord&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;dateOfBirth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_dob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_countryCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;isVerified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @dev Executed via cross-chain message from Base.
     * Computes compliance without revealing raw data.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyComplianceCrossChain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_baseCallbackContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;messageBus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only Relayer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;IdentityRecord&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;privateIdentities&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// Confidential computation: Is over 18 and not US?&lt;/span&gt;
        &lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;isCompliant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isVerified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dateOfBirth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;31556926&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Approx years&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;isCompliant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Construct payload to send back to Base&lt;/span&gt;
        &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isCompliant&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Send via Celer IM (Oasis Privacy Layer) back to Base&lt;/span&gt;
        &lt;span class="c1"&gt;// (Pseudocode for Celer IM integration)&lt;/span&gt;
        &lt;span class="nc"&gt;ICelerMessageBus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageBus&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;_baseCallbackContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="mi"&gt;8453&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Base Mainnet Chain ID&lt;/span&gt;
            &lt;span class="nx"&gt;message&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: Because Sapphire encrypts calldata, an observer on block explorers will only see gibberish bytes, completely protecting the user's DoB and Country.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: The Cross-Chain ENS Resolver on Base&lt;br&gt;
On Base, our custom ENS Resolver implements executeWithCCIPRead to pause execution, request data from Sapphire, and resume via a callback once Celer IM delivers the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;ENSIdentityResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;messageBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;sapphireVaultAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;complianceCache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nc"&gt;OffchainLookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;callData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytes4&lt;/span&gt; &lt;span class="nx"&gt;callbackFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;extraData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_messageBus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_sapphireVaultAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;messageBus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_messageBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;sapphireVaultAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_sapphireVaultAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @dev Standard check called by DeFi protocols on Base.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isUserCompliant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If not in cache, trigger CCIP-Read / Cross-chain flow&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;complianceCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[https://our-relayer-gateway.io/verify/](https://our-relayer-gateway.io/verify/){sender}/{data}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="nx"&gt;revert&lt;/span&gt; &lt;span class="nc"&gt;OffchainLookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encodePacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;celfCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;complianceCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_user&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @dev Callback executed by Celer Executor once Sapphire computes the result.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;executeMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_srcContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;uint64&lt;/span&gt; &lt;span class="nx"&gt;_srcChainId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;calldata&lt;/span&gt; &lt;span class="nx"&gt;_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_executor&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="nx"&gt;payable&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;messageBus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only Celer Message Bus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_srcChainId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;23294&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only Sapphire Mainnet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sapphire Chain ID&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_srcContract&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;sapphireVaultAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid source contract&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt; &lt;span class="nx"&gt;isCompliant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// Cache the confidential state on Base&lt;/span&gt;
        &lt;span class="nx"&gt;complianceCache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isCompliant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Hardhat Deployment &amp;amp; Orchestration&lt;br&gt;
To deploy this, your hardhat.config.js needs RPC endpoints for both networks. Oasis Sapphire provides standard Web3 RPC endpoints, making the developer experience seamless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;solidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.8.19&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[https://mainnet.base.org](https://mainnet.base.org)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;sapphire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[https://sapphire.oasis.io](https://sapphire.oasis.io)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23294&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When integrating the frontend, use the @oasisprotocol/sapphire-paratime wrapper for Ethers.js. This automatically encrypts your transactions on the client side before sending them to the Sapphire network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;sapphire&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@oasisprotocol/sapphire-paratime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Wrap your standard signer to enable end-to-end encryption&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Web3Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sapphire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigner&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Now call the vault; the calldata will be automatically encrypted!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vaultContract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VAULT_ADDR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VAULT_ABI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vaultContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;946684800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Building the CCIP-Read Off-Chain Gateway (Node.js)&lt;br&gt;
When the Base contract reverts with OffchainLookup, the user's wallet (or the dApp frontend) will automatically send an HTTP GET request to the URL specified in the revert. We need an Express server to catch this, trigger the Celer IM message to Sapphire, and manage the asynchronous state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js (Gateway)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/verify/:sender/:data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Decode the user address from the original call data&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultAbiCoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// 1. Trigger the cross-chain message on the Relayer/Sapphire side&lt;/span&gt;
        &lt;span class="c1"&gt;// In a production environment, this triggers a relayer bot to pay gas on Sapphire&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;dispatchToSapphire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. We return a pending state or an acknowledgment &lt;/span&gt;
        &lt;span class="c1"&gt;// Note: Because Celer IM is asynchronous, the Base contract will receive &lt;/span&gt;
        &lt;span class="c1"&gt;// the actual boolean value from the Celer Executor in a subsequent block.&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending_cross_chain_execution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Celer IM dispatched to Oasis Sapphire. Await callback on Base.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CCIP-Read Gateway running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: Gas Abstraction (Zero-Gas UX on Sapphire)&lt;br&gt;
A major UX hurdle in cross-chain architectures is forcing the user to hold the native gas token (ROSE) on Oasis Sapphire just to update their identity.&lt;/p&gt;

&lt;p&gt;To solve this, we can implement the Oasis Gas Station Network (GSN) or a custom Paymaster. Because Sapphire supports EIP-4337 (Account Abstraction) and standard meta-transactions, you can set up a Gas Relayer. The user signs an EIP-712 message containing their encrypted KYC data, and your backend submits it to Sapphire, paying the ROSE gas fee on their behalf. The user only ever needs ETH on Base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;: Security &amp;amp; Threat Model&lt;br&gt;
When dealing with cross-chain PII, the threat model extends beyond standard smart contract bugs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State Desyncs&lt;/strong&gt;: If a user updates their age on Sapphire but the Base cache isn't refreshed, the Base DeFi protocol might operate on stale data. Always implement a Time-to-Live (TTL) or an expiration timestamp on the complianceCache mapping on Base.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replay Attacks&lt;/strong&gt;: Ensure the Celer IM callback (executeMessage) strictly verifies _srcChainId and _srcContract. If an attacker can spoof the message origin, they can arbitrarily set their complianceCache to true on Base.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TEE Side-Channels&lt;/strong&gt;: While Sapphire's Secure Enclaves (Intel SGX) protect data in use, developers must ensure they do not leak data via access patterns. Avoid writing logic that significantly changes gas consumption based on the value of a private variable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By combining ENS on Base with the Oasis Privacy Layer, we've built a robust architecture that solves Web3's biggest compliance hurdle. Smart contracts on L2s can now leverage confidential logic, request attestations, and maintain composability without doxxing their users.&lt;/p&gt;

&lt;p&gt;The future of Web3 identity isn't fully transparent, nor is it siloed in isolated private chains. The future is modular, cross-chain, and selectively private.&lt;/p&gt;

&lt;p&gt;If you found this advanced tutorial helpful, leave a comment below or check out the &lt;a href="https://docs.oasis.io/build/opl/" rel="noopener noreferrer"&gt;Oasis Privacy Layer Documentation&lt;/a&gt; to dive deeper into confidential smart contracts!&lt;/p&gt;

</description>
      <category>web3</category>
      <category>cryptocurrency</category>
      <category>privacy</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a Confidential Data Clean Room on Oasis Sapphire: Privacy-Preserving Analytics Across Organizations</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Sat, 13 Jun 2026 17:27:40 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-data-clean-room-on-oasis-sapphire-privacy-preserving-analytics-across-3el3</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-data-clean-room-on-oasis-sapphire-privacy-preserving-analytics-across-3el3</guid>
      <description>&lt;p&gt;One of the biggest unsolved problems in data engineering is collaboration between organizations that do not trust each other enough to share raw data.&lt;/p&gt;

&lt;p&gt;Consider the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two crypto exchanges want to identify overlapping fraudulent wallets.&lt;/li&gt;
&lt;li&gt;Multiple banks want to detect coordinated money laundering activity.&lt;/li&gt;
&lt;li&gt;Healthcare providers want to compare patient cohorts for research.&lt;/li&gt;
&lt;li&gt;Marketing companies want to measure customer overlap.&lt;/li&gt;
&lt;li&gt;Cybersecurity firms want to identify shared threat actors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all of these cases, the organizations would benefit from collaboration.&lt;/p&gt;

&lt;p&gt;The problem is that they cannot expose their underlying datasets.&lt;/p&gt;

&lt;p&gt;Traditional solutions usually involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trusted third parties&lt;/li&gt;
&lt;li&gt;Secure legal agreements&lt;/li&gt;
&lt;li&gt;Data escrow providers&lt;/li&gt;
&lt;li&gt;Multi-party computation frameworks&lt;/li&gt;
&lt;li&gt;Internal clean room infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These solutions are expensive, operationally complex, and often difficult to audit.&lt;/p&gt;

&lt;p&gt;This is where Oasis Sapphire, once again (:p), becomes interesting.&lt;/p&gt;

&lt;p&gt;Instead of sharing raw data, organizations can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Encrypt datasets locally&lt;/li&gt;
&lt;li&gt;Submit encrypted data to Sapphire&lt;/li&gt;
&lt;li&gt;Execute confidential computations&lt;/li&gt;
&lt;li&gt;Receive aggregated results&lt;/li&gt;
&lt;li&gt;Never expose the underlying records&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this tutorial we will build a complete confidential data clean room that allows multiple organizations to compare datasets without revealing them. I will also answer any comments/dms I'm going to get as soon as possible!&lt;/p&gt;

&lt;p&gt;We will cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Architecture design&lt;/li&gt;
&lt;li&gt;Confidential smart contracts&lt;/li&gt;
&lt;li&gt;Dataset encryption&lt;/li&gt;
&lt;li&gt;Secure submissions&lt;/li&gt;
&lt;li&gt;Set intersection algorithms&lt;/li&gt;
&lt;li&gt;Similarity metrics&lt;/li&gt;
&lt;li&gt;Differential privacy&lt;/li&gt;
&lt;li&gt;Attestation concepts&lt;/li&gt;
&lt;li&gt;Production deployment considerations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By the end, you will have a system capable of performing privacy-preserving analytics across organizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Suppose Exchange A maintains a list of suspicious wallets:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;0x111...&lt;br&gt;
0x222...&lt;br&gt;
0x333...&lt;br&gt;
0x444...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exchange B maintains its own list:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;0x333...&lt;br&gt;
0x444...&lt;br&gt;
0x555...&lt;br&gt;
0x666...&lt;/p&gt;

&lt;p&gt;Both exchanges want to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many suspicious wallets do we have in common?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A traditional approach would require both exchanges to reveal their full datasets.&lt;/p&gt;

&lt;p&gt;This creates multiple risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer privacy violations&lt;/li&gt;
&lt;li&gt;Regulatory concerns&lt;/li&gt;
&lt;li&gt;Competitive intelligence leakage&lt;/li&gt;
&lt;li&gt;Data retention obligations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The clean room model allows the question to be answered without exposing the datasets themselves.&lt;/p&gt;

&lt;p&gt;The final output becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overlap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing else is revealed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;High-Level Architecture
                Exchange A
                     |
                     |
            Encrypt Dataset
                     |
                     v

          +------------------+
          | Oasis Sapphire   |
          | Confidential     |
          | Data Clean Room  |
          +------------------+

                     ^
                     |
            Encrypt Dataset
                     |
                     |

                Exchange B

                     |
                     v

              Aggregated Result

               overlap = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical observation is that raw data never leaves the confidential execution boundary in plaintext.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project Structure
sapphire-clean-room/

├── contracts/
│   ├── CleanRoom.sol
│   ├── SimilarityEngine.sol
│
├── scripts/
│   ├── deploy.ts
│
├── worker/
│   ├── encryptDataset.ts
│   ├── submitDataset.ts
│
├── api/
│   ├── server.ts
│
├── datasets/
│   ├── exchangeA.csv
│   ├── exchangeB.csv
│
├── test/
│   ├── cleanRoom.test.ts
│
└── hardhat.config.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding Data Clean Rooms
&lt;/h2&gt;

&lt;p&gt;A data clean room is a controlled environment where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data providers submit information&lt;/li&gt;
&lt;li&gt;Computations occur&lt;/li&gt;
&lt;li&gt;Raw data remains hidden&lt;/li&gt;
&lt;li&gt;Only approved outputs leave the environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditionally, cloud providers offer clean room services.&lt;/p&gt;

&lt;p&gt;The challenge is trust.&lt;/p&gt;

&lt;p&gt;Organizations must trust:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Infrastructure operators&lt;/li&gt;
&lt;li&gt;Database administrators&lt;/li&gt;
&lt;li&gt;Cloud providers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sapphire introduces a different trust model.&lt;/p&gt;

&lt;p&gt;The computation occurs inside confidential execution environments backed by Trusted Execution Environments (TEEs).&lt;/p&gt;

&lt;p&gt;This means computation is isolated from the host system itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Dataset Registration Contract
&lt;/h3&gt;

&lt;p&gt;We begin by allowing organizations to register encrypted datasets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity ^0.8.20;

contract CleanRoom {

    struct Dataset {
        bytes encryptedData;
        address owner;
        uint256 submittedAt;
        bool active;
    }

    uint256 public datasetCount;

    mapping(uint256 =&amp;gt; Dataset) public datasets;

    event DatasetSubmitted(
        uint256 indexed datasetId,
        address indexed owner
    );

    function submitDataset(
        bytes calldata encryptedData
    ) external {

        datasetCount++;

        datasets[datasetCount] = Dataset({
            encryptedData: encryptedData,
            owner: msg.sender,
            submittedAt: block.timestamp,
            active: true
        });

        emit DatasetSubmitted(
            datasetCount,
            msg.sender
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This contract stores encrypted payloads only.&lt;/li&gt;
&lt;li&gt;The blockchain never sees plaintext records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Encryption Alone Is Not Enough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A common misconception is:&lt;/p&gt;

&lt;p&gt;"Just encrypt the data and store it on-chain."&lt;/p&gt;

&lt;p&gt;Encryption protects storage.&lt;/p&gt;

&lt;p&gt;It does not solve computation.&lt;/p&gt;

&lt;p&gt;Eventually somebody must decrypt and process the data.&lt;/p&gt;

&lt;p&gt;The real challenge is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do we compute on sensitive information without exposing it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is where Sapphire enters the picture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Confidential Set Intersection
&lt;/h3&gt;

&lt;p&gt;The most common clean room operation is set intersection.&lt;/p&gt;

&lt;p&gt;We want:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A ∩ B&lt;/strong&gt; without revealing A or B.&lt;/p&gt;

&lt;p&gt;Inside Sapphire we can safely decrypt datasets and perform the operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity ^0.8.20;

contract SimilarityEngine {

    function computeIntersection(
        address[] memory a,
        address[] memory b
    )
        public
        pure
        returns (uint256)
    {
        uint256 overlap;

        for (uint256 i = 0; i &amp;lt; a.length; i++) {

            for (uint256 j = 0; j &amp;lt; b.length; j++) {

                if (a[i] == b[j]) {
                    overlap++;
                }
            }
        }

        return overlap;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation is intentionally simple. We will optimize it later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Naive Approach Does Not Scale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dataset A = 100,000 entries&lt;br&gt;
Dataset B = 100,000 entries&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Complexity becomes:&lt;/p&gt;

&lt;p&gt;100,000 × 100,000 = 10 billion comparisons&lt;/p&gt;

&lt;p&gt;This is not practical. We need better data structures.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Hash-Based Intersection
&lt;/h3&gt;

&lt;p&gt;Instead of nested loops we build lookup tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function computeIntersectionFast(
    bytes32[] memory a,
    bytes32[] memory b
)
    public
    pure
    returns (uint256)
{
    uint256 overlap;

    for (uint256 i = 0; i &amp;lt; a.length; i++) {

        for (uint256 j = 0; j &amp;lt; b.length; j++) {

            if (a[i] == b[j]) {
                overlap++;
            }
        }
    }

    return overlap;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, off-chain preprocessing should convert datasets into hashed representations.&lt;/p&gt;

&lt;p&gt;Benefits include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller payloads&lt;/li&gt;
&lt;li&gt;Better privacy&lt;/li&gt;
&lt;li&gt;Faster matching&lt;/li&gt;
&lt;li&gt;Similarity Metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes overlap counts are insufficient.&lt;/p&gt;

&lt;p&gt;Organizations may want similarity scores.&lt;/p&gt;

&lt;p&gt;A common metric is Jaccard Similarity.&lt;/p&gt;

&lt;p&gt;The formula is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jaccard Similarity = Number of Shared Records / Total Unique Records​&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Interpretation:&lt;/p&gt;

&lt;p&gt;0.0 = completely different&lt;br&gt;
1.0 = identical datasets&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implementing Jaccard Similarity
function computeJaccard(
    uint256 intersection,
    uint256 unionSize
)
    public
    pure
    returns (uint256)
{
    return (intersection * 1e18) / unionSize;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returning scaled values avoids floating-point arithmetic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Local Dataset Encryption
&lt;/h3&gt;

&lt;p&gt;Before submission, organizations encrypt locally.&lt;/p&gt;

&lt;p&gt;Example Node.js utility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;algorithm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aes-256-gcm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;encryptDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cipher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCipheriv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;iv&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;cipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;cipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;final&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;cipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAuthTag&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Encryption occurs before data leaves the organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Submission Worker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;submitDataset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;ABI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;signer&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;encryptDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;key&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUtf8Bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worker becomes the ingestion pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Differential Privacy Layer
&lt;/h2&gt;

&lt;p&gt;Even aggregate results can leak information.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;Dataset overlap: 1245&lt;/p&gt;

&lt;p&gt;Repeated queries could allow reconstruction attacks.&lt;/p&gt;

&lt;p&gt;To mitigate this, we can introduce controlled noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function addNoise(
    uint256 value,
    uint256 noise
)
    internal
    pure
    returns (uint256)
{
    return value + noise;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;p&gt;Actual overlap: 1245&lt;br&gt;
Reported overlap: 1242&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy improves while preserving utility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multi-Party Data Clean Rooms&lt;/p&gt;

&lt;p&gt;The real power appears when more than two organizations participate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exchange A
Exchange B
Exchange C
Exchange D

        |
        v

   Sapphire Clean Room

        |
        v

  Aggregated Analysis

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared fraud detection&lt;/li&gt;
&lt;li&gt;Shared sanctions monitoring&lt;/li&gt;
&lt;li&gt;Shared AML screening&lt;/li&gt;
&lt;li&gt;Shared cybersecurity intelligence&lt;/li&gt;
&lt;li&gt;Production Security Considerations&lt;/li&gt;
&lt;li&gt;Dataset Poisoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An attacker may submit fabricated data.&lt;/p&gt;

&lt;p&gt;Mitigations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Staking requirements&lt;/li&gt;
&lt;li&gt;Dataset signatures&lt;/li&gt;
&lt;li&gt;Reputation systems&lt;/li&gt;
&lt;li&gt;Replay Attacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An attacker may repeatedly submit old datasets.&lt;/p&gt;

&lt;p&gt;Mitigations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping(bytes32 =&amp;gt; bool)
public processedHashes;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reject duplicates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query Abuse
&lt;/h2&gt;

&lt;p&gt;Attackers may repeatedly request computations.&lt;/p&gt;

&lt;p&gt;Mitigations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Access control&lt;/li&gt;
&lt;li&gt;Usage quotas&lt;/li&gt;
&lt;li&gt;Membership Inference Attacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An attacker may attempt to infer whether a specific record exists.&lt;/p&gt;

&lt;p&gt;Mitigations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Differential privacy&lt;/li&gt;
&lt;li&gt;Minimum cohort sizes&lt;/li&gt;
&lt;li&gt;Query restrictions&lt;/li&gt;
&lt;li&gt;Future Extensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture can evolve into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidential AML Network&lt;/li&gt;
&lt;li&gt;Banks privately compare suspicious entities.&lt;/li&gt;
&lt;li&gt;Cross-Exchange Fraud Detection&lt;/li&gt;
&lt;li&gt;Exchanges identify shared attackers.&lt;/li&gt;
&lt;li&gt;Healthcare Research Network&lt;/li&gt;
&lt;li&gt;Hospitals compare patient cohorts without sharing records.&lt;/li&gt;
&lt;li&gt;Privacy-Preserving Advertising Analytics&lt;/li&gt;
&lt;li&gt;Companies measure audience overlap.&lt;/li&gt;
&lt;li&gt;Confidential Supply Chain Intelligence&lt;/li&gt;
&lt;li&gt;Manufacturers compare supplier data without exposing relationships.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Most blockchain applications focus on moving value.&lt;/p&gt;

&lt;p&gt;Far fewer explore moving information safely.&lt;/p&gt;

&lt;p&gt;Confidential data clean rooms represent a massive opportunity because they solve a real-world problem that organizations face every day:&lt;/p&gt;

&lt;p&gt;How can we collaborate on data without exposing the data itself?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oasis Sapphire&lt;/strong&gt; provides a powerful foundation for this model by combining smart contracts with confidential execution.&lt;/p&gt;

&lt;p&gt;Instead of choosing between privacy and computation, we can finally have both.&lt;/p&gt;

&lt;p&gt;The result is a new class of applications where organizations can learn from each other without surrendering control of their most valuable asset: their data.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Confidential Event-Driven Indexer on Oasis Sapphire (Beyond The Graph Pattern)</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Thu, 11 Jun 2026 07:18:56 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-event-driven-indexer-on-oasis-sapphire-beyond-the-graph-pattern-3kac</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-event-driven-indexer-on-oasis-sapphire-beyond-the-graph-pattern-3kac</guid>
      <description>&lt;p&gt;Most blockchain indexing systems today assume a very simple model:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;all blockchain data is public, and indexing is just a deterministic transformation of public events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This assumption works well for analytics platforms like dashboards, DEX trackers, or portfolio tools.&lt;/p&gt;

&lt;p&gt;However, it breaks down in real-world scenarios where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;event data is sensitive (wallet behavior, internal flows, private classifications)&lt;/li&gt;
&lt;li&gt;indexing logic itself is proprietary (risk models, scoring systems, compliance logic)&lt;/li&gt;
&lt;li&gt;or you need selective disclosure (some users can see more data than others)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In traditional architectures, you are forced to choose between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;transparency (but no privacy)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;privacy (but no verifiability)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis Sapphire introduces a third option:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;execute indexing logic inside a confidential runtime (TEE-backed), while still using standard EVM interfaces.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This tutorial builds a &lt;strong&gt;confidential event-driven indexer&lt;/strong&gt;, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;raw blockchain events remain public&lt;/li&gt;
&lt;li&gt;classification logic is hidden&lt;/li&gt;
&lt;li&gt;only sanitized outputs are exposed externally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The Graph, but the mapping function is private and policy-controlled.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why This Is Architecturally Different
&lt;/h2&gt;

&lt;p&gt;To understand why this matters, let's compare the standard indexing pipeline:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Indexing (The Graph-style)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Smart Contract -&amp;gt; Events -&amp;gt; Indexer -&amp;gt; Public Database -&amp;gt; API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deterministic&lt;/li&gt;
&lt;li&gt;reproducible&lt;/li&gt;
&lt;li&gt;fully observable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is great for trustlessness, but it creates problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;any adversary can reconstruct your analytics logic&lt;/li&gt;
&lt;li&gt;wallet behavior is fully exposed&lt;/li&gt;
&lt;li&gt;proprietary scoring models cannot exist&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Confidential Indexing (Oasis Sapphire model)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Smart Contract -&amp;gt; Events
                     ↓
        Sapphire Confidential Runtime
                     ↓
          Private State Transformation
                     ↓
         Offchain Worker + Storage
                     ↓
            Sanitized Public API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key difference:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transformation function (indexing logic) is no longer public.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Real-World Use Case
&lt;/h2&gt;

&lt;p&gt;We will build a &lt;strong&gt;confidential DeFi activity indexer&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tracks swaps on a DEX&lt;/li&gt;
&lt;li&gt;computes internal risk scores&lt;/li&gt;
&lt;li&gt;classifies activity (low / medium / high risk)&lt;/li&gt;
&lt;li&gt;hides wallet-level behavior&lt;/li&gt;
&lt;li&gt;exposes only aggregated metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;institutional compliance dashboards&lt;/li&gt;
&lt;li&gt;private DeFi analytics platforms&lt;/li&gt;
&lt;li&gt;MEV-aware monitoring systems&lt;/li&gt;
&lt;li&gt;regulated crypto infrastructure&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Not Just Use Existing Indexers?
&lt;/h2&gt;

&lt;p&gt;Existing solutions like The Graph or custom ETL pipelines have a fundamental limitation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;they cannot hide the transformation logic itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even if you obfuscate the backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input events are public&lt;/li&gt;
&lt;li&gt;transformations are reproducible&lt;/li&gt;
&lt;li&gt;reverse-engineering is trivial&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Sapphire:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logic executes in a TEE (Trusted Execution Environment)&lt;/li&gt;
&lt;li&gt;computation is not externally observable&lt;/li&gt;
&lt;li&gt;only final outputs are revealed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Structure (Production-Grade)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;confidential-indexer/
│
├── contracts/
│   ├── Dex.sol
│   ├── ConfidentialIndexer.sol
│   ├── interfaces/
│
├── worker/
│   ├── indexer.ts
│   ├── eventDecoder.ts
│   ├── riskEngine.ts
│   ├── db.ts
│
├── api/
│   ├── server.ts
│   ├── routes/
│
├── scripts/
│   ├── deploy.ts
│
├── test/
│   ├── indexer.test.ts
│
└── hardhat.config.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Event Source Contract (DEX Simulation)
&lt;/h2&gt;

&lt;p&gt;We start with a simple decentralized exchange emitting swap events.&lt;/p&gt;

&lt;p&gt;This is intentionally minimal, the complexity happens in the indexing layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// contracts/Dex.sol
pragma solidity ^0.8.20;

contract Dex {

    event Swap(
        address indexed user,
        address indexed tokenIn,
        address indexed tokenOut,
        uint256 amount,
        uint256 timestamp
    );

    function swap(
        address tokenIn,
        address tokenOut,
        uint256 amount
    ) external {
        emit Swap(
            msg.sender,
            tokenIn,
            tokenOut,
            amount,
            block.timestamp
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key idea:
&lt;/h3&gt;

&lt;p&gt;We deliberately include structured data so the indexer can apply richer classification logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Confidential Indexer Contract (Sapphire Core Logic)
&lt;/h2&gt;

&lt;p&gt;This is the core of the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key properties:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;runs inside Sapphire confidential runtime&lt;/li&gt;
&lt;li&gt;decrypts event payloads&lt;/li&gt;
&lt;li&gt;applies private scoring rules&lt;/li&gt;
&lt;li&gt;stores encrypted internal state
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// contracts/ConfidentialIndexer.sol
pragma solidity ^0.8.20;

import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";

contract ConfidentialIndexer {

    struct SwapData {
        address user;
        address tokenIn;
        address tokenOut;
        uint256 amount;
        uint256 timestamp;
    }

    struct PoolState {
        uint256 riskScore;
        uint256 volume;
    }

    mapping(bytes32 =&amp;gt; PoolState) private pools;

    event Indexed(bytes32 indexed poolId, uint256 riskScore, uint256 volume);

    function processSwap(bytes calldata encryptedSwap) external {

        // Step 1: decrypt inside TEE
        bytes memory decrypted = Sapphire.decrypt(encryptedSwap);

        SwapData memory data = abi.decode(decrypted, (SwapData));

        // Step 2: derive pool identifier
        bytes32 poolId = keccak256(
            abi.encodePacked(data.tokenIn, data.tokenOut)
        );

        // Step 3: update private state
        PoolState storage state = pools[poolId];

        state.volume += data.amount;

        uint256 risk = computeRisk(data, state);

        state.riskScore = risk;

        // Step 4: emit only sanitized output
        emit Indexed(poolId, state.riskScore, state.volume);
    }

    function computeRisk(
        SwapData memory data,
        PoolState memory state
    )
        internal
        pure
        returns (uint256)
    {
        uint256 score = 0;

        // Large trade heuristic
        if (data.amount &amp;gt; 1_000_000 ether) {
            score += 5;
        }

        // Volume spike heuristic
        if (state.volume &amp;gt; 10_000_000 ether) {
            score += 3;
        }

        return score;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Why this is important:
&lt;/h3&gt;

&lt;p&gt;The key innovation is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the risk model is never observable externally&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adversaries cannot game your scoring system&lt;/li&gt;
&lt;li&gt;analytics logic is not reproducible&lt;/li&gt;
&lt;li&gt;you can build proprietary indexers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Event Worker (Offchain Index Aggregator)
&lt;/h2&gt;

&lt;p&gt;This component listens to Sapphire outputs and stores them.&lt;/p&gt;

&lt;p&gt;Unlike traditional indexers, it does NOT compute logic, only stores results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// worker/indexer.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ABI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Indexed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;poolId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;riskScore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Indexed update received&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;poolId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;poolId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;riskScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;riskScore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Public API Layer (Sanitized Exposure)
&lt;/h2&gt;

&lt;p&gt;This layer ensures no raw or sensitive data leaks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// api/server.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/pool/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;riskScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;riskScore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// intentionally no user-level data exposed&lt;/span&gt;
    &lt;span class="na"&gt;granularity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aggregated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Design Insight: Privacy Boundary Model
&lt;/h2&gt;

&lt;p&gt;This architecture introduces a strict separation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Visibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Smart contract events&lt;/td&gt;
&lt;td&gt;Public&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sapphire execution logic&lt;/td&gt;
&lt;td&gt;Confidential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk model / heuristics&lt;/td&gt;
&lt;td&gt;Hidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggregated outputs&lt;/td&gt;
&lt;td&gt;Public (sanitized)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Hard Engineering Problems (Important Section)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Determinism vs Confidential Execution
&lt;/h3&gt;

&lt;p&gt;TEE environments must remain deterministic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;same input -&amp;gt; same output&lt;/li&gt;
&lt;li&gt;but without revealing internal computation steps&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Trust in Index Correctness
&lt;/h3&gt;

&lt;p&gt;Because computation is hidden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you rely on enclave guarantees&lt;/li&gt;
&lt;li&gt;or multi-worker validation systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Composability Limitations
&lt;/h3&gt;

&lt;p&gt;Unlike The Graph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;results are not universally reproducible&lt;/li&gt;
&lt;li&gt;external contracts cannot verify raw computation&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. State Replay Problem
&lt;/h3&gt;

&lt;p&gt;Encrypted state introduces challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;historical recomputation is non-trivial&lt;/li&gt;
&lt;li&gt;debugging requires replay tooling inside TEE&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;This pattern enables a new category of systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;private blockchain analytics&lt;/li&gt;
&lt;li&gt;institutional DeFi infrastructure&lt;/li&gt;
&lt;li&gt;MEV-resistant monitoring tools&lt;/li&gt;
&lt;li&gt;compliance-aware data pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It shifts the paradigm from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“everything is transparent and verifiable”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“computation is verifiable, but not necessarily observable”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Most blockchain infrastructure assumes transparency is always beneficial.&lt;/p&gt;

&lt;p&gt;But in real systems, especially financial and institutional contexts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;privacy is required&lt;/li&gt;
&lt;li&gt;analytics are proprietary&lt;/li&gt;
&lt;li&gt;and data exposure is a liability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis Sapphire enables a third design space:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;verifiable execution with confidential logic&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This indexer pattern is just one example of what becomes possible when indexing itself becomes programmable and private.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Verifiable Multi-Chain AI Execution Layer with Oasis ROFL + Sapphire + TDX</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Thu, 21 May 2026 07:02:11 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-verifiable-multi-chain-ai-execution-layer-with-oasis-rofl-sapphire-tdx-14na</link>
      <guid>https://dev.to/rollingindo/building-a-verifiable-multi-chain-ai-execution-layer-with-oasis-rofl-sapphire-tdx-14na</guid>
      <description>&lt;p&gt;Most AI on blockchain applications today still rely heavily on trust.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The AI inference happens on centralized servers.
&lt;/li&gt;
&lt;li&gt;The wallet keys live in backend infrastructure.
&lt;/li&gt;
&lt;li&gt;Smart contracts trust APIs blindly.
&lt;/li&gt;
&lt;li&gt;And users have no way to verify what actually happened off-chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, we’ll build something far more advanced:&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;verifiable confidential AI execution layer&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oasis Sapphire&lt;/li&gt;
&lt;li&gt;ROFL (Runtime Offchain Logic)&lt;/li&gt;
&lt;li&gt;Intel TDX Trusted Execution Environments&lt;/li&gt;
&lt;li&gt;Remote Attestation&lt;/li&gt;
&lt;li&gt;Enclave-managed wallet signing&lt;/li&gt;
&lt;li&gt;Cross-chain transaction execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private AI inference
&lt;/li&gt;
&lt;li&gt;Verifiable off-chain execution
&lt;/li&gt;
&lt;li&gt;Secure enclave wallet management
&lt;/li&gt;
&lt;li&gt;Cross-chain automation
&lt;/li&gt;
&lt;li&gt;Trust-minimized AI agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without needing zkML or expensive zkVM proving systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  What We’re Building
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------------------------------------+
|                 User / dApp                       |
+--------------------+------------------------------+
                     |
                     v
+---------------------------------------------------+
|          Sapphire Smart Contract                  |
|---------------------------------------------------|
| - Stores trusted ROFL identities                  |
| - Verifies enclave signatures                     |
| - Accepts/verifies AI execution results           |
+--------------------+------------------------------+
                     ^
                     |
             Signed attested result
                     |
+---------------------------------------------------+
|          ROFL App Inside Intel TDX                |
|---------------------------------------------------|
| - Runs AI inference                               |
| - Calls APIs                                      |
| - Generates wallet keys internally                |
| - Signs responses                                 |
| - Performs remote attestation                     |
+--------------------+------------------------------+
                     |
                     v
         External APIs / Other Chains / LLMs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Why This Matters
&lt;/h1&gt;

&lt;p&gt;Most AI agents today suffer from major trust issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Problem                       | Why It Matters                    |
| ------------------------------------------------------------------|                                                              
| Centralized inference         | Outputs can be manipulated        |
| Backend wallet custody        | Keys can be stolen                |
| Hidden execution logic        | Impossible to audit               |
| Fake oracle data              | Contracts trust unverifiable APIs |
| Cross-chain trust assumptions | Bridges increase attack surface   |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ROFL changes this entirely.&lt;/p&gt;

&lt;p&gt;Instead of trusting a backend server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;computation runs inside a Trusted Execution Environment&lt;/li&gt;
&lt;li&gt;outputs are cryptographically signed&lt;/li&gt;
&lt;li&gt;enclave identities are attested&lt;/li&gt;
&lt;li&gt;smart contracts verify execution authenticity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates &lt;strong&gt;trustless off-chain compute&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Core Technologies
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Sapphire
&lt;/h2&gt;

&lt;p&gt;Sapphire is Oasis Network’s confidential EVM runtime.&lt;/p&gt;

&lt;p&gt;Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;encrypted calldata&lt;/li&gt;
&lt;li&gt;encrypted state&lt;/li&gt;
&lt;li&gt;confidential smart contract execution&lt;/li&gt;
&lt;li&gt;Solidity/EVM compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike traditional EVM chains, node operators cannot inspect execution data.&lt;/p&gt;




&lt;h2&gt;
  
  
  ROFL (Runtime Offchain Logic)
&lt;/h2&gt;

&lt;p&gt;ROFL allows developers to run arbitrary off-chain logic inside TEEs.&lt;/p&gt;

&lt;p&gt;This means your application can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call APIs&lt;/li&gt;
&lt;li&gt;run AI models&lt;/li&gt;
&lt;li&gt;sign transactions&lt;/li&gt;
&lt;li&gt;manage secrets&lt;/li&gt;
&lt;li&gt;execute autonomous workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;while remaining cryptographically verifiable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Intel TDX
&lt;/h2&gt;

&lt;p&gt;Intel TDX provides confidential virtual machines with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;encrypted memory&lt;/li&gt;
&lt;li&gt;hardware isolation&lt;/li&gt;
&lt;li&gt;attestation support&lt;/li&gt;
&lt;li&gt;strong execution guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ROFL supports TDX-backed confidential compute environments.&lt;/p&gt;




&lt;h1&gt;
  
  
  Architecture Deep Dive
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        User Prompt
              |
              v
+-----------------------------------+
|      Sapphire Smart Contract      |
+-----------------------------------+
              |
              v
+-----------------------------------+
|       ROFL TDX Enclave            |
|-----------------------------------|
| 1. Receives encrypted request     |
| 2. Runs AI inference              |
| 3. Generates signed result        |
| 4. Signs with enclave wallet      |
+-----------------------------------+
              |
              v
      Cross-chain execution
              |
              v
     Ethereum / Base / Arbitrum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 1: Install Oasis CLI
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://get.oasis.io | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oasis &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 2: Create a ROFL Application
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;ai-executor
&lt;span class="nb"&gt;cd &lt;/span&gt;ai-executor

oasis rofl init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rofl.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The manifest defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enclave configuration&lt;/li&gt;
&lt;li&gt;TEE type&lt;/li&gt;
&lt;li&gt;networking&lt;/li&gt;
&lt;li&gt;policies&lt;/li&gt;
&lt;li&gt;deployment settings&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 3: Configure ROFL Manifest
&lt;/h1&gt;

&lt;p&gt;Example advanced configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-executor&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.1.0&lt;/span&gt;

&lt;span class="na"&gt;tee&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tdx&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containers&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8192&lt;/span&gt;
  &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paratime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sapphire&lt;/span&gt;

&lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;quotes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pcs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tdx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;

&lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Important Manifest Settings
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Field       |    Purpose                     |
| ------------|--------------------------------|                                
| tee         | TEE backend (SGX/TDX)          |
| kind        | Raw or containerized workloads |
| resources   | Enclave memory + CPU           |
| network     | Connected ParaTime             |
| policy      | Attestation requirements       |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 4: Build the Enclave
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oasis rofl build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates an ORC bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app.orc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bundle includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enclave measurements&lt;/li&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;binaries&lt;/li&gt;
&lt;li&gt;identity hashes&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 5: Generate Wallet Keys Inside the Enclave
&lt;/h1&gt;

&lt;p&gt;This is one of the most powerful features.&lt;/p&gt;

&lt;p&gt;Traditional AI agents store keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;in cloud KMS systems&lt;/li&gt;
&lt;li&gt;in backend infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Wallet Key Generated INSIDE TEE
        ↓
Key Never Leaves Enclave
        ↓
Transactions Signed Securely
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No backend custody required.&lt;/p&gt;




&lt;h1&gt;
  
  
  Example Rust Wallet Generation
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;k256&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ecdsa&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SigningKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rand_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;OsRng&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;generate_wallet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SigningKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;SigningKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;OsRng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The private key only exists inside enclave memory.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 6: Run AI Inference Securely
&lt;/h1&gt;

&lt;p&gt;Example inference service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;

&lt;span class="n"&gt;classifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-classification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distilbert-base-uncased&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Execute strategy?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside ROFL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prompts remain confidential&lt;/li&gt;
&lt;li&gt;inference is hidden from operators&lt;/li&gt;
&lt;li&gt;outputs are signed by the enclave&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Secure AI Execution Flow
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Prompt
    ↓
ROFL Enclave
    ↓
Private AI Inference
    ↓
Signed Output
    ↓
Sapphire Verification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 7: Remote Attestation
&lt;/h1&gt;

&lt;p&gt;Attestation proves:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This exact code executed inside genuine TDX hardware.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without attestation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TEEs are just claims&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With attestation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;contracts can verify enclave authenticity&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Attestation Flow
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROFL App Starts
      ↓
TEE Generates Measurement
      ↓
Attestation Quote Produced
      ↓
Quote Registered On-Chain
      ↓
Sapphire Verifies Identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 8: Register the ROFL App
&lt;/h1&gt;

&lt;p&gt;Create the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oasis rofl create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; testnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--paratime&lt;/span&gt; sapphire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oasis rofl update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enclave identities&lt;/li&gt;
&lt;li&gt;measurements&lt;/li&gt;
&lt;li&gt;execution policy&lt;/li&gt;
&lt;li&gt;attestation configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;on-chain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 9: Smart Contract Verification
&lt;/h1&gt;

&lt;p&gt;Now we create a Sapphire contract that verifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enclave identity&lt;/li&gt;
&lt;li&gt;signatures&lt;/li&gt;
&lt;li&gt;trusted execution&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Solidity Verification Contract
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity ^0.8.20;

contract VerifiedAIExecutor {

    mapping(bytes32 =&amp;gt; bool) public trustedEnclaves;

    function registerEnclave(bytes32 enclaveId) external {
        trustedEnclaves[enclaveId] = true;
    }

    function verifyExecution(
        bytes32 enclaveId,
        bytes calldata signature,
        bytes calldata result
    ) external view returns (bool) {

        require(trustedEnclaves[enclaveId], "Untrusted enclave");

        // Signature verification logic
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 10: Cross-Chain Transaction Execution
&lt;/h1&gt;

&lt;p&gt;This is where things become extremely powerful.&lt;/p&gt;

&lt;p&gt;The enclave can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manage Ethereum wallets&lt;/li&gt;
&lt;li&gt;sign Base transactions&lt;/li&gt;
&lt;li&gt;execute Arbitrum transactions&lt;/li&gt;
&lt;li&gt;interact with multiple chains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without bridges.&lt;/p&gt;




&lt;h1&gt;
  
  
  Cross-Chain Execution Flow
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Strategy Generated
        ↓
TEE Decides Action
        ↓
TEE Signs Transaction
        ↓
Broadcast To Ethereum RPC
        ↓
Result Returned To Sapphire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;autonomous trading systems&lt;/li&gt;
&lt;li&gt;AI treasury management&lt;/li&gt;
&lt;li&gt;confidential DAO executors&lt;/li&gt;
&lt;li&gt;secure cross-chain agents&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 11: Deterministic Execution Logging
&lt;/h1&gt;

&lt;p&gt;AI outputs are often nondeterministic.&lt;/p&gt;

&lt;p&gt;To improve auditability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hash prompts&lt;/li&gt;
&lt;li&gt;hash outputs&lt;/li&gt;
&lt;li&gt;hash model versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;model_version&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;output hash&lt;/li&gt;
&lt;li&gt;enclave ID&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;on-chain.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security Considerations
&lt;/h1&gt;

&lt;p&gt;TEEs are powerful but not perfect.&lt;/p&gt;

&lt;p&gt;Potential risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;side-channel attacks&lt;/li&gt;
&lt;li&gt;hardware trust assumptions&lt;/li&gt;
&lt;li&gt;speculative execution vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, TEEs provide dramatically stronger guarantees than centralized servers.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why This Architecture Is Important
&lt;/h1&gt;

&lt;p&gt;Traditional AI systems rely on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trusted cloud providers&lt;/li&gt;
&lt;li&gt;centralized APIs&lt;/li&gt;
&lt;li&gt;backend custody&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ROFL + Sapphire introduces:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional Backend&lt;/th&gt;
&lt;th&gt;ROFL + Sapphire&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Centralized inference&lt;/td&gt;
&lt;td&gt;TEE-attested inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend wallet storage&lt;/td&gt;
&lt;td&gt;Enclave-managed keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidden execution&lt;/td&gt;
&lt;td&gt;Verifiable execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API trust assumptions&lt;/td&gt;
&lt;td&gt;Hardware-backed trust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Centralized orchestration&lt;/td&gt;
&lt;td&gt;Permissionless infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Real-World Use Cases
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Verifiable AI Trading Agents
&lt;/h2&gt;

&lt;p&gt;The enclave:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runs private strategies&lt;/li&gt;
&lt;li&gt;signs trades&lt;/li&gt;
&lt;li&gt;proves execution authenticity&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Confidential Healthcare AI
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;patient data remains private&lt;/li&gt;
&lt;li&gt;inference occurs inside TEEs&lt;/li&gt;
&lt;li&gt;outputs become auditable&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Autonomous DAO Executors
&lt;/h2&gt;

&lt;p&gt;AI agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;propose governance actions&lt;/li&gt;
&lt;li&gt;execute treasury operations&lt;/li&gt;
&lt;li&gt;prove execution legitimacy&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Multi-Chain AI Wallet Infrastructure
&lt;/h2&gt;

&lt;p&gt;One enclave can securely control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ethereum&lt;/li&gt;
&lt;li&gt;Base&lt;/li&gt;
&lt;li&gt;Arbitrum&lt;/li&gt;
&lt;li&gt;future ecosystems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without custodial bridges.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Bigger Picture
&lt;/h1&gt;

&lt;p&gt;ROFL introduces something Web3 desperately needs:&lt;/p&gt;

&lt;h1&gt;
  
  
  Trustless Off-Chain Compute
&lt;/h1&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“another oracle”&lt;/li&gt;
&lt;li&gt;“another AI chain”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verifiable computation&lt;/li&gt;
&lt;li&gt;confidential execution&lt;/li&gt;
&lt;li&gt;autonomous infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a new primitive between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;smart contracts&lt;/li&gt;
&lt;li&gt;AI infrastructure&lt;/li&gt;
&lt;li&gt;confidential computing&lt;/li&gt;
&lt;li&gt;cross-chain orchestration&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Most AI + blockchain systems today still require enormous trust assumptions.&lt;/p&gt;

&lt;p&gt;With:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sapphire&lt;/li&gt;
&lt;li&gt;ROFL&lt;/li&gt;
&lt;li&gt;TDX&lt;/li&gt;
&lt;li&gt;attestation&lt;/li&gt;
&lt;li&gt;enclave-managed wallets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;developers can finally build systems where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;computation is private&lt;/li&gt;
&lt;li&gt;outputs are verifiable&lt;/li&gt;
&lt;li&gt;execution is cryptographically authenticated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This unlocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;autonomous AI agents&lt;/li&gt;
&lt;li&gt;confidential DeFi automation&lt;/li&gt;
&lt;li&gt;verifiable off-chain compute&lt;/li&gt;
&lt;li&gt;secure cross-chain execution&lt;/li&gt;
&lt;li&gt;trusted AI infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this is likely only the beginning of what confidential compute on Oasis can enable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/" rel="noopener noreferrer"&gt;https://docs.oasis.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/rofl/" rel="noopener noreferrer"&gt;https://docs.oasis.io/build/rofl/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/rofl/workflow/" rel="noopener noreferrer"&gt;https://docs.oasis.io/build/rofl/workflow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/tools/cli/rofl/" rel="noopener noreferrer"&gt;https://docs.oasis.io/build/tools/cli/rofl/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://oasis.net/sapphire" rel="noopener noreferrer"&gt;https://oasis.net/sapphire&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>web3</category>
      <category>privacy</category>
      <category>development</category>
    </item>
    <item>
      <title>Building a Confidential AI RAG Agent on Oasis Sapphire + ROFL</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Tue, 12 May 2026 08:42:31 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-ai-rag-agent-on-oasis-sapphire-rofl-4e5e</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-ai-rag-agent-on-oasis-sapphire-rofl-4e5e</guid>
      <description>&lt;p&gt;&lt;em&gt;An advanced Oasis Network tutorial for developers who want to combine AI, confidential smart contracts, encrypted vector storage, and verifiable off-chain execution.&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Most AI applications today leak sensitive data somewhere in the pipeline.&lt;/p&gt;

&lt;p&gt;Even if the frontend is secure, prompts, embeddings, retrieval context, API calls, or inference metadata are usually visible to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud providers&lt;/li&gt;
&lt;li&gt;Backend operators&lt;/li&gt;
&lt;li&gt;Database administrators&lt;/li&gt;
&lt;li&gt;Analytics tools&lt;/li&gt;
&lt;li&gt;Infrastructure vendors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This becomes a serious issue for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Financial AI assistants&lt;/li&gt;
&lt;li&gt;Healthcare copilots&lt;/li&gt;
&lt;li&gt;Private enterprise knowledge bases&lt;/li&gt;
&lt;li&gt;Identity systems&lt;/li&gt;
&lt;li&gt;Reputation engines&lt;/li&gt;
&lt;li&gt;On-chain AI agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, we will build a &lt;strong&gt;Confidential Retrieval-Augmented Generation (RAG) Agent&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oasis Sapphire&lt;/li&gt;
&lt;li&gt;ROFL (Runtime Off-chain Logic)&lt;/li&gt;
&lt;li&gt;Encrypted storage&lt;/li&gt;
&lt;li&gt;Smart contracts&lt;/li&gt;
&lt;li&gt;Off-chain AI inference&lt;/li&gt;
&lt;li&gt;Verifiable confidential execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of storing prompts or embeddings publicly, we will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Encrypt user knowledge before storage&lt;/li&gt;
&lt;li&gt;Perform confidential retrieval&lt;/li&gt;
&lt;li&gt;Execute AI inference off-chain through ROFL&lt;/li&gt;
&lt;li&gt;Return only approved outputs on-chain&lt;/li&gt;
&lt;li&gt;Keep sensitive data hidden from everyone except the user&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  What We Are Building
&lt;/h1&gt;

&lt;p&gt;We are building a confidential AI assistant where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users upload private documents&lt;/li&gt;
&lt;li&gt;Documents are encrypted before storage&lt;/li&gt;
&lt;li&gt;Embeddings are generated off-chain&lt;/li&gt;
&lt;li&gt;Retrieval happens confidentially&lt;/li&gt;
&lt;li&gt;AI inference runs through ROFL&lt;/li&gt;
&lt;li&gt;Smart contracts manage permissions and integrity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidential EVM execution&lt;/li&gt;
&lt;li&gt;Off-chain compute&lt;/li&gt;
&lt;li&gt;AI pipelines&lt;/li&gt;
&lt;li&gt;Privacy-preserving storage&lt;/li&gt;
&lt;li&gt;Ethereum compatibility&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Final Architecture
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[User Uploads Document] --&amp;gt; B[Frontend Encrypts Data]
    B --&amp;gt; C[IPFS / Arweave Storage]
    B --&amp;gt; D[Oasis Sapphire Contract]

    D --&amp;gt; E[ROFL Worker]
    E --&amp;gt; F[Embedding Model]
    F --&amp;gt; G[Encrypted Vector Store]

    H[User Query] --&amp;gt; I[Confidential Retrieval]
    I --&amp;gt; G

    G --&amp;gt; J[Relevant Context]
    J --&amp;gt; K[LLM Inference]
    K --&amp;gt; L[Signed Response]
    L --&amp;gt; D

    D --&amp;gt; M[Verified Output Returned]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Why Sapphire Matters
&lt;/h1&gt;

&lt;p&gt;Traditional EVM chains expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contract state&lt;/li&gt;
&lt;li&gt;Function arguments&lt;/li&gt;
&lt;li&gt;Internal logic&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;User activity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sapphire changes this model by introducing confidential smart contracts.&lt;/p&gt;

&lt;p&gt;This allows developers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encrypt application state&lt;/li&gt;
&lt;li&gt;Generate secrets securely&lt;/li&gt;
&lt;li&gt;Store confidential metadata&lt;/li&gt;
&lt;li&gt;Protect AI prompts&lt;/li&gt;
&lt;li&gt;Hide retrieval logic&lt;/li&gt;
&lt;li&gt;Secure wallet-based identity systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike “privacy through frontend encryption,” Sapphire provides confidentiality at the runtime layer.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why ROFL Matters
&lt;/h1&gt;

&lt;p&gt;Large AI models cannot realistically run fully on-chain.&lt;/p&gt;

&lt;p&gt;Inference requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPUs&lt;/li&gt;
&lt;li&gt;External APIs&lt;/li&gt;
&lt;li&gt;Heavy compute&lt;/li&gt;
&lt;li&gt;Vector databases&lt;/li&gt;
&lt;li&gt;Long-running processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ROFL enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Off-chain execution&lt;/li&gt;
&lt;li&gt;Trusted compute&lt;/li&gt;
&lt;li&gt;Verifiable outputs&lt;/li&gt;
&lt;li&gt;Secure communication with Sapphire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of ROFL as a confidential execution bridge between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smart contracts&lt;/li&gt;
&lt;li&gt;AI systems&lt;/li&gt;
&lt;li&gt;External infrastructure&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Tech Stack
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Smart Contracts&lt;/td&gt;
&lt;td&gt;Solidity + Sapphire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Off-chain Compute&lt;/td&gt;
&lt;td&gt;ROFL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;IPFS / Arweave&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Embeddings&lt;/td&gt;
&lt;td&gt;Sentence Transformers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector Search&lt;/td&gt;
&lt;td&gt;ChromaDB / Qdrant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wallet&lt;/td&gt;
&lt;td&gt;RainbowKit + Wagmi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;AES-GCM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Inference&lt;/td&gt;
&lt;td&gt;OpenAI / Local LLM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Step 1: Setting Up Sapphire
&lt;/h1&gt;

&lt;p&gt;Create a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;confidential-rag-agent
&lt;span class="nb"&gt;cd &lt;/span&gt;confidential-rag-agent
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;hardhat ethers dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize Hardhat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add Sapphire configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@oasisprotocol/sapphire-hardhat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;solidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.8.20&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sapphire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://testnet.sapphire.oasis.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23295&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 2: Creating the Confidential Contract
&lt;/h1&gt;

&lt;p&gt;Our contract will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store encrypted metadata&lt;/li&gt;
&lt;li&gt;Register AI responses&lt;/li&gt;
&lt;li&gt;Verify ROFL signatures&lt;/li&gt;
&lt;li&gt;Manage access permissions
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ConfidentialRAG {

    struct Document {
        string cid;
        bytes encryptedKey;
        address owner;
    }

    mapping(uint256 =&amp;gt; Document) public documents;

    uint256 public docCounter;

    event DocumentStored(uint256 indexed id, address owner);

    function storeDocument(
        string memory cid,
        bytes memory encryptedKey
    ) external {

        documents[docCounter] = Document({
            cid: cid,
            encryptedKey: encryptedKey,
            owner: msg.sender
        });

        emit DocumentStored(docCounter, msg.sender);

        docCounter++;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 3: Encrypting User Data
&lt;/h1&gt;

&lt;p&gt;Never upload raw user data directly.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a local encryption key&lt;/li&gt;
&lt;li&gt;Encrypt the document client-side&lt;/li&gt;
&lt;li&gt;Upload encrypted data to IPFS&lt;/li&gt;
&lt;li&gt;Store only references on Sapphire&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant U as User
    participant F as Frontend
    participant I as IPFS
    participant S as Sapphire

    U-&amp;gt;&amp;gt;F: Upload document
    F-&amp;gt;&amp;gt;F: Generate AES key
    F-&amp;gt;&amp;gt;F: Encrypt document
    F-&amp;gt;&amp;gt;I: Upload encrypted file
    I--&amp;gt;&amp;gt;F: CID
    F-&amp;gt;&amp;gt;S: Store CID + encrypted key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example encryption:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;encryptFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 4: Generating Embeddings Through ROFL
&lt;/h1&gt;

&lt;p&gt;We now process the encrypted content off-chain.&lt;/p&gt;

&lt;p&gt;ROFL workers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch encrypted files&lt;/li&gt;
&lt;li&gt;Decrypt securely&lt;/li&gt;
&lt;li&gt;Generate embeddings&lt;/li&gt;
&lt;li&gt;Store vectors&lt;/li&gt;
&lt;li&gt;Return signed proofs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ROFL Flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Sapphire Event] --&amp;gt; B[ROFL Worker]
    B --&amp;gt; C[Decrypt File]
    C --&amp;gt; D[Generate Embeddings]
    D --&amp;gt; E[Vector Database]
    E --&amp;gt; F[Signed Result]
    F --&amp;gt; G[Sapphire Contract]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example Python embedding pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 5: Confidential Retrieval
&lt;/h1&gt;

&lt;p&gt;When a user asks a question:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query embedding is generated&lt;/li&gt;
&lt;li&gt;Similar vectors are retrieved&lt;/li&gt;
&lt;li&gt;Relevant context is assembled&lt;/li&gt;
&lt;li&gt;Context stays encrypted/private&lt;/li&gt;
&lt;li&gt;LLM receives only authorized information&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This prevents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt leakage&lt;/li&gt;
&lt;li&gt;Public retrieval inspection&lt;/li&gt;
&lt;li&gt;Competitor scraping&lt;/li&gt;
&lt;li&gt;Metadata deanonymization&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 6: AI Inference Layer
&lt;/h1&gt;

&lt;p&gt;The inference engine can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI APIs&lt;/li&gt;
&lt;li&gt;Local Llama models&lt;/li&gt;
&lt;li&gt;Mistral&lt;/li&gt;
&lt;li&gt;DeepSeek&lt;/li&gt;
&lt;li&gt;Confidential GPU infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prompt assembly example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Context:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;retrieved_context&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Question:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 7: Returning Verifiable Results
&lt;/h1&gt;

&lt;p&gt;ROFL signs outputs before returning them.&lt;/p&gt;

&lt;p&gt;The contract verifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorized worker&lt;/li&gt;
&lt;li&gt;Valid signature&lt;/li&gt;
&lt;li&gt;Fresh timestamp&lt;/li&gt;
&lt;li&gt;Integrity proof&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a verifiable AI pipeline.&lt;/p&gt;




&lt;h1&gt;
  
  
  Security Design
&lt;/h1&gt;

&lt;p&gt;Our architecture protects against:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Public prompt leakage&lt;/td&gt;
&lt;td&gt;Sapphire confidentiality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage snooping&lt;/td&gt;
&lt;td&gt;Client-side encryption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector DB exposure&lt;/td&gt;
&lt;td&gt;Encrypted embeddings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fake AI outputs&lt;/td&gt;
&lt;td&gt;ROFL signatures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata correlation&lt;/td&gt;
&lt;td&gt;Confidential execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend compromise&lt;/td&gt;
&lt;td&gt;Wallet authorization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Is this Architecture good?
&lt;/h1&gt;

&lt;p&gt;Most Web3 AI projects today are not truly private.&lt;/p&gt;

&lt;p&gt;They usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store prompts publicly&lt;/li&gt;
&lt;li&gt;Leak retrieval context&lt;/li&gt;
&lt;li&gt;Expose embeddings&lt;/li&gt;
&lt;li&gt;Depend entirely on centralized APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis enables something different:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confidential AI infrastructure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is a much bigger opportunity than simple “AI on blockchain” narratives. So yes, this is huge.&lt;/p&gt;




&lt;h1&gt;
  
  
  Improvements (!)
&lt;/h1&gt;

&lt;p&gt;You can extend this architecture with:&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Agent Systems
&lt;/h2&gt;

&lt;p&gt;Use multiple ROFL workers for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieval&lt;/li&gt;
&lt;li&gt;Ranking&lt;/li&gt;
&lt;li&gt;Moderation&lt;/li&gt;
&lt;li&gt;Inference&lt;/li&gt;
&lt;li&gt;Verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  zkML Integration
&lt;/h2&gt;

&lt;p&gt;Combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ROFL confidentiality&lt;/li&gt;
&lt;li&gt;ZK proofs&lt;/li&gt;
&lt;li&gt;Verifiable inference&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Token-Gated Knowledge Access
&lt;/h2&gt;

&lt;p&gt;Require NFT or token ownership before retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Chain Identity
&lt;/h2&gt;

&lt;p&gt;Use confidential reputation scores across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ethereum&lt;/li&gt;
&lt;li&gt;Base&lt;/li&gt;
&lt;li&gt;Arbitrum&lt;/li&gt;
&lt;li&gt;Solana&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Potential Real-World Use Cases
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Industry&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Healthcare&lt;/td&gt;
&lt;td&gt;Private medical copilots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finance&lt;/td&gt;
&lt;td&gt;Confidential portfolio AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gaming&lt;/td&gt;
&lt;td&gt;Hidden strategy agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Secure internal knowledge AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAOs&lt;/td&gt;
&lt;td&gt;Private governance research&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;Confidential trust scoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Performance Considerations
&lt;/h1&gt;

&lt;p&gt;AI systems are expensive.&lt;/p&gt;

&lt;p&gt;Optimize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedding caching&lt;/li&gt;
&lt;li&gt;Batched retrieval&lt;/li&gt;
&lt;li&gt;Async inference&lt;/li&gt;
&lt;li&gt;Vector compression&lt;/li&gt;
&lt;li&gt;Partial encryption&lt;/li&gt;
&lt;li&gt;Event indexing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid pushing unnecessary data on-chain.&lt;/p&gt;

&lt;p&gt;The blockchain should coordinate trust, not store entire AI workloads.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common Mistakes Developers Make
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Storing Raw AI Data On-Chain
&lt;/h2&gt;

&lt;p&gt;Never store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompts&lt;/li&gt;
&lt;li&gt;Embeddings&lt;/li&gt;
&lt;li&gt;Conversations&lt;/li&gt;
&lt;li&gt;User documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Directly on public chains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treating Frontend Encryption as Enough
&lt;/h2&gt;

&lt;p&gt;Frontend-only privacy still exposes backend operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignoring Metadata Privacy
&lt;/h2&gt;

&lt;p&gt;Even transaction patterns can reveal sensitive information.&lt;/p&gt;




&lt;h1&gt;
  
  
  Production Architecture
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph User Layer
        A[Wallet User]
        B[Next.js Frontend]
    end

    subgraph Confidential Layer
        C[Sapphire Smart Contract]
        D[ROFL Workers]
    end

    subgraph AI Layer
        E[Embedding Models]
        F[LLM Inference]
        G[Vector Database]
    end

    subgraph Storage Layer
        H[IPFS]
        I[Arweave]
    end

    A --&amp;gt; B
    B --&amp;gt; C
    B --&amp;gt; H
    C --&amp;gt; D
    D --&amp;gt; E
    D --&amp;gt; F
    D --&amp;gt; G
    H --&amp;gt; D
    I --&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Closingg Thoughts
&lt;/h1&gt;

&lt;p&gt;The next generation of AI applications will not be won by the teams with the biggest models.&lt;/p&gt;

&lt;p&gt;They will be won by the teams that solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy&lt;/li&gt;
&lt;li&gt;Data ownership&lt;/li&gt;
&lt;li&gt;Confidential computation&lt;/li&gt;
&lt;li&gt;Verifiable inference&lt;/li&gt;
&lt;li&gt;User-controlled AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis Sapphire and ROFL provide a powerful foundation for building that future.&lt;/p&gt;

&lt;p&gt;Instead of thinking about blockchain as "where data lives". think about it as a trust coordination layer for confidential AI systems.&lt;/p&gt;

&lt;p&gt;That shift changes everything.&lt;/p&gt;

&lt;h1&gt;
  
  
  Useful Resources
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Oasis Network
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://oasisprotocol.org" rel="noopener noreferrer"&gt;Oasis Network Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io" rel="noopener noreferrer"&gt;Oasis Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/sapphire/" rel="noopener noreferrer"&gt;Sapphire Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/rofl/" rel="noopener noreferrer"&gt;ROFL Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/oasisprotocol" rel="noopener noreferrer"&gt;Oasis GitHub Organization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/oasisprotocol/sapphire-paratime" rel="noopener noreferrer"&gt;Oasis Sapphire Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/tutorials/" rel="noopener noreferrer"&gt;Oasis Developer Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ethereum / Smart Contract Development
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://book.getfoundry.sh" rel="noopener noreferrer"&gt;Foundry Book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hardhat.org/docs" rel="noopener noreferrer"&gt;Hardhat Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openzeppelin.com/contracts" rel="noopener noreferrer"&gt;OpenZeppelin Contracts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.ethers.org" rel="noopener noreferrer"&gt;Ethers.js Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Decentralized Storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.ipfs.tech" rel="noopener noreferrer"&gt;IPFS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pinata.cloud" rel="noopener noreferrer"&gt;Pinata IPFS Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.arweave.org" rel="noopener noreferrer"&gt;Arweave Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web3.storage" rel="noopener noreferrer"&gt;Web3.Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AI / Vector Databases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.sbert.net" rel="noopener noreferrer"&gt;Sentence Transformers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.trychroma.com" rel="noopener noreferrer"&gt;ChromaDB Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qdrant.tech/documentation/" rel="noopener noreferrer"&gt;Qdrant Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python.langchain.com/docs/introduction/" rel="noopener noreferrer"&gt;LangChain Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.llamaindex.ai" rel="noopener noreferrer"&gt;LlamaIndex Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frontend / Wallet Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs" rel="noopener noreferrer"&gt;Next.js Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wagmi.sh" rel="noopener noreferrer"&gt;Wagmi Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rainbowkit.com/docs/introduction" rel="noopener noreferrer"&gt;RainbowKit Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security / Cryptography
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://owasp.org/www-project-smart-contract-top-10/" rel="noopener noreferrer"&gt;OWASP Smart Contract Top 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.trailofbits.com" rel="noopener noreferrer"&gt;Trail of Bits Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://consensysdiligence.github.io/smart-contract-best-practices/" rel="noopener noreferrer"&gt;Consensys Smart Contract Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rag</category>
      <category>web3</category>
      <category>ai</category>
      <category>development</category>
    </item>
    <item>
      <title>Building a Transparent Crypto Donation Ledger with Oasis Network (Beginner Tutorial)</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Mon, 20 Apr 2026 07:52:23 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-transparent-crypto-donation-ledger-with-oasis-network-beginner-tutorial-5g9h</link>
      <guid>https://dev.to/rollingindo/building-a-transparent-crypto-donation-ledger-with-oasis-network-beginner-tutorial-5g9h</guid>
      <description>&lt;p&gt;Crypto donations are great for transparency… until your donors open a block explorer and see a wall of hashes.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll build a &lt;strong&gt;simple, verifiable donation ledger&lt;/strong&gt; using Oasis Network, a blockchain that lets you combine &lt;strong&gt;privacy + transparency&lt;/strong&gt; using confidential smart contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we’re building
&lt;/h2&gt;

&lt;p&gt;A minimal system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Donations are recorded on-chain&lt;/li&gt;
&lt;li&gt;Expenses are recorded and linked to donations&lt;/li&gt;
&lt;li&gt;Anyone can see &lt;strong&gt;totals and categories (public)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sensitive details stay &lt;strong&gt;private but provable&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Oasis?
&lt;/h2&gt;

&lt;p&gt;With Oasis Sapphire (its confidential EVM):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You write &lt;strong&gt;Solidity like Ethereum&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;But contract state can be &lt;strong&gt;encrypted&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You can expose only what matters (e.g. totals)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docs: &lt;a href="https://docs.oasis.io" rel="noopener noreferrer"&gt;https://docs.oasis.io&lt;/a&gt;&lt;br&gt;
Sapphire: &lt;a href="https://oasis.net/sapphire" rel="noopener noreferrer"&gt;https://oasis.net/sapphire&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: Setup your environment
&lt;/h2&gt;

&lt;p&gt;You can use Hardhat like a normal EVM chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;oasis-ledger &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;oasis-ledger
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; hardhat
npx hardhat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add Oasis Sapphire testnet config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sapphire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://testnet.sapphire.oasis.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;solidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.8.20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Write the smart contract
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;DonationLedger.sol&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DonationLedger {

    struct Donation {
        address donor;
        uint amount;
    }

    struct Expense {
        uint amount;
        string category;
    }

    Donation[] public donations;
    Expense[] public expenses;

    uint public totalDonations;
    uint public totalSpent;

    function donate() external payable {
        donations.push(Donation(msg.sender, msg.value));
        totalDonations += msg.value;
    }

    function addExpense(uint _amount, string memory _category) external {
        expenses.push(Expense(_amount, _category));
        totalSpent += _amount;
    }

    function getBalance() public view returns (uint) {
        return totalDonations - totalSpent;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is intentionally simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully transparent totals&lt;/li&gt;
&lt;li&gt;Expand later with private metadata (Oasis advantage)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Deploy to Oasis Sapphire
&lt;/h2&gt;

&lt;p&gt;Create a deploy script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DonationLedger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForDeployment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deployed to:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat run scripts/deploy.js &lt;span class="nt"&gt;--network&lt;/span&gt; sapphire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Interact with the contract
&lt;/h2&gt;

&lt;p&gt;Example (Hardhat console):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat console &lt;span class="nt"&gt;--network&lt;/span&gt; sapphire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DonationLedger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DEPLOYED_ADDRESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// donate&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;donate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// add expense&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addExpense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseEther&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.05&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;food&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// check totals&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;totalDonations&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;totalSpent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Build a simple frontend (optional)
&lt;/h2&gt;

&lt;p&gt;Even a basic UI can transform usability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total donations&lt;/li&gt;
&lt;li&gt;Total spent&lt;/li&gt;
&lt;li&gt;Remaining balance&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Add buttons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Donate”&lt;/li&gt;
&lt;li&gt;“Add Expense” (admin)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;ethers.js&lt;/li&gt;
&lt;li&gt;wagmi&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 6: Where Oasis shines (upgrade idea)
&lt;/h2&gt;

&lt;p&gt;Right now everything is public.&lt;/p&gt;

&lt;p&gt;With Oasis, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store &lt;strong&gt;private expense metadata&lt;/strong&gt; (e.g. invoice hashes)&lt;/li&gt;
&lt;li&gt;Keep donor notes confidential&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reveal only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;totals&lt;/li&gt;
&lt;li&gt;categories&lt;/li&gt;
&lt;li&gt;proofs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is done using &lt;strong&gt;confidential state inside Sapphire contracts&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;Instead of showing donors this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Here’s our wallet, go check Etherscan”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You show them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;€100k raised&lt;/li&gt;
&lt;li&gt;€40k spent on food&lt;/li&gt;
&lt;li&gt;€20k spent on shelter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And every number is still &lt;strong&gt;verifiable on-chain&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Using Oasis, you can build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transparent donation systems&lt;/li&gt;
&lt;li&gt;Clean public dashboards&lt;/li&gt;
&lt;li&gt;Private but provable accounting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without leaving the EVM ecosystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://oasis.net" rel="noopener noreferrer"&gt;https://oasis.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io" rel="noopener noreferrer"&gt;https://docs.oasis.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://oasis.net/sapphire" rel="noopener noreferrer"&gt;https://oasis.net/sapphire&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Privacy-Preserving Federated Learning on Oasis Network (Sapphire ParaTime)</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Thu, 16 Apr 2026 09:21:10 +0000</pubDate>
      <link>https://dev.to/rollingindo/privacy-preserving-federated-learning-on-oasis-network-sapphire-paratime-5490</link>
      <guid>https://dev.to/rollingindo/privacy-preserving-federated-learning-on-oasis-network-sapphire-paratime-5490</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we’ll build an &lt;strong&gt;advanced machine learning system&lt;/strong&gt; on the Oasis Network using &lt;strong&gt;confidential smart contracts (Sapphire ParaTime)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of common examples (NFT auctions, oracles, etc.), we’ll implement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Confidential Federated Learning with On-Chain Aggregation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The key points on why this tutorial is really on point
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Data &lt;strong&gt;never leaves clients in plaintext&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Model updates are &lt;strong&gt;encrypted&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Aggregation happens &lt;strong&gt;inside TEEs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fully &lt;strong&gt;privacy-preserving collaborative AI&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clients (Hospitals / Devices)
|
| Encrypted Gradients
v
+-----------------------------+
| Oasis Sapphire ParaTime     |
|                             |
| - Decrypt gradients (TEE)   |
| - Aggregate updates         |
| - Update global model       |
+-----------------------------+
|
v
Clients download updated model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Oasis Sapphire (Confidential EVM)&lt;/li&gt;
&lt;li&gt;Solidity&lt;/li&gt;
&lt;li&gt;Python (PyTorch)&lt;/li&gt;
&lt;li&gt;Web3.py / ethers.js&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Smart Contract (Confidential Aggregator)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ConfidentialFederatedLearning {

    struct Update {
        bytes encryptedGradient;
    }

    mapping(address =&amp;gt; Update) public updates;
    address[] public participants;

    bytes public globalModel;

    function submitUpdate(bytes calldata encryptedGradient) external {
        if (updates[msg.sender].encryptedGradient.length == 0) {
            participants.push(msg.sender);
        }

        updates[msg.sender] = Update(encryptedGradient);
    }

    function aggregate() external {
        bytes[] memory gradients = new bytes[](participants.length);

        for (uint i = 0; i &amp;lt; participants.length; i++) {
            gradients[i] = updates[participants[i]].encryptedGradient;
        }

        // Executed confidentially in Oasis Sapphire
        globalModel = confidentialAggregate(gradients);

        delete participants;
    }

    function confidentialAggregate(bytes[] memory gradients)
        internal
        returns (bytes memory)
    {
        // Placeholder logic (runs inside TEE)
        return abi.encodePacked("aggregated_model");
    }

    function getModel() external view returns (bytes memory) {
        return globalModel;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Local Training (PyTorch)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.nn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.optim&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;optim&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;optimizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SGD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;loss_fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MSELoss&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loss_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Serialize + Encrypt Updates
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serialize_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Send Updates to Oasis
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Web3&lt;/span&gt;

&lt;span class="n"&gt;w3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://testnet.sapphire.oasis.dev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;contract_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;abi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...]&lt;/span&gt;  &lt;span class="c1"&gt;# compiled ABI
&lt;/span&gt;
&lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;contract_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encrypted_gradient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;encrypted_gradient&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build_transaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nonce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_transaction_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gas&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000000&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tx_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_raw_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rawTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tx_hash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Trigger Aggregation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;trigger_aggregation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;build_transaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nonce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_transaction_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gas&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000000&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tx_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_raw_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rawTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tx_hash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Fetch Global Model
&lt;/h2&gt;

&lt;p&gt;Once the Oasis network has aggregated all encrypted updates inside the confidential execution environment, clients can retrieve the updated global model.&lt;/p&gt;

&lt;p&gt;This step completes the federated learning cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_global_model&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;model_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getModel&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model_bytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, the returned model_bytes represent the latest version of the collaboratively trained model.&lt;/p&gt;

&lt;p&gt;In a real production system, this data would typically be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A serialized PyTorch/TensorFlow model&lt;/li&gt;
&lt;li&gt;Or compressed weight tensors&lt;/li&gt;
&lt;li&gt;Or even encrypted checkpoints for secure distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key idea is that the model itself becomes a shared decentralized artifact, continuously evolving without exposing any participant’s raw data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Use Case #1: Healthcare AI Across Hospitals
&lt;/h2&gt;

&lt;p&gt;One of the most impactful applications of this architecture is in healthcare machine learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hospitals often cannot share patient data due to strict regulations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GDPR (Europe)&lt;/li&gt;
&lt;li&gt;HIPAA (United States)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a major bottleneck:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each hospital has only a partial view of reality.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disease prediction models are weaker&lt;/li&gt;
&lt;li&gt;Rare conditions are underrepresented&lt;/li&gt;
&lt;li&gt;AI generalization suffers significantly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution Using Oasis Confidential ML
&lt;/h2&gt;

&lt;p&gt;With Oasis Sapphire, each hospital:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trains a local model on private patient data&lt;/li&gt;
&lt;li&gt;Extracts gradients or weight updates&lt;/li&gt;
&lt;li&gt;Encrypts them automatically via confidential execution&lt;/li&gt;
&lt;li&gt;Sends updates to the shared aggregation contract&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Oasis network then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggregates updates inside a Trusted Execution Environment (TEE)&lt;/li&gt;
&lt;li&gt;Prevents any party from seeing individual contributions&lt;/li&gt;
&lt;li&gt;Produces a globally improved model&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Works So Well
&lt;/h2&gt;

&lt;p&gt;Instead of centralizing sensitive data, we are centralizing intelligence, not information.&lt;/p&gt;

&lt;p&gt;This shift has massive implications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Patient data never leaves hospitals&lt;/li&gt;
&lt;li&gt;Research collaboration becomes frictionless&lt;/li&gt;
&lt;li&gt;Models benefit from global diversity of data&lt;/li&gt;
&lt;li&gt;Compliance is preserved by design, not enforcement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, this enables systems like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Early disease detection models trained across continents&lt;/li&gt;
&lt;li&gt;Rare condition classifiers with global coverage&lt;/li&gt;
&lt;li&gt;Personalized treatment recommendation systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Use Case #2: Cross-Bank Fraud Detection Network
&lt;/h2&gt;

&lt;p&gt;Another powerful application is in financial systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Banks face a paradox:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fraud detection improves with shared data&lt;/li&gt;
&lt;li&gt;But banks cannot share customer transaction data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fragmented fraud intelligence&lt;/li&gt;
&lt;li&gt;Delayed detection of new fraud patterns&lt;/li&gt;
&lt;li&gt;High false-negative rates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution: Confidential Federated Fraud Learning
&lt;/h2&gt;

&lt;p&gt;Using this architecture:&lt;/p&gt;

&lt;p&gt;Each bank:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trains a fraud detection model locally&lt;/li&gt;
&lt;li&gt;Learns patterns specific to its users&lt;/li&gt;
&lt;li&gt;Computes gradient updates privately&lt;/li&gt;
&lt;li&gt;Submits encrypted updates to Oasis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggregates all updates confidentially&lt;/li&gt;
&lt;li&gt;Produces a global fraud detection model&lt;/li&gt;
&lt;li&gt;Sends improved model back to all participants&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Is Powerful
&lt;/h2&gt;

&lt;p&gt;The system effectively creates a collective fraud intelligence network without violating privacy.&lt;/p&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster detection of new fraud patterns&lt;/li&gt;
&lt;li&gt;Cross-bank intelligence without data exposure&lt;/li&gt;
&lt;li&gt;Stronger anomaly detection models&lt;/li&gt;
&lt;li&gt;Reduced financial losses across the ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Full Training Loop Visualization
&lt;/h2&gt;

&lt;p&gt;Let’s now put everything together into a full lifecycle loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROUND t (Federated Learning Cycle)

Client A (Hospital / Bank / Device)
    └── Train local model
    └── Compute gradients
    └── Encrypt updates
    └── Submit to Oasis

Client B
    └── Train
    └── Encrypt
    └── Submit

Client C
    └── Train
    └── Encrypt
    └── Submit

                ↓

     🔐 Oasis Sapphire TEE Layer
     ┌──────────────────────────────┐
     │ 1. Decrypt securely          │
     │ 2. Aggregate updates         │
     │ 3. Apply weighting (optional)│
     │ 4. Update global model       │
     └──────────────────────────────┘

                ↓

📦 Global Model Updated On-Chain

                ↓

Clients Pull Latest Model → Next Training Round
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Enhancements (Production-Level Thinking)
&lt;/h2&gt;

&lt;p&gt;At this stage, the system is already functional, but real-world deployments require additional sophistication.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Differential Privacy Layer
&lt;/h2&gt;

&lt;p&gt;To further strengthen privacy guarantees, we can inject controlled noise into model updates before submission.&lt;/p&gt;

&lt;p&gt;This ensures that even in edge cases, individual contributions cannot be reverse-engineered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_noise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;epsilon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;noisy_weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;noise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randn_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;epsilon&lt;/span&gt;
        &lt;span class="n"&gt;noisy_weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;noise&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;noisy_weights&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This technique is widely used in privacy-preserving ML systems and complements Oasis’s confidential execution layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Weighted Aggregation Strategy
&lt;/h2&gt;

&lt;p&gt;Not all participants contribute equally.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hospital with 1M patients should have more influence than one with 1K&lt;/li&gt;
&lt;li&gt;A bank with higher transaction volume should contribute more signal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of simple averaging, we apply weighted aggregation.&lt;/p&gt;

&lt;p&gt;This can be implemented inside the confidential contract layer to ensure fairness while preserving privacy.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Incentive Mechanism for Participants
&lt;/h2&gt;

&lt;p&gt;A real decentralized ML system must also include economic incentives.&lt;/p&gt;

&lt;p&gt;You can design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token rewards per valid update&lt;/li&gt;
&lt;li&gt;Slashing for malicious or low-quality gradients&lt;/li&gt;
&lt;li&gt;Reputation scoring for long-term contributors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This transforms the system into a self-sustaining AI network economy.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Hybrid ZK + Confidential ML Systems
&lt;/h2&gt;

&lt;p&gt;An even more advanced extension is combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero-Knowledge Proofs (ZKPs)&lt;/li&gt;
&lt;li&gt;Oasis confidential compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows participants to prove properties like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'My model was trained correctly'&lt;/li&gt;
&lt;li&gt;'My data distribution satisfies constraints'&lt;/li&gt;
&lt;li&gt;'No poisoning attacks were introduced'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without revealing any underlying data.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Why Does This Architecture Matter
&lt;/h2&gt;

&lt;p&gt;What we built here is not just a federated learning system.&lt;/p&gt;

&lt;p&gt;It represents a shift in how machine learning systems are designed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From centralized data collection → to decentralized intelligence collaboration&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oasis enables something very rare in blockchain systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Computation on private data&lt;/li&gt;
&lt;li&gt;Without exposing the data itself&lt;/li&gt;
&lt;li&gt;While still preserving verifiability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This unlocks entirely new categories of AI systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy-first foundation models&lt;/li&gt;
&lt;li&gt;Cross-organization collaborative AI&lt;/li&gt;
&lt;li&gt;Decentralized intelligence networks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where You Can Take This Next
&lt;/h2&gt;

&lt;p&gt;If you want to push this further (and it gets very interesting), you can extend this system into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidential LLM fine-tuning networks&lt;/li&gt;
&lt;li&gt;Multi-agent decentralized reinforcement learning&lt;/li&gt;
&lt;li&gt;Privacy-preserving recommendation systems&lt;/li&gt;
&lt;li&gt;Genomics ML across research institutions&lt;/li&gt;
&lt;li&gt;Token-incentivized AI training marketplaces&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thought
&lt;/h2&gt;

&lt;p&gt;The combination of federated learning + confidential execution fundamentally changes what is possible in machine learning systems.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where do we store the data?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We now ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do we learn from data without ever seeing it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that is exactly the space where Oasis Network becomes powerful.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>privacy</category>
      <category>machinelearning</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a Confidential Sealed-Bid NFT Auction on Oasis Sapphire</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Fri, 20 Mar 2026 10:36:48 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-sealed-bid-nft-auction-on-oasis-sapphire-j1m</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-sealed-bid-nft-auction-on-oasis-sapphire-j1m</guid>
      <description>&lt;p&gt;Ever wanted to run a blockchain auction without revealing bids until the end?&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll build a sealed-bid NFT auction on Oasis Sapphire, where bids are fully encrypted, events are private, and the winner is publicly verifiable, perfect for NFT drops, private DeFi auctions, or confidential governance.&lt;/p&gt;

&lt;p&gt;We’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Sapphire for privacy-first auctions&lt;/li&gt;
&lt;li&gt;Smart contract design for sealed-bid auctions&lt;/li&gt;
&lt;li&gt;Off-chain bid commitment and encryption&lt;/li&gt;
&lt;li&gt;Encrypted events on Sapphire&lt;/li&gt;
&lt;li&gt;React + ethers.js frontend integration&lt;/li&gt;
&lt;li&gt;Testing, deployment, and advanced tips&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you’ll have a production-ready privacy-preserving auction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Why Privacy Matters on Blockchain Auctions
&lt;/h2&gt;

&lt;p&gt;Traditional Ethereum-style auctions expose bids as soon as they hit the blockchain. This causes issues like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Front-running attacks: Bots see your bid and immediately outbid you&lt;/li&gt;
&lt;li&gt;Loss of privacy: High-value bids are visible to competitors&lt;/li&gt;
&lt;li&gt;Manipulation risk: Public bids allow strategic manipulation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis Sapphire solves this by offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidential calldata: Input values are encrypted&lt;/li&gt;
&lt;li&gt;Encrypted events: Logs can be private but verifiable&lt;/li&gt;
&lt;li&gt;EVM compatibility: Solidity contracts work with minimal changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the result: You can build auctions where nobody knows your bid until reveal, yet the outcome is fully trustless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐        submitBid(commitment)       ┌───────────────┐
│   Bidder A  │ ────────────────────────────────&amp;gt;  │  SealedAuction│
└─────────────┘                                    │ (encrypted)   │
┌─────────────┐        submitBid(commitment)       │               │
│   Bidder B  │ ────────────────────────────────&amp;gt;  │               │
└─────────────┘                                    └─────┬─────────┘
                                                      │
                                                      ▼
                                        ┌───────────────────────┐
                                        │ Reveal Phase          │
                                        │ - submit value+salt   │
                                        │ - verify commitment   │
                                        │ - determine winner    │
                                        └─────────┬─────────────┘
                                                  │
                                                  ▼
                                       ┌─────────────────────────┐
                                       │ Winner + Final Price    │
                                       │ (publicly verifiable)   │
                                       └─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 2: Project Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create Hardhat Project
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;sapphire-nft-auction
&lt;span class="nb"&gt;cd &lt;/span&gt;sapphire-nft-auction
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; hardhat @nomiclabs/hardhat-ethers ethereum-waffle chai
npm &lt;span class="nb"&gt;install&lt;/span&gt; @oasisprotocol/sapphire-paratime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Configure Hardhat for Sapphire Testnet
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@nomiclabs/hardhat-ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@oasisprotocol/sapphire-paratime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;solidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.8.20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sapphire_testnet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://testnet.sapphire.oasis.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23295&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// your testnet wallet&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you fund your testnet wallet with some ROSE test tokens (&lt;a href="https://faucet.testnet.oasis.dev/" rel="noopener noreferrer"&gt;Testnet Faucet&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: Smart Contract Architecture
&lt;/h2&gt;

&lt;p&gt;We’ll implement a sealed-bid auction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;submitBid&lt;/strong&gt;: Accepts encrypted bid commitments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reveal&lt;/strong&gt;: Checks commitments, reveals bids, determines winner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;events&lt;/strong&gt;: Logs encrypted bid submissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create &lt;strong&gt;contracts/SealedAuction.sol&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;SPDX&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;License&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MIT&lt;/span&gt;
&lt;span class="n"&gt;pragma&lt;/span&gt; &lt;span class="n"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="n"&gt;SealedAuction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Bid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bytes32&lt;/span&gt; &lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;revealed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;finalPrice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Bid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;biddingEnd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;revealEnd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;BidEncrypted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;indexed&lt;/span&gt; &lt;span class="n"&gt;bidder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes32&lt;/span&gt; &lt;span class="k"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Revealed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;indexed&lt;/span&gt; &lt;span class="n"&gt;bidder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;_biddingTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;_revealTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;biddingEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;_biddingTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;revealEnd&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;biddingEnd&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;_revealTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;submitBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes32&lt;/span&gt; &lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;biddingEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"Bidding closed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Bid&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;revealed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="n"&gt;emit&lt;/span&gt; &lt;span class="n"&gt;BidEncrypted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes32&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;biddingEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"Reveal not started"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;revealEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"Reveal ended"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Bid&lt;/span&gt; &lt;span class="k"&gt;storage&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commitment&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encodePacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nv"&gt;"Bad reveal"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revealed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"Already revealed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revealed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;emit&lt;/span&gt; &lt;span class="n"&gt;Revealed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;finalPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;finalPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only the hash of the bid is stored on-chain, not the bid itself.&lt;/li&gt;
&lt;li&gt;Bids remain private until reveal().&lt;/li&gt;
&lt;li&gt;Events can optionally be encrypted for extra confidentiality.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 4: Off-Chain Bid Commitment
&lt;/h2&gt;

&lt;p&gt;Before submitting a bid, you generate a commitment hash off-chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCommitment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bidValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commitment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultAbiCoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uint256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bytes32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bidValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCommitment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Commitment:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Salt:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Salt prevents brute-force attacks on commitments&lt;/li&gt;
&lt;li&gt;The commitment hash is sent on-chain using submitBid()
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bidder Client
+-----------------+
| bidValue + salt |
+--------+--------+
         |
         | keccak256 -&amp;gt; commitment
         v
+-----------------+     encrypted calldata    +------------------------+
| submitBid()     | ------------------------&amp;gt; | SealedAuction Contract |
| (commitment)    |                           | stores only commitment |
+-----------------+                           |                        |
                                              +-----------+------------+
                                                          |
                                               encrypted event emitted
                                                          |
                                                          v
                                               Off-chain listener/frontend
                                               (decrypts if authorized)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 5: Submitting &amp;amp; Revealing Bids
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auctionContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Wait until bidding period ends&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auctionContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bidValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Sapphire, you can use encrypted calldata so even miners/validators don’t see the bid during submission.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 6: Adding NFT Rewards
&lt;/h2&gt;

&lt;p&gt;Let’s take it a step further: the winner receives an NFT. Add this to your contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;interface&lt;/span&gt; &lt;span class="n"&gt;IERC721&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;safeMint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;external&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;IERC721&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;rewardNFT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;_nft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;rewardNFT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IERC721&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_nft&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="k"&gt;After&lt;/span&gt; &lt;span class="n"&gt;reveal&lt;/span&gt; &lt;span class="n"&gt;ends&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;distributeNFT&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;revealEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"Reveal not ended"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;"No winner yet"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rewardNFT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;safeMint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 7: Encrypted Events on Sapphire (Advanced)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EncryptedBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;indexed&lt;/span&gt; &lt;span class="n"&gt;bidder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes32&lt;/span&gt; &lt;span class="n"&gt;encryptedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sapphire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encodePacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bidValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;emit&lt;/span&gt; &lt;span class="n"&gt;EncryptedBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Only the bidder can decrypt the event off-chain&lt;/li&gt;
&lt;li&gt;Useful for building off-chain bid monitoring tools without revealing bids publicly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 8: Frontend Integration (React + ethers.js)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generate bid commitment in React form&lt;/li&gt;
&lt;li&gt;Call submitBid(commitment) on click&lt;/li&gt;
&lt;li&gt;Reveal after auction ends&lt;/li&gt;
&lt;li&gt;Optional: use encrypted events to show notifications without revealing bid amounts
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCommitment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auctionContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add a timer for bidding and reveal periods&lt;/li&gt;
&lt;li&gt;Use ethers.js and MetaMask for transactions&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 9: Hardhat Testing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SealedAuction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepts bids and picks winner correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Auction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SealedAuction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bidA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1001salt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bidB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2002salt2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bidA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bidB&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evm_increaseTime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatBytes32String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;salt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatBytes32String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;salt2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalPrice&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2002&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 10: Deploying to Sapphire Testnet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat run scripts/deploy.js &lt;span class="nt"&gt;--network&lt;/span&gt; sapphire_testnet

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Auction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SealedAuction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deployed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Auction deployed at&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 11: Next-Level Ideas you could implement
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Build a full frontend auction dApp&lt;/li&gt;
&lt;li&gt;Integrate off-chain listeners for encrypted events&lt;/li&gt;
&lt;li&gt;Add ZK proofs for bid fairness without revealing bids&lt;/li&gt;
&lt;li&gt;Support multi-item auctions or batch NFT rewards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have a fully functional, privacy-preserving NFT auction on Oasis Sapphire. Bids are private, events can be private, and the winner is verifiable. Perfect for real-world confidential DeFi and NFT applications!&lt;/p&gt;

</description>
      <category>nft</category>
      <category>privacy</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Building a Privacy-Preserving AI Oracle on Oasis Sapphire</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Fri, 13 Mar 2026 08:41:00 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-privacy-preserving-ai-oracle-on-oasis-sapphire-1ibd</link>
      <guid>https://dev.to/rollingindo/building-a-privacy-preserving-ai-oracle-on-oasis-sapphire-1ibd</guid>
      <description>&lt;p&gt;Recently, I built a &lt;a href="https://dev.to/rollingindo/building-a-cross-chain-confidential-trust-score-oracle-with-oasis-sapphire-opl-rofl-39n8"&gt;cross-chain confidential Trust Score Oracle using Sapphire ParaTime&lt;/a&gt;, Oasis Privacy Layer, and ROFL on the Oasis Network. That project focused on computing a reputation score across multiple blockchains while keeping the scoring logic private.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll explore a different but related use case: building a privacy-preserving AI inference oracle.&lt;/p&gt;

&lt;p&gt;Instead of calculating a trust score from on-chain activity, we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run an AI model privately&lt;/li&gt;
&lt;li&gt;process sensitive user data&lt;/li&gt;
&lt;li&gt;return verifiable results on-chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is extremely useful for applications like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;private credit scoring&lt;/li&gt;
&lt;li&gt;confidential trading signals&lt;/li&gt;
&lt;li&gt;healthcare ML analytics&lt;/li&gt;
&lt;li&gt;AI-powered DeFi risk models&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Privacy Matters for AI on Blockchain
&lt;/h2&gt;

&lt;p&gt;Most blockchains are fully transparent. Every transaction and input is publicly visible.&lt;/p&gt;

&lt;p&gt;That creates a major problem for AI applications.&lt;/p&gt;

&lt;p&gt;Example: a credit scoring system.&lt;/p&gt;

&lt;p&gt;Inputs might include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;income&lt;/li&gt;
&lt;li&gt;transaction history&lt;/li&gt;
&lt;li&gt;loan repayment data&lt;/li&gt;
&lt;li&gt;identity attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this can safely be published on a public blockchain.&lt;/p&gt;

&lt;p&gt;This is where the confidential compute model of Oasis Network becomes powerful.&lt;/p&gt;

&lt;p&gt;It allows smart contracts to execute encrypted computations inside secure enclaves.&lt;/p&gt;




&lt;h2&gt;
  
  
  Oasis Network Architecture
&lt;/h2&gt;

&lt;p&gt;The Oasis architecture separates consensus from computation, which allows specialized runtimes for different workloads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------------------------------------+
|                 Applications                   |
+------------------------------------------------+
|                ParaTime Layer                  |
|------------------------------------------------|
|  Sapphire (Confidential EVM)                   |
|  Emerald (EVM Compatible)                      |
|  Custom Runtimes                               |
+------------------------------------------------+
|              Consensus Layer                   |
|           Proof-of-Stake Validators            |
+------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this tutorial we use Sapphire ParaTime, which provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EVM compatibility&lt;/li&gt;
&lt;li&gt;encrypted state&lt;/li&gt;
&lt;li&gt;confidential smart contracts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What We Will Build
&lt;/h2&gt;

&lt;p&gt;We will build a Confidential AI Oracle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Confidential AI Oracle System

       +-------------------+
       |   User Wallet     |
       | (encrypted data)  |
       +---------+---------+
                 |
                 v
        +---------------------+
        | Sapphire Smart      |
        | Contract            |
        | (confidential EVM)  |
        +---------+-----------+
                  |
                  v
        +---------------------+
        | ROFL Oracle Worker  |
        |---------------------|
        | Secure Enclave      |
        | AI Model Inference  |
        +---------+-----------+
                  |
                  v
        +---------------------+
        | Signed Result       |
        | returned on-chain   |
        +---------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive encrypted user data&lt;/li&gt;
&lt;li&gt;Run an AI model inside a secure environment&lt;/li&gt;
&lt;li&gt;Produce a prediction&lt;/li&gt;
&lt;li&gt;Store only the result on-chain&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Wallet
     |
     v
Encrypted Data
     |
     v
Sapphire Smart Contract
     |
     v
ROFL Worker
     |
     v
AI Model Inference
     |
     v
Signed Result
     |
     v
Smart Contract Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real Use Case: Private Credit Scoring
&lt;/h2&gt;

&lt;p&gt;Imagine a DeFi lending protocol.&lt;/p&gt;

&lt;p&gt;It wants to compute a credit score before issuing a loan.&lt;/p&gt;

&lt;p&gt;But the borrower’s financial data must remain private.&lt;/p&gt;

&lt;p&gt;Traditional blockchain flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User -&amp;gt; Smart Contract -&amp;gt; Public Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everyone can see the input.&lt;/p&gt;

&lt;p&gt;With confidential computation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Data (encrypted)
        |
        v
Confidential Runtime
        |
        v
AI Model
        |
        v
Credit Score Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the result is visible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Repository Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oasis-private-ai-oracle/

contracts/
  CreditOracle.sol

oracle/
  worker.py
  model.py
  enclave_runner.py

scripts/
  deploy.js

frontend/
  submit-data.ts

README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Smart Contract (Sapphire)
&lt;/h2&gt;

&lt;p&gt;Example confidential oracle contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;solidity&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="nx"&gt;CreditOracle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;oracle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;struct&lt;/span&gt; &lt;span class="nx"&gt;ScoreRecord&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ScoreRecord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="nc"&gt;ScoreUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;indexed&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;_oracle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;oracle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_oracle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;submitScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;calldata&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;external&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ScoreRecord&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;emit&lt;/span&gt; &lt;span class="nc"&gt;ScoreUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;uint256&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;internal&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="nx"&gt;bytes32&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encodePacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;bytes32&lt;/span&gt; &lt;span class="nx"&gt;ethSigned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;keccak256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encodePacked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;x19Ethereum Signed Message:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;32&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;message&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes32&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytes32&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint8&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;splitSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="nx"&gt;recovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ecrecover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ethSigned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;recovered&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;oracle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;splitSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;internal&lt;/span&gt;
        &lt;span class="nx"&gt;pure&lt;/span&gt;
        &lt;span class="nf"&gt;returns &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes32&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytes32&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint8&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid signature length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;r&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;mload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verifies oracle signatures&lt;/li&gt;
&lt;li&gt;prevents tampering&lt;/li&gt;
&lt;li&gt;stores final scores&lt;/li&gt;
&lt;li&gt;event logging&lt;/li&gt;
&lt;li&gt;timestamped scores&lt;/li&gt;
&lt;li&gt;full signature verification&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Oracle Worker
&lt;/h2&gt;

&lt;p&gt;The oracle runs the AI model and signs the result.&lt;/p&gt;

&lt;p&gt;Example Python worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Web3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;predict_score&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;eth_account&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;

&lt;span class="n"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sapphire.oasis.io&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PRIVATE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_ORACLE_PRIVATE_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;contract_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEPLOYED_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;contract_abi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CreditOracleABI.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;w3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RPC_URL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;contract_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;abi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;contract_abi&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_oracle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;predict_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;solidity_keccak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uint256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Web3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_hash_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build_transaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nonce&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_transaction_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;signed_tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tx_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_raw_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signed_tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rawTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tx_hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real deployment this worker would run inside:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a secure enclave&lt;/li&gt;
&lt;li&gt;confidential compute environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;computes the AI score&lt;/li&gt;
&lt;li&gt;signs it&lt;/li&gt;
&lt;li&gt;sends a transaction on-chain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Example AI Model
&lt;/h2&gt;

&lt;p&gt;A simplified scoring function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;income&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;income&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;debt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;debt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;history&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;income&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;debt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does some real feature normalization and gives realistic scoring output&lt;/p&gt;

&lt;p&gt;In production you could replace this with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XGBoost&lt;/li&gt;
&lt;li&gt;LightGBM&lt;/li&gt;
&lt;li&gt;neural networks&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Install development tools&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; hardhat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure Sapphire network&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;sapphire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://sapphire.oasis.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;chainId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23294&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy contract&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat run scripts/deploy.js &lt;span class="nt"&gt;--network&lt;/span&gt; sapphire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  End-to-End Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User submits encrypted data
        |
        v
Oracle decrypts inside secure compute
        |
        v
AI model runs prediction
        |
        v
Oracle signs result
        |
        v
Smart contract stores score
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Architecture Is Powerful
&lt;/h2&gt;

&lt;p&gt;Without privacy infrastructure, AI + blockchain rarely works because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;training data is private&lt;/li&gt;
&lt;li&gt;models are proprietary&lt;/li&gt;
&lt;li&gt;inputs contain sensitive information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using confidential runtimes on Oasis Network enables a new class of applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;private DeFi analytics&lt;/li&gt;
&lt;li&gt;confidential AI marketplaces&lt;/li&gt;
&lt;li&gt;encrypted healthcare ML&lt;/li&gt;
&lt;li&gt;secure financial scoring&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Possible next additions
&lt;/h2&gt;

&lt;p&gt;Possible extensions to this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add zero-knowledge proofs for inference&lt;/li&gt;
&lt;li&gt;build a data marketplace&lt;/li&gt;
&lt;li&gt;enable federated learning&lt;/li&gt;
&lt;li&gt;create private trading signals&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The combination of blockchain verification and confidential computation unlocks entirely new types of applications.&lt;/p&gt;

&lt;p&gt;With runtimes like Sapphire ParaTime on the Oasis Network, developers can build systems where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data remains private&lt;/li&gt;
&lt;li&gt;computation is verifiable&lt;/li&gt;
&lt;li&gt;results are trustless&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This opens the door to privacy-first AI in Web3.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>privacy</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Cross-Chain Confidential "Trust Score Oracle" with Oasis Sapphire + OPL + ROFL</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Sun, 22 Feb 2026 08:15:46 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-cross-chain-confidential-trust-score-oracle-with-oasis-sapphire-opl-rofl-39n8</link>
      <guid>https://dev.to/rollingindo/building-a-cross-chain-confidential-trust-score-oracle-with-oasis-sapphire-opl-rofl-39n8</guid>
      <description>&lt;p&gt;This tutorial shows how to add privacy + verifiable off-chain compute to any EVM dApp by combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sapphire: Oasis’ confidential EVM (encrypted calldata + confidential contract state).&lt;/li&gt;
&lt;li&gt;OPL (Oasis Privacy Layer): message bridge pattern to “bolt privacy onto” existing EVM apps by offloading sensitive logic to Sapphire.&lt;/li&gt;
&lt;li&gt;ROFL (Runtime Off-Chain Logic): a TEE-managed app runtime (containers or single binaries) with secrets stored via built-in KMS and on-chain management through Sapphire.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real use case
&lt;/h2&gt;

&lt;p&gt;You run a lending/access-control dApp on a Home chain (Ethereum/L2). You want a wallet "Trust Score" derived from sensitive signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;private allowlists (KYC provider, internal risk flags),&lt;/li&gt;
&lt;li&gt;off-chain identity reputation (GitHub activity, paid subscription, enterprise customer),&lt;/li&gt;
&lt;li&gt;model inference results (fraud classifier).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t want to leak:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;raw signals,&lt;/li&gt;
&lt;li&gt;model features,&lt;/li&gt;
&lt;li&gt;user identity mappings,&lt;/li&gt;
&lt;li&gt;or scoring logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Design goal:&lt;br&gt;
Home chain contract gets a public, verifiable decision (e.g., score bucket/allow/deny/rate tier) while sensitive scoring happens privately and verifiably.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;High-level diagram&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrg6fopnb48s2t90ccej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrg6fopnb48s2t90ccej.png" alt=" " width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What each piece does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Home chain contract&lt;/strong&gt;: minimal "public" state, accepts final decision and enforces it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sapphire contract&lt;/strong&gt; (the enclave): holds private state (scores, salts, user-to-signal mappings), verifies authorized sources, emits public commitments only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROFL app&lt;/strong&gt;: runs your scoring code in a TEE; keeps API keys/model weights/feature rules in ROFL secrets; posts updates on-chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ROFL apps are explicitly designed to run in TEEs and be managed via Sapphire, with built-in secrets support.&lt;/p&gt;
&lt;h2&gt;
  
  
  Repo blueprint
&lt;/h2&gt;

&lt;p&gt;Monorepo layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oasis-trustscore-oracle/
  README.md
  .env.example

  contracts/
    hardhat.config.ts
    package.json
    contracts/
      HomeHost.sol
      SapphireEnclave.sol
    scripts/
      deploy-home.ts
      deploy-sapphire.ts
      link-endpoints.ts
    tasks/
      send-opl-message.ts

  rofl/
    compose.yaml
    rofl.yaml
    app/
      Dockerfile
      requirements.txt
      main.py
      scorer.py
      signals.py

  diagrams/
    architecture.mmd
    sequence.mmd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Networks &amp;amp; prerequisites
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sapphire Testnet settings (for dev)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From Oasis docs, Sapphire Testnet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RPC: &lt;a href="https://testnet.sapphire.oasis.io" rel="noopener noreferrer"&gt;https://testnet.sapphire.oasis.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chain ID (decimal): 23295&lt;/li&gt;
&lt;li&gt;WSS: wss://testnet.sapphire.oasis.io/ws&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll also want the Sapphire wrapper for encrypted transactions (TS wrapper / ethers v6 wrapper).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ROFL prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ROFL quickstart expects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a containerized app (OCI image),&lt;/li&gt;
&lt;li&gt;Oasis CLI (latest),&lt;/li&gt;
&lt;li&gt;testnet tokens from the faucet&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1 - Smart contracts: Home chain + Sapphire enclave
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1A) SapphireEnclave.sol (runs confidentially)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This contract runs on Sapphire and receives cross-chain requests via OPL. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stores scores privately,&lt;/li&gt;
&lt;li&gt;accepts updates from ROFL,&lt;/li&gt;
&lt;li&gt;sends a public decision back to Home chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal skeleton based on the official OPL SDK pattern (Enclave + endpoints)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// contracts/contracts/SapphireEnclave.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Enclave, Result, autoswitch} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";

contract SapphireEnclave is Enclave {
    // Private by design on Sapphire: state + calldata are confidential.
    mapping(address =&amp;gt; uint256) private score; // example: 0..100

    // Optional: authorize a ROFL app address (or a set of them).
    address public roflUpdater;

    constructor(address homeContract, string memory homeChain)
        Enclave(homeContract, autoswitch(homeChain))
    {
        registerEndpoint("requestScore", onRequestScore);
    }

    function setRoflUpdater(address _rofl) external {
        // replace with proper access control for real deployments
        roflUpdater = _rofl;
    }

    // ROFL posts score updates (confidential write).
    function updateScore(address user, uint256 newScore) external returns (bool) {
        require(msg.sender == roflUpdater, "not authorized");
        score[user] = newScore;
        return true;
    }

    // OPL endpoint: called when Home chain requests a score decision.
    function onRequestScore(bytes calldata args) internal returns (Result) {
        address user = abi.decode(args, (address));

        uint256 s = score[user];

        // Only return a coarse bucket publicly (avoid leaking exact score).
        uint8 bucket =
            s &amp;gt;= 80 ? 3 :
            s &amp;gt;= 50 ? 2 :
            s &amp;gt;= 20 ? 1 : 0;

        // Send decision back to Home chain (publicly observable).
        postMessage("scoreDecision", abi.encode(user, bucket));
        return Result.Success;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sapphire is an EVM ParaTime built for confidential computation.&lt;/li&gt;
&lt;li&gt;OPL uses message bridges so your Home chain can trigger private computation on Sapphire.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;1B) HomeHost.sol (your existing dApp stays here)&lt;/strong&gt;&lt;br&gt;
Based on official OPL Host pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// contracts/contracts/HomeHost.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Host, Result} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";

contract HomeHost is Host {
    mapping(address =&amp;gt; uint8) public lastBucket;

    constructor(address sapphireContract) Host(sapphireContract) {
        registerEndpoint("scoreDecision", onScoreDecision);
    }

    // User or app requests a score check.
    function requestScore(address user) external payable {
        postMessage("requestScore", abi.encode(user));
    }

    // Called by OPL bridge after Sapphire computed the decision.
    function onScoreDecision(bytes calldata args) internal returns (Result) {
        (address user, uint8 bucket) = abi.decode(args, (address, uint8));
        lastBucket[user] = bucket;
        return Result.Success;
    }

    function canBorrow(address user) external view returns (bool) {
        return lastBucket[user] &amp;gt;= 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 - Hardhat setup (with Sapphire encryption)
&lt;/h2&gt;

&lt;p&gt;Install the OPL/Sapphire contracts library and Sapphire tooling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OPL SDK lives in @oasisprotocol/sapphire-contracts.&lt;/li&gt;
&lt;li&gt;Sapphire wrapper guidance (TypeScript wrapper / ethers wrapper).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;strong&gt;hardhat.config.ts&lt;/strong&gt; should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home chain network (your choice),&lt;/li&gt;
&lt;li&gt;Sapphire testnet using the official RPC and chain id&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example network params:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// contracts/hardhat.config.ts (snippet)
networks: {
  sapphire_testnet: {
    url: "https://testnet.sapphire.oasis.io",
    chainId: 23295,
    accounts: [process.env.PRIVATE_KEY!],
  },
  // home: { ... } // e.g. sepolia/arbitrum etc.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 - Link OPL endpoints (the "wiring")
&lt;/h2&gt;

&lt;p&gt;OPL requires that each side knows the other side and which endpoints exist (1–1 paired contracts).&lt;/p&gt;

&lt;p&gt;In your scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deploy SapphireEnclave on Sapphire&lt;/li&gt;
&lt;li&gt;deploy HomeHost on Home chain&lt;/li&gt;
&lt;li&gt;set each other’s address + register endpoints appropriately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Exactly how you do step (3) depends on your preferred OPL bridge route; the docs list multiple integration methods, including OPL SDK wrapper around Celer IM.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - ROFL app: compute score inside a TEE and push to Sapphire
&lt;/h2&gt;

&lt;p&gt;ROFL gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TEE execution (SGX/TDX)&lt;/li&gt;
&lt;li&gt;app lifecycle managed via Oasis&lt;/li&gt;
&lt;li&gt;built-in secrets stored on-chain and injected securely&lt;/li&gt;
&lt;li&gt;ability to sign and submit special transactions back to Sapphire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ROFL gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TEE execution (SGX/TDX)&lt;/li&gt;
&lt;li&gt;app lifecycle managed via Oasis&lt;/li&gt;
&lt;li&gt;built-in secrets stored on-chain and injected securely&lt;/li&gt;
&lt;li&gt;ability to sign and submit special transactions back to Sapphire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4A) ROFL app code (Python example)&lt;/strong&gt;&lt;br&gt;
rofl/app/main.py:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loads secrets (API keys)&lt;/li&gt;
&lt;li&gt;fetches signals&lt;/li&gt;
&lt;li&gt;runs scoring&lt;/li&gt;
&lt;li&gt;calls updateScore(user, score) on Sapphire
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# rofl/app/main.py
import os
import time
from web3 import Web3

SAPPHIRE_RPC = os.environ["SAPPHIRE_RPC"]
ENCLAVE_ADDR = Web3.to_checksum_address(os.environ["ENCLAVE_ADDR"])
ROFL_SIGNER_KEY = os.environ["ROFL_SIGNER_KEY"]  # stored as ROFL secret

ABI = [...]  # contract ABI for updateScore

w3 = Web3(Web3.HTTPProvider(SAPPHIRE_RPC))
acct = w3.eth.account.from_key(ROFL_SIGNER_KEY)
enclave = w3.eth.contract(address=ENCLAVE_ADDR, abi=ABI)

def compute_score(user: str) -&amp;gt; int:
    # pull private signals using secrets (tokens), then score
    # keep it deterministic if you want auditability
    return 82

def submit_score(user: str, s: int):
    tx = enclave.functions.updateScore(Web3.to_checksum_address(user), s).build_transaction({
        "from": acct.address,
        "nonce": w3.eth.get_transaction_count(acct.address),
        "gas": 500_000,
    })
    signed = acct.sign_transaction(tx)
    return w3.eth.send_raw_transaction(signed.rawTransaction)

def main():
    users = os.environ["USERS"].split(",")
    while True:
        for u in users:
            s = compute_score(u)
            txh = submit_score(u, s)
            print("updated", u, s, txh.hex())
        time.sleep(60)

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;4B) Containerize + ROFLize&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ROFL quickstart is literally the flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;oasis rofl init&lt;/li&gt;
&lt;li&gt;oasis rofl create --network testnet&lt;/li&gt;
&lt;li&gt;oasis rofl build &lt;/li&gt;
&lt;li&gt;oasis rofl secret set ...&lt;/li&gt;
&lt;li&gt;oasis rofl update&lt;/li&gt;
&lt;li&gt;oasis rofl deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# inside rofl/
echo -n "0xYOUR_SIGNER_KEY" | oasis rofl secret set ROFL_SIGNER_KEY -
echo -n "https://testnet.sapphire.oasis.io" | oasis rofl secret set SAPPHIRE_RPC -
echo -n "0xYourEnclaveAddress" | oasis rofl secret set ENCLAVE_ADDR -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ROFL secrets are explicitly supported and meant for sensitive env vars like API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequence diagram (end-to-end)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv67pfn3fo40rlsbioam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv67pfn3fo40rlsbioam.png" alt=" " width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the core pattern: public app stays on its chain, sensitive logic runs confidentially, and you only export a minimal decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Threat model notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don’t export raw scores: export buckets, booleans, or commitments.&lt;/li&gt;
&lt;li&gt;Secrets: keep API keys/model access tokens in ROFL secrets (not in Docker image).&lt;/li&gt;
&lt;li&gt;RPC trust: Oasis docs explicitly warn RPC endpoints are a trust point (rate limits, censorship, MITM); run your own if your security bar is high.&lt;/li&gt;
&lt;li&gt;Testnet disclaimer: Sapphire testnet confidentiality is not guaranteed and state may be wiped; treat it as public.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/sapphire/" rel="noopener noreferrer"&gt;Sapphire overview + dev entrypoint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/sapphire/network/" rel="noopener noreferrer"&gt;Sapphire network info (RPC + chain IDs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/opl/" rel="noopener noreferrer"&gt;OPL overview + message bridge options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/opl/opl-sdk/" rel="noopener noreferrer"&gt;OPL SDK contract examples (Enclave/Host, endpoints, postMessage)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oasis.io/build/rofl/" rel="noopener noreferrer"&gt;ROFL overview + quickstart steps + workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>confidentiality</category>
      <category>privacy</category>
      <category>web3</category>
    </item>
    <item>
      <title>Building a Confidential Web3 Application with Oasis</title>
      <dc:creator>rayQu</dc:creator>
      <pubDate>Thu, 19 Feb 2026 09:17:40 +0000</pubDate>
      <link>https://dev.to/rollingindo/building-a-confidential-web3-application-with-oasis-5c27</link>
      <guid>https://dev.to/rollingindo/building-a-confidential-web3-application-with-oasis-5c27</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Web3 applications increasingly need reputation, risk scoring, and trust signals.&lt;/p&gt;

&lt;p&gt;However, current solutions have a major issue:&lt;/p&gt;

&lt;p&gt;• Reputation systems expose user data publicly&lt;br&gt;
• Credit/risk models require sending sensitive data to centralized servers&lt;br&gt;
• Wallet analytics often reveal full transaction history&lt;br&gt;
• AI scoring pipelines break user privacy&lt;/p&gt;

&lt;p&gt;This creates a trade-off between &lt;strong&gt;useful intelligence&lt;/strong&gt; and &lt;strong&gt;data confidentiality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal of this project was to remove that trade-off.&lt;/p&gt;




&lt;h2&gt;
  
  
  PrivateAI Oracle
&lt;/h2&gt;

&lt;p&gt;A privacy-preserving trust scoring system that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collects wallet or activity signals&lt;/li&gt;
&lt;li&gt;Runs an AI-based trust evaluation off-chain&lt;/li&gt;
&lt;li&gt;Produces a verifiable score&lt;/li&gt;
&lt;li&gt;Stores proof/commitment on Oasis confidential smart contracts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The system allows applications to query a user's trust score &lt;strong&gt;without revealing the underlying private data used to compute it&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Oasis Network
&lt;/h2&gt;

&lt;p&gt;This architecture relies on Oasis confidential computing features.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;Sapphire (confidential EVM runtime)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sensitive inputs remain encrypted&lt;/li&gt;
&lt;li&gt;Smart contract state is confidential when required&lt;/li&gt;
&lt;li&gt;Only final outputs are selectively revealed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes Oasis ideal for AI + blockchain workflows involving private datasets.&lt;/p&gt;

&lt;p&gt;Traditional public chains would expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;model inputs&lt;/li&gt;
&lt;li&gt;intermediate scoring logic&lt;/li&gt;
&lt;li&gt;sensitive user metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oasis prevents this.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The system consists of three main components:&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Collection Layer
&lt;/h3&gt;

&lt;p&gt;Collects public or permissioned signals such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wallet activity patterns&lt;/li&gt;
&lt;li&gt;GitHub contribution signals&lt;/li&gt;
&lt;li&gt;behavioral indicators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only necessary features are extracted.&lt;/p&gt;




&lt;h3&gt;
  
  
  Off-Chain AI Inference Engine
&lt;/h3&gt;

&lt;p&gt;A machine learning model computes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a normalized trust score&lt;/li&gt;
&lt;li&gt;optional confidence level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The raw feature data never needs to be publicly stored on-chain.&lt;/p&gt;




&lt;h3&gt;
  
  
  Oasis Confidential Smart Contract
&lt;/h3&gt;

&lt;p&gt;The contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;receives a signed result&lt;/li&gt;
&lt;li&gt;records a commitment hash&lt;/li&gt;
&lt;li&gt;allows verification of authenticity&lt;/li&gt;
&lt;li&gt;exposes only the final trust score&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This preserves verifiability while protecting user privacy.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Enables
&lt;/h2&gt;

&lt;p&gt;PrivateAI Oracle can be used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DeFi borrower trust scoring without exposing full history&lt;/li&gt;
&lt;li&gt;DAO contributor reputation without revealing private activity&lt;/li&gt;
&lt;li&gt;sybil-resistant onboarding systems&lt;/li&gt;
&lt;li&gt;Privacy-preserving AI reputation layers&lt;/li&gt;
&lt;li&gt;Secure Web3 identity primitives&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Takeaways
&lt;/h2&gt;

&lt;p&gt;During development, several important lessons emerged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidential smart contracts change how data pipelines must be designed&lt;/li&gt;
&lt;li&gt;AI outputs should be treated as attestations, not raw datasets&lt;/li&gt;
&lt;li&gt;Privacy must be built into architecture, not added later&lt;/li&gt;
&lt;li&gt;Hybrid off-chain compute + on-chain proof is extremely powerful&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The final prototype demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;End-to-end confidential trust scoring&lt;/li&gt;
&lt;li&gt;AI inference integrated with Oasis smart contracts&lt;/li&gt;
&lt;li&gt;Selective transparency (score visible, data hidden)&lt;/li&gt;
&lt;li&gt;Practical architecture for privacy-preserving Web3 intelligence&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Future Implementations that could help
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Zero-knowledge proof integration for stronger verification&lt;/li&gt;
&lt;li&gt;Multi-model scoring pipelines&lt;/li&gt;
&lt;li&gt;public demo UI&lt;/li&gt;
&lt;li&gt;Integration with lending or DAO onboarding flows&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Web3 does not just need decentralization.&lt;/p&gt;

&lt;p&gt;It needs &lt;strong&gt;private, verifiable intelligence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;PrivateAI Oracle explores how Oasis confidential computing can enable exactly that.&lt;/p&gt;

</description>
      <category>ethics</category>
      <category>networksec</category>
      <category>news</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
