Skip to content
Merged

Dev #111

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
affceea
feat: implement multi-node IPFS upload and Nostr pin broadcasting
vrogojin Dec 6, 2025
92048ed
feat: implement IPNS-based bidirectional sync for IPFS storage
vrogojin Dec 6, 2025
0ed210f
feat: add IPFS sync hardening with tombstones and tab coordination
vrogojin Dec 6, 2025
6381d4f
feat: implement dual IPNS publishing (HTTP + browser DHT)
vrogojin Dec 6, 2025
cecae97
feat: fetch nametags from IPNS during wallet import address selection
vrogojin Dec 7, 2025
add3f16
fix: filter peer connection logs to show only bootstrap peers
vrogojin Dec 7, 2025
e60ed9a
fix: show feedback when recovery phrase is unavailable
vrogojin Dec 7, 2025
c2fd17d
feat: add peer IDs for IPFS bootstrap nodes 2-5
vrogojin Dec 7, 2025
f61b341
feat: implement progressive IPNS resolution with multi-gateway confli…
vrogojin Dec 8, 2025
9e5b975
feat: restrict IPFS connections and optimize sync
vrogojin Dec 8, 2025
da4a442
feat: implement tombstone sanity check with Unicity verification
vrogojin Dec 8, 2025
6364a4f
fix: resolve sync scheduling bug and add token security hardening
vrogojin Dec 8, 2025
832385d
fix: detect IPNS content changes when sequence numbers match
vrogojin Dec 8, 2025
444722f
docs: add comprehensive IPNS sync architecture TODO
vrogojin Dec 9, 2025
dffc8fb
Merge dev branch into @cryptohog/ipfs
vrogojin Dec 9, 2025
d68cf68
feat: adaptive IPNS polling based on tab visibility
vrogojin Dec 9, 2025
6af4f4a
fix: use L3 identity key for IPNS nametag lookup during wallet restore
vrogojin Dec 9, 2025
e2acb0c
Merge remote-tracking branch 'origin/dev' into @cryptohog/ipfs
vrogojin Dec 9, 2025
b65cecb
fix: resolve lint errors in IpnsNametagFetcher
vrogojin Dec 9, 2025
26aebfc
fix: sync nametag-only wallets to IPFS
vrogojin Dec 9, 2025
a07b7c9
feat: add JSON wallet export/import with mnemonic support
KruGoL Dec 9, 2025
4a0e7d4
fix: skip scan modal for JSON imports with mnemonic
KruGoL Dec 9, 2025
d8d3a0d
feat: add paste support for full mnemonic phrase
KruGoL Dec 9, 2025
a2b9155
feat: display nametags in L1 wallet address dropdown
vrogojin Dec 9, 2025
9062194
feat: auto-detect nametag from IPNS on Complete Setup screen
vrogojin Dec 9, 2025
10ef527
feat: improve nametag loading in L1 wallet and Complete Setup screen
vrogojin Dec 9, 2025
a0ed6bc
Merge branch 'main' into @cryptohog/ipfs
vrogojin Dec 10, 2025
59d310b
fix: add missing useCallback dependency in BridgeModal useEffect
KruGoL Dec 10, 2025
e671a8d
fix: add IPNS recovery when records expire but local data exists
vrogojin Dec 10, 2025
0bfded8
fix: reinitialize IPFS keys when identity changes
vrogojin Dec 11, 2025
99e53d1
fix: use actual address index for L3 derivation, skip change addresses
vrogojin Dec 11, 2025
f3f05c9
feat: give change addresses unique L3 identities with Change badge
vrogojin Dec 11, 2025
3981fb7
fix: IPNS nametag discovery and IPFS identity switching
vrogojin Dec 12, 2025
383f9d5
feat: show Load Selected button immediately after L1 scan completes
vrogojin Dec 12, 2025
bee7eaf
fix: extend IPNS record lifetime from 24 hours to 99 years
vrogojin Dec 14, 2025
59932ff
fix: make Stop Scan button work and keep Load Selected visible after …
vrogojin Dec 14, 2025
3833767
feat: add IPFS sync spinner to L3 wallet view
vrogojin Dec 14, 2025
bf54215
fix: change "cloud" to "fog" in sync spinner text
vrogojin Dec 14, 2025
97790e7
feat: add dual IPNS resolution racing for faster sync
vrogojin Dec 14, 2025
7c5ec3e
fix: ensure change tokens have valid TXF structure for IPFS sync
vrogojin Dec 14, 2025
e692456
feat: await IPFS sync before marking Nostr events as processed
vrogojin Dec 14, 2025
13add1c
feat: add outbox pattern with periodic retry for token transfers
vrogojin Dec 14, 2025
dd0ccb0
feat: improve L1 SDK and wallet infrastructure
vrogojin Dec 14, 2025
cb644f1
fix: restore Standard wallet (HMAC) import support
KruGoL Dec 16, 2025
1af4ea2
fix: ensure wallet data is fully cleared before create/import operations
KruGoL Dec 16, 2025
b51b372
fix: improve wallet import flow with better IPNS handling and UX
KruGoL Dec 16, 2025
8bd477a
fix: add required fields to NametagData in saveNametagForAddress calls
KruGoL Dec 16, 2025
921137e
improved markdown processing
ristik Dec 17, 2025
046202e
Update Viktor's greeting
igmahl Dec 17, 2025
6e9d477
feat: add nametag validation with availability check
KruGoL Dec 17, 2025
b56b256
Merge branch 'dev' into @cryptohog/ipfs
KruGoL Dec 17, 2025
556f9ab
fix: remove unused restoreWallet from CreateWalletFlow
KruGoL Dec 17, 2025
d93406f
Add welcome modal with age verification and terms acceptance
KruGoL Dec 17, 2025
c9aa68e
Adjust welcome modal: dim glow effect and match icon color to shield
KruGoL Dec 17, 2025
cac816c
Merge pull request #108 from unicitynetwork/feature/welcome-modal
KruGoL Dec 17, 2025
40dfdd1
fix: remove temporary documentation files
KruGoL Dec 18, 2025
3f080fd
Merge pull request #97 from unicitynetwork/@cryptohog/ipfs
KruGoL Dec 18, 2025
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
237 changes: 237 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Unicity AgentSphere is a React-based cryptocurrency wallet application for the Unicity network. It provides a dual-layer wallet interface supporting both Layer 1 (ALPHA blockchain) and Layer 3 (Unicity state transition network) operations. The app integrates with multiple Unicity SDKs for token management, state transitions, and peer-to-peer transfers via Nostr.

