Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Intuition Protocol Configuration
# Copy to .env and fill in values

# Chain ID: 1155 = mainnet, 13579 = testnet
VITE_CHAIN_ID=13579

# MultiVault contract address on Intuition L3
VITE_MULTIVAULT_ADDRESS=0x430BbF52503Bd4801E51182f4cB9f8F534225DE5

# GraphQL endpoint for Intuition indexer
VITE_GRAPHQL_URL=https://api.intuition.systems/v1/graphql

# RPC URL for Intuition L3
VITE_RPC_URL=https://rpc.intuition.systems

# WalletConnect Project ID (get from cloud.walletconnect.com)
VITE_WALLETCONNECT_PROJECT_ID=
115 changes: 115 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Architecture

## System Overview

The Ontology app bridges a local claim-building UI with the Intuition Protocol's
on-chain knowledge graph. Claims authored in the browser are transformed into
Intuition atoms and triples, pinned to IPFS, and submitted to the MultiVault
smart contract on the Intuition L3 chain.

```
┌─────────────────────────────────────────────────────┐
│ Browser (React) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Pages │→ │Components│→ │ OnchainFeed │ │
│ └────┬─────┘ └────┬─────┘ └────────┬─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────────────────────────────────────────┐ │
│ │ React Hooks Layer │ │
│ │ useAtoms · useTriples · useCreateAtom │ │
│ │ useCreateTriple · useIntuitionSession │ │
│ └──────────────┬────────────────────┬───────────┘ │
│ │ │ │
│ ┌─────────┘ └─────────┐ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌────────────────┐ │
│ │ GraphQL Service │ │ IPFS Service │ │
│ │ (indexer queries) │ │ (pin mutations) │ │
│ └────────┬─────────┘ └────────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ wagmi / viem │ │
│ │ (wallet connect, contract reads/writes) │ │
│ └──────────────────────┬───────────────────────┘ │
└─────────────────────────┼───────────────────────────┘
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Intuition│ │ Intuition│ │ IPFS │
│ L3 Chain │ │ Indexer │ │ (Pinata) │
│(MultiVault)│ │(GraphQL) │ │ │
└──────────┘ └──────────┘ └──────────┘
```

## Layer Rules

Import direction is strictly top-down. Reverse imports are violations.

| Layer | Allowed imports | Forbidden |
|-------|----------------|-----------|
| `pages/` | components, hooks, lib | services directly |
| `components/` | hooks, lib, types | services, config, contract calls |
| `hooks/` | services, lib, config, wagmi | JSX, DOM access |
| `services/` | lib, config, types, viem | React, components, hooks |
| `lib/` | types, config (constants) | services, hooks, components |
| `config/` | lib, types, zod | side effects, React |

## Module: `src/intuition/`

Self-contained protocol integration module. Can be extracted as a standalone
package if needed.

```
src/intuition/
├── abi/
│ └── multivault.ts # Read + Write ABI fragments
├── hooks/
│ ├── use-atoms.ts # Fetch atoms from indexer
│ ├── use-triples.ts # Fetch triples from indexer
│ ├── use-create-atom.ts # Write: create atom on-chain
│ ├── use-create-triple.ts # Write: create triple on-chain
│ └── use-intuition-session.ts # Session state (costs, vault ID)
├── services/
│ ├── graphql.service.ts # Indexer queries (atoms, triples, search)
│ └── ipfs.service.ts # IPFS pinning with retry + cache
├── chains.ts # Intuition L3 chain definitions
├── types.ts # Branded AtomId / TripleId / CurveId
├── wagmi-config.ts # wagmi createConfig
└── index.ts # Barrel export
```

## Data Flow: Claim → On-Chain Triple

