DEV Community

rayQu
rayQu

Posted on

Building a Confidential Event-Driven Indexer on Oasis Sapphire (Beyond The Graph Pattern)

Most blockchain indexing systems today assume a very simple model:

all blockchain data is public, and indexing is just a deterministic transformation of public events.

This assumption works well for analytics platforms like dashboards, DEX trackers, or portfolio tools.

However, it breaks down in real-world scenarios where:

  • event data is sensitive (wallet behavior, internal flows, private classifications)
  • indexing logic itself is proprietary (risk models, scoring systems, compliance logic)
  • or you need selective disclosure (some users can see more data than others)

In traditional architectures, you are forced to choose between:

  • transparency (but no privacy)
  • privacy (but no verifiability)

Oasis Sapphire introduces a third option:

execute indexing logic inside a confidential runtime (TEE-backed), while still using standard EVM interfaces.

This tutorial builds a confidential event-driven indexer, where:

  • raw blockchain events remain public
  • classification logic is hidden
  • only sanitized outputs are exposed externally

Think of it as:

“The Graph, but the mapping function is private and policy-controlled.”


Why This Is Architecturally Different

To understand why this matters, let's compare the standard indexing pipeline:

Traditional Indexing (The Graph-style)

Smart Contract -> Events -> Indexer -> Public Database -> API
Enter fullscreen mode Exit fullscreen mode

Everything is:

  • deterministic
  • reproducible
  • fully observable

This is great for trustlessness, but it creates problems:

  • any adversary can reconstruct your analytics logic
  • wallet behavior is fully exposed
  • proprietary scoring models cannot exist

Confidential Indexing (Oasis Sapphire model)

Smart Contract -> Events
                     ↓
        Sapphire Confidential Runtime
                     ↓
          Private State Transformation
                     ↓
         Offchain Worker + Storage
                     ↓
            Sanitized Public API
Enter fullscreen mode Exit fullscreen mode

Key difference:

The transformation function (indexing logic) is no longer public.


Real-World Use Case

We will build a confidential DeFi activity indexer that:

  • tracks swaps on a DEX
  • computes internal risk scores
  • classifies activity (low / medium / high risk)
  • hides wallet-level behavior
  • exposes only aggregated metrics

This is useful for:

  • institutional compliance dashboards
  • private DeFi analytics platforms
  • MEV-aware monitoring systems
  • regulated crypto infrastructure

Why Not Just Use Existing Indexers?

Existing solutions like The Graph or custom ETL pipelines have a fundamental limitation:

they cannot hide the transformation logic itself.

Even if you obfuscate the backend:

  • input events are public
  • transformations are reproducible
  • reverse-engineering is trivial

With Sapphire:

  • logic executes in a TEE (Trusted Execution Environment)
  • computation is not externally observable
  • only final outputs are revealed

Project Structure (Production-Grade)

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
Enter fullscreen mode Exit fullscreen mode

Step 1: Event Source Contract (DEX Simulation)

We start with a simple decentralized exchange emitting swap events.

This is intentionally minimal, the complexity happens in the indexing layer.

// 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
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Key idea:

We deliberately include structured data so the indexer can apply richer classification logic.


Step 2: Confidential Indexer Contract (Sapphire Core Logic)

This is the core of the system.

Key properties:

  • runs inside Sapphire confidential runtime
  • decrypts event payloads
  • applies private scoring rules
  • stores encrypted internal state
// 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 => 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 > 1_000_000 ether) {
            score += 5;
        }

        // Volume spike heuristic
        if (state.volume > 10_000_000 ether) {
            score += 3;
        }

        return score;
    }
}
Enter fullscreen mode Exit fullscreen mode

Why this is important:

The key innovation is:

the risk model is never observable externally

That means:

  • adversaries cannot game your scoring system
  • analytics logic is not reproducible
  • you can build proprietary indexers

Step 3: Event Worker (Offchain Index Aggregator)

This component listens to Sapphire outputs and stores them.

Unlike traditional indexers, it does NOT compute logic, only stores results.

// worker/indexer.ts
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);

const contract = new ethers.Contract(
  process.env.CONTRACT_ADDRESS,
  ABI,
  provider
);

contract.on("Indexed", async (poolId, riskScore, volume) => {

  console.log("Indexed update received");

  await db.upsert({
    poolId: poolId.toString(),
    riskScore: riskScore.toString(),
    volume: volume.toString(),
    updatedAt: Date.now()
  });
});
Enter fullscreen mode Exit fullscreen mode

Step 4: Public API Layer (Sanitized Exposure)

This layer ensures no raw or sensitive data leaks.

// api/server.ts
import express from "express";
const app = express();

app.get("/pool/:id", async (req, res) => {

  const pool = await db.get(req.params.id);

  res.json({
    pool: req.params.id,
    riskScore: pool.riskScore,
    volume: pool.volume,

    // intentionally no user-level data exposed
    granularity: "aggregated"
  });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Key Design Insight: Privacy Boundary Model

This architecture introduces a strict separation:

Layer Visibility
Smart contract events Public
Sapphire execution logic Confidential
Risk model / heuristics Hidden
Aggregated outputs Public (sanitized)

Hard Engineering Problems (Important Section)

1. Determinism vs Confidential Execution

TEE environments must remain deterministic:

  • same input -> same output
  • but without revealing internal computation steps

2. Trust in Index Correctness

Because computation is hidden:

  • you rely on enclave guarantees
  • or multi-worker validation systems

3. Composability Limitations

Unlike The Graph:

  • results are not universally reproducible
  • external contracts cannot verify raw computation

4. State Replay Problem

Encrypted state introduces challenges:

  • historical recomputation is non-trivial
  • debugging requires replay tooling inside TEE

Why This Matters

This pattern enables a new category of systems:

  • private blockchain analytics
  • institutional DeFi infrastructure
  • MEV-resistant monitoring tools
  • compliance-aware data pipelines

It shifts the paradigm from:

“everything is transparent and verifiable”

to:

“computation is verifiable, but not necessarily observable”


Final Thoughts

Most blockchain infrastructure assumes transparency is always beneficial.

But in real systems, especially financial and institutional contexts:

  • privacy is required
  • analytics are proprietary
  • and data exposure is a liability

Oasis Sapphire enables a third design space:

verifiable execution with confidential logic

This indexer pattern is just one example of what becomes possible when indexing itself becomes programmable and private.

Top comments (0)