## Development Commands

```bash
# Start development server
npm run dev

# Build for production (runs TypeScript compiler then Vite build)
npm run build

# Lint the codebase
npm run lint

# Run all tests (watch mode)
npm run test

# Run tests once (no watch mode)
npm run test:run

# Run a single test file
npx vitest run tests/unit/components/wallet/L3/services/TokenValidationService.test.ts

# Preview production build
npm run preview

# Type check only (without building)
npx tsc --noEmit
```

## Architecture

### Tech Stack
- React 19 + TypeScript with Vite 7
- TanStack Query v5 for server state management
- Tailwind CSS 4 for styling
- Framer Motion for animations
- React Router DOM v7 for routing
- Vitest + jsdom for testing
- Helia for IPFS/IPNS browser integration

### Application Structure

The app uses a single-page architecture with three main routes:
- `/` - Intro/splash screen
- `/home` - Main dashboard with agent cards, chat, and wallet panel
- `/ai` - AI assistant page

All routes except intro use `DashboardLayout` which provides header, navigation, and handles incoming transfers.

### Wallet Architecture (Two-Layer System)

**Layer 1 (L1) - ALPHA Blockchain:**
- Location: `src/components/wallet/L1/`
- Custom HD wallet implementation with BIP32-style derivation (see `SPHERE_DEVELOPER_GUIDE.md` for details)
- Uses Fulcrum WebSocket for blockchain data (Electrum-style protocol)
- Supports vesting classification (coins from blocks ≤280,000 are "vested")
- SDK in `src/components/wallet/L1/sdk/` handles crypto, transactions, network calls

**Layer 3 (L3) - Unicity Network:**
- Location: `src/components/wallet/L3/`
- Uses `@unicitylabs/state-transition-sdk` for token operations
- Nostr integration for P2P messaging and token transfers
- Nametag system for human-readable addresses
- IPFS/IPNS for decentralized token storage and sync
- ServiceProvider singleton manages SDK clients