```
1. User fills ClaimBuilder (subject, predicate, object)
2. Each entity is checked: CAIP-10 address → raw bytes, else → IPFS pin
3. pinAtomData() pins structured data, returns ipfs:// URI
4. URI encoded as bytes via viem's toHex()
5. MultiVault.createAtom(bytes) called for each new atom
6. MultiVault.createTriple(subjectId, predicateId, objectId) links them
7. OnchainFeed auto-refreshes via React Query to show the new triple
```

## Environment

All env vars are read exclusively through `src/config/env.ts` (zod-validated).
The app supports three modes:

| Mode | Trigger | Behavior |
|------|---------|----------|
| **Demo** | No `.env` file | Read-only UI, no wallet, static data |
| **Testnet** | `VITE_CHAIN_ID=13579` | Full functionality, Base Sepolia faucet |
| **Mainnet** | `VITE_CHAIN_ID=1155` | Production, real ETH required |

## Key Dependencies

| Package | Purpose | Why chosen |
|---------|---------|------------|
| `wagmi` + `viem` | Contract interaction | Industry standard for React dapps |
| `@rainbow-me/rainbowkit` | Wallet UI | Best UX, maintained by Rainbow team |
| `@tanstack/react-query` | Async state | Required by wagmi, handles caching |
| `zod` | Schema validation | Type-safe env loading at boot |
103 changes: 103 additions & 0 deletions docs/decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Architecture Decision Records

Append-only log of non-obvious technical decisions.

---

## ADR-001: Self-contained `src/intuition/` module

**Date:** 2026-04-29
**Status:** Accepted

**Context:** The bounty requires integrating wallet connection, contract calls,
IPFS pinning, and GraphQL queries into a static Vite/React app. Scattering these
across the existing directory structure would create coupling and make the
integration hard to review, test, or extract.

**Decision:** All Intuition-specific code lives under `src/intuition/` with a
barrel export (`index.ts`). The module is self-contained: it owns its ABI
fragments, chain definitions, hooks, services, and types. The rest of the app
imports only from `src/intuition` (the barrel), never from internal paths.

**Consequences:**
- Clean diff: reviewers see exactly what's new vs. what's existing code.
- Extractable: the module can become `@ontology/intuition` if the project grows.
- Barrel import keeps the public API surface minimal and stable.

---

## ADR-002: Lightweight GraphQL over `@0xintuition/graphql`

**Date:** 2026-04-29
**Status:** Accepted

**Context:** The official `@0xintuition/graphql` SDK provides pre-built queries
but pulls in significant bundle weight (graphql-codegen runtime, urql bindings)
and is tightly coupled to a specific GraphQL client.

**Decision:** Implement a thin `graphql.service.ts` using raw `fetch` + typed
response mappers. Queries are plain template strings, not code-generated.

**Consequences:**
- ~50KB smaller bundle than the SDK approach.
- Manual type maintenance for query responses (acceptable: the schema is small).
- No automatic type generation from GraphQL schema (trade-off for simplicity).

---

## ADR-003: Demo mode for zero-config development

**Date:** 2026-04-29
**Status:** Accepted

**Context:** Contributors cloning the repo need to create a `.env` file with
RPC URLs and contract addresses before the app boots. This friction discourages
casual exploration and breaks CI preview deployments.

**Decision:** `src/config/env.ts` falls back to a demo mode when env vars are
missing. Demo mode uses hardcoded testnet defaults and disables write operations.
A visible badge in the UI header indicates the current mode.

**Consequences:**
- `git clone && npm install && npm run dev` works immediately.
- Demo mode cannot submit on-chain transactions (by design).
- CI/CD preview deploys work without secrets configuration.

---

## ADR-004: Branded types for protocol IDs

**Date:** 2026-04-29
**Status:** Accepted

**Context:** Atom IDs, Triple IDs, and Curve IDs are all `bigint` values at the
contract level, but passing a Triple ID where an Atom ID is expected causes
silent data corruption — the contract accepts any `uint256`.

