Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .github/.gitleaks.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
[extend]
useDefault = true

[allowlist]
description = "Well-known Anvil/Hardhat test private keys (not secrets)"
paths = [
'''apps/virtual-addresses/src/lib/demo-client\.ts''',
'''apps/virtual-addresses/README\.md''',
]

[[rules]]
id = "rpc-auth-credentials"
description = "RPC auth credentials in config files"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ _artifacts
_tmp
_verify
.pnpm-store
.dev.vars
4 changes: 4 additions & 0 deletions apps/virtual-addresses/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Pre-funded accounts for the walkthrough demo (server-side only)
# These accounts must be funded with PathUSD on Tempo Devnet
EXCHANGE_PRIVATE_KEY=0x...
SENDER_PRIVATE_KEY=0x...
111 changes: 111 additions & 0 deletions apps/virtual-addresses/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Virtual Addresses — TIP-1022 Demo

Interactive demo for [TIP-1022](https://github.com/tempoxyz/tempo/pull/3286) virtual addresses on Tempo. Three views:

- **Intro** — TIP-1022 docs landing page: motivation, address layout, protocol flow, properties, and security considerations.
- **Registry** — Mine a salt, register as a virtual-address master, derive deposit addresses, and test transfers. Works with a connected wallet or manual address input.
- **Walkthrough** — Animated visual walkthrough of the full TIP-1022 flow (register → derive → send → resolve → forward), with real on-chain transactions using pre-funded test accounts.

## Prerequisites

- [Node.js](https://nodejs.org/) ≥ 24
- [pnpm](https://pnpm.io/) ≥ 10

## 1. Install dependencies

From the monorepo root:

```bash
pnpm install
```

## 2. Configure environment

Create a `.dev.vars` file with funded devnet account keys:

```
EXCHANGE_PRIVATE_KEY=0x...
SENDER_PRIVATE_KEY=0x...
RPC_URL=https://rpc.devnet.tempoxyz.dev
```

The private keys must correspond to accounts funded with PathUSD on Tempo Devnet. Set these as Cloudflare Worker secrets for production, or use org-level secrets.

## 3. Run the app

```bash
pnpm --filter virtual-addresses dev
```

Open [http://localhost:3002](http://localhost:3002).

## Architecture

```
src/
├── app.tsx # Hash-based router: #intro | #registry | #walkthrough
├── server.ts # Hono API routes (Cloudflare Workers)
├── comps/
│ ├── header.tsx # Navigation links + wallet connect
│ ├── intro-view.tsx # TIP-1022 docs landing page
│ ├── registry-view.tsx # Salt miner flow (4 steps)
│ ├── step-mine.tsx # Multi-threaded WASM salt miner
│ ├── step-register.tsx # On-chain registration
│ ├── step-generate.tsx # Offline address derivation
│ ├── step-transfer.tsx # Demo transfer
│ ├── address-anatomy.tsx # Color-coded virtual address breakdown
│ └── walkthrough/
│ ├── walkthrough-demo.tsx # 3-column layout + speed controls
│ ├── exchange-panel.tsx # Exchange actor panel
│ ├── protocol-panel.tsx # TIP-1022 registry + TIP-20 precompile
│ ├── sender-panel.tsx # Sender actor panel
│ ├── guide-overlay.tsx # Spotlight walkthrough (intro + post-demo)
│ ├── event-log.tsx # Animated transaction log
│ └── status-badge.tsx # State indicator
├── store/
│ └── walkthrough-store.ts # Zustand state machine with speed control
└── lib/
├── abi.ts # Contract ABIs + addresses
├── demo-client.ts # Fetch wrapper for server API (walkthrough)
├── virtual-address.ts # Address building/decoding utilities
├── walkthrough-types.ts # Demo state types
├── wagmi.ts # Wagmi config (tempoDevnet)
├── miner.worker.ts # WASM keccak256 mining (hash-wasm)
├── miner.pool.ts # Web Worker pool manager
├── miner.protocol.ts # Worker message protocol
├── use-miner.ts # React hook for miner
└── css.ts # cx() utility
```

## How the walkthrough works

1. **Fund** — Pre-funds exchange and sender accounts with PathUSD via `tempo_fundAddress` + `Actions.token.mint`
2. **Register** — Calls `registerVirtualMaster(salt)` with a pre-mined salt
3. **Derive** — Builds a virtual address offline: `[masterId][FDFDFD...FD magic][userTag]`
4. **Transfer** — Sender sends 100 PathUSD to the virtual address
5. **Resolve** — TIP-20 precompile detects magic bytes, looks up masterId → master address
6. **Forward** — Tokens credited to master; two Transfer events emitted

All walkthrough RPC calls are handled server-side via the Hono API routes — the client calls `/api/demo/*` endpoints.

## Key addresses

| Contract | Address |
|----------|---------|
| PathUSD (TIP-20) | `0x20C0000000000000000000000000000000000000` |
| Virtual Registry | `0xfDC0000000000000000000000000000000000000` |

## Virtual address format

```
0x [masterId 4B] [magic 10B: FDFDFDFDFDFDFDFDFDFD] [userTag 6B]
^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^
blue purple green
```

## Checks

```bash
pnpm --filter virtual-addresses check # biome lint + type check
pnpm --filter virtual-addresses build # production build
```
1 change: 1 addition & 0 deletions apps/virtual-addresses/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
17 changes: 17 additions & 0 deletions apps/virtual-addresses/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Virtual Addresses — TIP-1022 Demo</title>
<link
rel="icon"
type="image/svg+xml"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔀</text></svg>"
>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
42 changes: 42 additions & 0 deletions apps/virtual-addresses/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "virtual-addresses",
"private": true,
"type": "module",
"imports": {
"#*": "./src/*"
},
"scripts": {
"postinstall": "pnpm gen:types",
"dev": "vite dev --port 3002",
"build": "vite build",
"check": "pnpm check:biome && pnpm check:types",
"check:biome": "biome check --write --unsafe",
"check:types": "tsgo --project tsconfig.json --noEmit",
"gen:types": "wrangler types"
},
"dependencies": {
"@noble/hashes": "^1.7.2",
"@tailwindcss/vite": "catalog:",
"@tanstack/react-query": "catalog:",
"framer-motion": "catalog:",
"hash-wasm": "catalog:",
"hono": "catalog:",
"ox": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"tailwindcss": "catalog:",
"viem": "catalog:",
"wagmi": "catalog:",
"zustand": "catalog:"
},
"devDependencies": {
"@cloudflare/vite-plugin": "catalog:",
"@iconify/json": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@vitejs/plugin-react": "catalog:",
"unplugin-icons": "catalog:",
"vite": "catalog:",
"wrangler": "catalog:"
}
}
50 changes: 50 additions & 0 deletions apps/virtual-addresses/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import { Header } from '#comps/header'
import { IntroView } from '#comps/intro-view'
import { RegistryView } from '#comps/registry-view'
import { WalkthroughDemo } from '#comps/walkthrough/walkthrough-demo'

const TABS: ReadonlySet<Header.Tab> = new Set([
'intro',
'registry',
'walkthrough',
])

function parseHash(): Header.Tab {
const raw = window.location.hash.replace('#', '')
return TABS.has(raw as Header.Tab) ? (raw as Header.Tab) : 'intro'
}

function useHashTab(): [Header.Tab, (tab: Header.Tab) => void] {
const [tab, setTab] = useState<Header.Tab>(parseHash)

useEffect(() => {
const onHash = (): void => setTab(parseHash())
window.addEventListener('hashchange', onHash)
return () => window.removeEventListener('hashchange', onHash)
}, [])

const navigate = useCallback((next: Header.Tab) => {
window.location.hash = next
}, [])

return [tab, navigate]
}

export function App(): React.JSX.Element {
const [activeTab, setActiveTab] = useHashTab()

return (
<div className="min-h-screen bg-bg">
<Header activeTab={activeTab} onTabChange={setActiveTab} />
{activeTab === 'intro' ? (
<IntroView />
) : activeTab === 'registry' ? (
<RegistryView />
) : (
<WalkthroughDemo />
)}
</div>
)
}
43 changes: 43 additions & 0 deletions apps/virtual-addresses/src/comps/address-anatomy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type * as React from 'react'

export function AddressAnatomy(props: AddressAnatomy.Props): React.JSX.Element {
const { address } = props

// Virtual address: [0x][4-byte masterId][10-byte magic][6-byte userTag]
// Hex positions: [0:2][2:10][10:30][30:42]
const prefix = address.slice(0, 2)
const masterId = address.slice(2, 10)
const magic = address.slice(10, 30)
const userTag = address.slice(30, 42)

return (
<div className="space-y-2">
<div className="font-mono text-sm break-all leading-relaxed">
<span className="text-text-tertiary">{prefix}</span>
<span className="text-master-id">{masterId}</span>
<span className="text-virtual-magic">{magic}</span>
<span className="text-user-tag">{userTag}</span>
</div>
<div className="flex gap-4 text-xs">
<span className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full bg-master-id" />
<span className="text-text-secondary">masterId</span>
</span>
<span className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full bg-virtual-magic" />
<span className="text-text-secondary">magic</span>
</span>
<span className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full bg-user-tag" />
<span className="text-text-secondary">userTag</span>
</span>
</div>
</div>
)
}

export declare namespace AddressAnatomy {
type Props = {
address: string
}
}
Loading
Loading