### Key Patterns

**State Management:**
- TanStack Query manages all async state (wallet, balance, transactions)
- Custom events (`wallet-updated`) trigger cross-component refreshes
- localStorage persists wallet data; IndexedDB for vesting cache

**Query Key Structure:**
- L1: `["l1", "wallet"]`, `["l1", "balance", address]`, `["l1", "vesting", address]`
- L3: `["wallet", "identity"]`, `["wallet", "tokens"]`, `["wallet", "aggregated"]`

**Services Pattern (L3):**
- `ServiceProvider` - singleton for SDK clients (aggregator, state transition)
- `IdentityManager` - handles wallet identity and key management
- `NostrService` - P2P messaging and token transfer via Nostr protocol
- `NametagService` - human-readable address resolution (@username lookup)
- `IpfsStorageService` - IPFS/IPNS storage with Helia, supports bidirectional sync
- `SyncCoordinator` - tab coordination for IPFS sync with tombstone support
- `TokenValidationService` - validates tokens against aggregator
- `ConflictResolutionService` - handles token conflicts during sync
- `FaucetService` - obtains test tokens from faucet
- `NostrPinPublisher` - broadcasts token pins for discovery
- `TxfSerializer` - serializes token transfer files (.txf format)
- `IpnsNametagFetcher` - resolves nametags via IPNS during wallet import

**Shared Services:**
- `UnifiedKeyManager` - cross-layer key management (L1/L3 key derivation)

### SDK Layer (L1)

The `src/components/wallet/L1/sdk/` directory contains:
- `wallet.ts` - wallet creation/management
- `address.ts` - HD key derivation and address generation
- `network.ts` - Fulcrum WebSocket connection and RPC calls
- `tx.ts` - transaction creation and signing
- `vesting.ts` - coinbase tracing for vesting classification
- `vestingState.ts` - vesting mode state management

### Important Types

```typescript
// L1 Wallet (sdk/types.ts)
interface Wallet {
masterPrivateKey: string;
chainCode?: string;
addresses: WalletAddress[];
isBIP32?: boolean;
}

// L3 Token (L3/data/model)
class Token {
id: string;
symbol: string;
amount: string;
jsonData: string; // Serialized SDK token
status: TokenStatus;
}
```

### Vite Configuration

- Base path: configurable via `BASE_PATH` env var (default `/`)
- Node polyfills enabled for crypto libraries
- Proxy `/rpc` to `https://goggregator-test.unicity.network` for L3 aggregator
- Optional HTTPS support via `SSL_CERT_PATH` env var
- Remote HMR support via `HMR_HOST` env var

### Component Hierarchy

```
App
└── DashboardLayout
├── Header
├── Navigation
└── HomePage
├── AgentCard[] (chat agents)
├── ChatSection / SimpleAIChat / etc.
└── WalletPanel
├── L1WalletView (when Layer 1 selected)
└── L3WalletView (when Layer 3 selected)
```

## Environment Variables

Copy `.env.example` to `.env` and configure:

```env
VITE_AGENT_API_URL=http://localhost:3000 # Agentic chatbot backend
VITE_USE_MOCK_AGENTS=true # Use mock agents (for local dev without backend)
VITE_AGGREGATOR_URL=/rpc # Unicity aggregator (proxied in dev)
VITE_ENABLE_IPFS=true # Enable IPFS storage for wallet backup

# Optional: HTTPS for dev server (e.g., for WebCrypto APIs)
SSL_CERT_PATH=/path/to/certs # Path to SSL certificate directory
HMR_HOST=your-dev-server.example.com # Custom HMR host for remote dev
BASE_PATH=/ # Base path for deployment (default: /)
```

## Testing

Tests are located in `tests/` directory and run with Vitest:
- Test files: `tests/**/*.test.ts`, `tests/**/*.test.tsx`
- Environment: jsdom
- Path alias: `@` maps to `/src`
- Globals enabled: `describe`, `it`, `expect`, `vi` are available without imports

## Developer Notes

### Crypto Libraries
The project uses node polyfills (`vite-plugin-node-polyfills`) for browser compatibility with crypto libraries like `elliptic`, `bip39`, and `crypto-js`. The `/rpc` endpoint is proxied to the Unicity aggregator in development.