**Decision:** Use TypeScript branded types (`AtomId`, `TripleId`, `CurveId`) to
make these domain-specific at the type level. Helper functions `toAtomId()`,
`toTripleId()`, `toCurveId()` perform the casting.

**Consequences:**
- Compiler catches cross-ID misuse at build time.
- Small cognitive overhead: callers must wrap raw bigints.
- Zero runtime cost (brands are erased at transpile time).

---

## ADR-005: IPFS pinning with retry and LRU cache

**Date:** 2026-04-30
**Status:** Accepted

**Context:** IPFS pinning via the indexer GraphQL endpoint is a network call that
can fail transiently. Duplicate pins for the same atom data waste bandwidth and
create unnecessary IPFS objects.

**Decision:** `ipfs.service.ts` implements exponential backoff retry (3 attempts)
and an in-memory LRU cache (128 entries) keyed by `type:JSON(data)`.

**Consequences:**
- Transient pinning failures don't block the user flow.
- Repeated claim submissions for the same entity reuse cached URIs.
- Cache is lost on page refresh (acceptable for a browser app).
68 changes: 68 additions & 0 deletions docs/env.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Environment Variables

Single source of truth: `src/config/env.ts` (zod-validated at boot).
Template: `.env.example`.

## Required Variables

| Name | Type | Description |
|------|------|-------------|
| `VITE_CHAIN_ID` | `1155 \| 13579` | Intuition chain ID. `1155` = mainnet, `13579` = testnet. |
| `VITE_RPC_URL` | URL | JSON-RPC endpoint for the selected chain. |
| `VITE_GRAPHQL_URL` | URL | Intuition indexer GraphQL endpoint. |
| `VITE_MULTIVAULT_ADDRESS` | `0x...` (40 hex) | MultiVault contract address. |
| `VITE_WALLETCONNECT_PROJECT_ID` | string | WalletConnect Cloud project ID for RainbowKit. |

## Optional Variables

| Name | Type | Default | Description |
|------|------|---------|-------------|
| `VITE_IPFS_PIN_ENDPOINT` | URL | (graphql url) | Separate IPFS pinning endpoint, if different from indexer. |

## Derived Values (computed, not env vars)

| Name | Source | Description |
|------|--------|-------------|
| `env.networkName` | `VITE_CHAIN_ID` | `'mainnet'` (1155) or `'testnet'` (13579) |
| `isDemoMode` | env parse result | `true` if any required var is missing |

## Network Presets

### Mainnet (chain `1155`)

```env
VITE_CHAIN_ID=1155
VITE_RPC_URL=https://rpc.intuition.systems/http
VITE_GRAPHQL_URL=https://mainnet.intuition.sh/v1/graphql
VITE_MULTIVAULT_ADDRESS=0x6E35cF57A41fA15eA0EaE9C33e751b01A784Fe7e
VITE_WALLETCONNECT_PROJECT_ID=your-project-id
```

### Testnet (chain `13579`)

```env
VITE_CHAIN_ID=13579
VITE_RPC_URL=https://testnet.rpc.intuition.systems/http
VITE_GRAPHQL_URL=https://testnet.intuition.sh/v1/graphql
VITE_MULTIVAULT_ADDRESS=0x78277F0e6237AB8bBab7F45c62F7e80eb7e4dd0d
VITE_WALLETCONNECT_PROJECT_ID=your-project-id
```

## Demo Mode

When `.env` is missing or incomplete, the app boots in **demo mode**:

- ✅ All read-only UI features work (claim builder, graph, matrix)
- ✅ Static data from `src/data/` is used as fallback
- ❌ Wallet connection is disabled
- ❌ On-chain submission is disabled
- ⚠️ A visible "DEMO" badge appears in the header

This allows `git clone && npm run dev` without any configuration.

## Adding New Variables

1. Add to `.env.example` with a comment
2. Add to this table
3. Add to the zod schema in `src/config/env.ts`
4. Import from `env` object — never read `import.meta.env` directly
Loading