### BIP32 Implementation
The L1 wallet uses a custom derivation that differs from standard BIP32 (see `SPHERE_DEVELOPER_GUIDE.md` for migration details). Standard path would be `m/44'/0'/0'/0/{index}`.

### Vesting System
ALPHA coins are classified as "vested" or "unvested" based on coinbase block height (threshold: 280,000). The classifier traces each UTXO back to its coinbase origin and caches results in IndexedDB.

### Token Transfer Flow (L3)
1. Calculate optimal token split via `TokenSplitCalculator`
2. Create transfer commitment with SDK
3. Submit to aggregator and wait for inclusion proof
4. Send token + proof to recipient via Nostr
5. Broadcast pin to Nostr for discovery
6. Update local storage and IPFS, trigger query refresh

### IPFS Storage (L3)
Tokens are synced to IPFS with IPNS for consistent addressing:
- Dual publishing: HTTP API to backend + browser DHT
- Bidirectional sync with conflict resolution
- Tombstones track deleted tokens across devices
- Tab coordination prevents concurrent writes

**Unicity IPFS Bootstrap Peers:**
| Host | Peer ID |
|------|---------|
| unicity-ipfs2.dyndns.org | 12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh |
| unicity-ipfs3.dyndns.org | 12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6 |
| unicity-ipfs4.dyndns.org | 12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1 |
| unicity-ipfs5.dyndns.org | 12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv |

### Embedded Wallet (guiwallet-main)
A standalone single-file HTML wallet exists at `src/components/wallet/L1/guiwallet-main/`. This is a separate 888KB self-contained wallet application, not integrated into the React app.

### localStorage Keys
Key persistence patterns:
- `unified_wallet_*` - Encrypted wallet credentials (mnemonic, master key, chain code)
- `unicity_wallet_{address}` - Per-address wallet data
- `unicity_transaction_history` - L1 transaction history
- `unicity_chat_*` - Chat conversations and messages
- `wallet-active-layer` - Currently selected layer (L1/L3)
- `sphere-theme` - UI theme preference
- `l3_selected_address_path` - Selected address BIP32 path for L3 identity (e.g., "m/84'/1'/0'/0/0"); determines which derived key is used for IPFS/IPNS publishing and token ownership

### Custom Events
The app uses custom events for cross-component communication:
- `wallet-updated` - Triggers TanStack Query refetch for wallet data
- Dispatch via `window.dispatchEvent(new Event('wallet-updated'))`

### Key External Dependencies
- `@unicitylabs/state-transition-sdk` (v1.6.0) - L3 token operations and state transitions
- `@unicitylabs/nostr-js-sdk` - P2P messaging and token transfers
- `helia` / `@helia/ipns` / `@helia/json` - Browser-based IPFS/IPNS for decentralized storage
- `elliptic` - secp256k1 cryptography for L1 wallet
- `bip39` - Seed phrase generation and validation
19 changes: 18 additions & 1 deletion src/components/auth/WalletGate.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { ReactNode } from "react";
import { type ReactNode, useEffect } from "react";
import { motion } from "framer-motion";
import { Loader2 } from "lucide-react";
import { useWallet } from "../wallet/L3/hooks/useWallet";
import { CreateWalletFlow } from "../wallet/L3/onboarding/CreateWalletFlow";
import { NostrPinPublisher } from "../wallet/L3/services/NostrPinPublisher";
import { NOSTR_PIN_CONFIG } from "../../config/nostrPin.config";

interface WalletGateProps {
children: ReactNode;
Expand Down Expand Up @@ -83,6 +85,21 @@ export function WalletGate({ children }: WalletGateProps) {
const isLoading = isLoadingIdentity || (!!identity && isLoadingNametag);
const isAuthenticated = !!identity && !!nametag;

// Start NostrPinPublisher when authenticated
// This enables automatic CID announcements to Nostr for pinning
useEffect(() => {
if (isAuthenticated && NOSTR_PIN_CONFIG.enabled) {
const publisher = NostrPinPublisher.getInstance();
publisher.start().catch((err) => {
console.error("Failed to start NostrPinPublisher:", err);
});

return () => {
publisher.stop();
};
}
}, [isAuthenticated]);

if (isLoading) {
return <LoadingScreen />;
}
Expand Down
Loading