Compare commits

..

No commits in common. "4c9febebd9db412ecbb8cc3a72076248f48c8da2" and "4bba111113baba9a1744ee6768c3b72d8c7a0ea2" have entirely different histories.

172 changed files with 369 additions and 7662 deletions

5
.gitignore vendored
View File

@ -55,8 +55,3 @@ src/components.d.ts
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
# Tauri
src-tauri/target
src-tauri/Cargo.lock
src-tauri/WixTools

View File

@ -1,173 +0,0 @@
# Tauri + WASM Integration Fix
## 🔍 Problem
WASM works in browser (`npm run dev`) but fails in Tauri webview (`npm run tauri:dev`) with error:
```
Cannot assign to read only property 'toString' of object '#<AxiosURLSearchParams>'
```
## ✅ Solutions Applied
### 1. **CSP (Content Security Policy) Update**
**File:** `src-tauri/tauri.conf.json`
Added required CSP directives for WASM:
```json
{
"security": {
"csp": {
"default-src": "'self' 'unsafe-inline' asset: https://asset.localhost",
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'",
// ... other directives with asset: protocol
},
"assetProtocol": {
"enable": true,
"scope": ["**"]
}
}
}
```
**Key changes:**
- ✅ Added `'wasm-unsafe-eval'` to `script-src` - **REQUIRED for WASM execution in Tauri 2.x**
- ✅ Added `asset:` and `https://asset.localhost` to relevant directives
- ✅ Enabled `assetProtocol` to serve assets with proper MIME types
### 2. **Vite Config Update**
**File:** `vite.config.ts`
```typescript
export default defineConfig({
optimizeDeps: {
exclude: ['@neptune/native'], // Only exclude native module
// ❌ NOT excluding @neptune/wasm - it must be bundled!
},
build: {
assetsInlineLimit: 0, // Don't inline WASM as base64
},
assetsInclude: ['**/*.wasm'], // Serve WASM with correct MIME type
})
```
**Key changes:**
- ✅ Removed `@neptune/wasm` from `exclude` and `external`
- ✅ Added `assetsInlineLimit: 0` to prevent base64 encoding
- ✅ Added `assetsInclude` for WASM MIME type
### 3. **Package.json Fix**
```json
{
"dependencies": {
"axios": "^1.7.9" // Fixed from invalid "1.13.2"
}
}
```
## 🧪 Testing
### 1. Browser (Should work)
```bash
npm run dev
```
Open http://localhost:5173/auth → Create wallet → Should work
### 2. Tauri (Should work now)
```bash
npm run tauri:dev
```
Create wallet → Should work without CSP/WASM errors
## 🐛 Additional Debugging
### If WASM still doesn't load:
#### Check 1: WASM File in Dist
```bash
npm run build
ls dist/assets/*.wasm # Should see neptune_wasm_bg-*.wasm
```
#### Check 2: Browser DevTools (in Tauri)
1. Open Tauri app
2. Right-click → Inspect Element
3. Console tab → Check for errors
4. Network tab → Filter `.wasm` → Check if WASM loads (200 status)
#### Check 3: CSP Errors
In DevTools Console, look for:
```
Refused to execute WebAssembly script...
```
If you see this → CSP is still blocking WASM
### Temporary Debug: Disable CSP
If nothing works, temporarily disable CSP to isolate the issue:
```json
// src-tauri/tauri.conf.json
{
"security": {
"dangerousDisableAssetCspModification": true, // Disable CSP temporarily
"csp": null // Remove CSP entirely for testing
}
}
```
⚠️ **WARNING:** Only use this for debugging! Re-enable CSP for production.
## 📝 Why This Happens
### Tauri vs Browser Differences
| Feature | Browser | Tauri Webview |
|---------|---------|---------------|
| CSP | Permissive by default | Strict by default |
| WASM | Always allowed | Needs `'wasm-unsafe-eval'` |
| Asset loading | HTTP(S) | Custom `asset://` protocol |
| MIME types | Auto-detected | Must be configured |
### WASM Loading in Tauri
1. Vite bundles WASM file → `dist/assets/neptune_wasm_bg-*.wasm`
2. Tauri serves it via `asset://localhost/assets/...`
3. CSP must allow:
- `script-src 'wasm-unsafe-eval'` → Execute WASM
- `connect-src asset:` → Fetch WASM file
4. AssetProtocol must serve with `Content-Type: application/wasm`
## 🔄 Next Steps After Fix
### 1. Test Full Wallet Flow
- ✅ Generate wallet (WASM)
- ✅ Display seed phrase
- ✅ Confirm seed phrase
- 🚧 Create keystore (needs Tauri commands)
### 2. Implement Tauri Commands
See `TAURI_COMMANDS_TODO.md` (if it exists, otherwise create it)
### 3. Build & Test Production
```bash
npm run tauri:build
```
## 📚 References
- [Tauri CSP Documentation](https://tauri.app/v2/reference/config/#securityconfig)
- [Vite WASM Plugin](https://vitejs.dev/guide/features.html#webassembly)
- [wasm-bindgen with Vite](https://rustwasm.github.io/wasm-bindgen/reference/deployment.html)
## 🎯 Summary
**Problem:** Tauri CSP blocked WASM execution
**Solution:** Add `'wasm-unsafe-eval'` + `asset:` protocol + proper Vite config
**Status:** Should work now! 🚀

View File

@ -1,309 +0,0 @@
# Tauri + WASM Setup Guide
## 🐛 Problem
**Symptom:**
- ✅ WASM works in browser (`npm run dev`)
- ❌ WASM fails in Tauri (`npm run tauri:dev`)
- Error: `Cannot assign to read only property 'toString'`
**Root Cause:** Tauri webview requires special configuration to load WASM files correctly.
---
## ✅ Solution: Vite Plugins for WASM
### 1. Install Required Plugins
```bash
pnpm add -D vite-plugin-wasm vite-plugin-top-level-await
```
**Why these plugins?**
- `vite-plugin-wasm`: Handles WASM file loading and initialization
- `vite-plugin-top-level-await`: Enables top-level await (required by WASM)
### 2. Update `vite.config.ts`
```typescript
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
plugins: [
vue(),
tailwindcss(),
wasm(), // ✅ Add WASM support
topLevelAwait(), // ✅ Add top-level await support
// ... other plugins
],
// Rest of config...
})
```
### 3. Tauri CSP Configuration
**File:** `src-tauri/tauri.conf.json`
Ensure CSP includes:
```json
{
"security": {
"csp": {
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'"
}
}
}
```
**Key directive:** `'wasm-unsafe-eval'` is **REQUIRED** for WASM in Tauri 2.x
---
## 🎯 How It Works
### Before (Without Plugins)
```
Tauri loads index.html
Vite bundles JS (WASM as regular asset)
Browser tries to load WASM
💥 WASM initialization fails
Error: Cannot assign to read only property
```
**Why it fails:**
- Vite doesn't know how to bundle WASM for Tauri
- WASM file is treated as regular asset
- Tauri webview can't initialize WASM correctly
### After (With Plugins)
```
Tauri loads index.html
vite-plugin-wasm handles WASM bundling
WASM file served with correct headers
vite-plugin-top-level-await enables async init
✅ WASM loads successfully
```
**Why it works:**
- `vite-plugin-wasm` handles WASM as special asset type
- Correct MIME type (`application/wasm`)
- Proper initialization order
- Compatible with Tauri's security model
---
## 📊 Comparison
| Aspect | Browser (dev) | Tauri (without plugins) | Tauri (with plugins) |
|--------|---------------|-------------------------|----------------------|
| WASM Loading | ✅ Works | ❌ Fails | ✅ Works |
| MIME Type | Auto | ❌ Wrong | ✅ Correct |
| Initialization | ✅ Success | ❌ Conflict | ✅ Success |
| CSP Compatibility | N/A | ❌ Issues | ✅ Compatible |
---
## 🔍 Debugging
### Check if WASM is Loading
**In Browser DevTools (F12 in Tauri window):**
1. **Network Tab:**
```
Look for: neptune_wasm_bg.wasm
Status: 200 OK
Type: application/wasm
```
2. **Console Tab:**
```
Should see: ✅ WASM initialized successfully
Should NOT see: ❌ WASM init error
```
3. **Sources Tab:**
```
Check if WASM file is listed under "webpack://" or "(no domain)"
```
### Common Issues
#### Issue 1: WASM file not found (404)
**Cause:** WASM not bundled correctly
**Fix:** Ensure `vite-plugin-wasm` is installed and configured
#### Issue 2: CSP violation
**Cause:** Missing `'wasm-unsafe-eval'` in CSP
**Fix:** Add to `script-src` in `tauri.conf.json`
#### Issue 3: Module initialization error
**Cause:** Top-level await not supported
**Fix:** Install `vite-plugin-top-level-await`
---
## 🧪 Testing Steps
### 1. Test in Browser (Should Work)
```bash
npm run dev
```
- Open http://localhost:5173
- Navigate to `/auth` → Create Wallet
- Check console: Should see "✅ WASM initialized successfully"
### 2. Test in Tauri (Now Should Work)
```bash
npm run tauri:dev
```
- Tauri window opens
- Navigate to `/auth` → Create Wallet
- Open DevTools (F12)
- Check console: Should see "✅ WASM initialized successfully"
- Should NOT see any `toString` errors
### 3. Test Wallet Generation
```typescript
// In CreateWalletFlow.vue
const { generateWallet } = useNeptuneWallet()
// Click "Create Wallet" button
const result = await generateWallet()
// Should return:
{
receiver_identifier: "...",
seed_phrase: ["word1", "word2", ..., "word18"]
}
```
---
## 📦 Package Versions
**Installed:**
```json
{
"devDependencies": {
"vite-plugin-wasm": "^3.5.0",
"vite-plugin-top-level-await": "^1.6.0"
}
}
```
**Compatible with:**
- Vite 7.x
- Tauri 2.x
- Vue 3.x
---
## 🔧 Configuration Files
### `vite.config.ts`
```typescript
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
plugins: [
vue(),
wasm(),
topLevelAwait(),
// ... other plugins
],
})
```
### `tauri.conf.json`
```json
{
"app": {
"security": {
"csp": {
"script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'"
}
}
}
}
```
### `package.json`
```json
{
"dependencies": {
"@neptune/wasm": "file:./packages/neptune-wasm"
},
"devDependencies": {
"vite-plugin-wasm": "^3.5.0",
"vite-plugin-top-level-await": "^1.6.0"
}
}
```
---
## 💡 Why Browser Works But Tauri Doesn't
### Browser (Chrome/Firefox)
- **Permissive WASM loading:** Browsers automatically handle WASM
- **Built-in support:** No special config needed
- **Dev server:** Vite dev server serves WASM with correct headers
### Tauri (Webview)
- **Strict security:** CSP enforced by default
- **Custom protocol:** Assets loaded via `tauri://` protocol
- **WASM restrictions:** Requires `'wasm-unsafe-eval'` in CSP
- **Asset handling:** Needs proper Vite configuration
**Tauri = Embedded Browser + Rust Backend**
- More secure (CSP enforced)
- More restrictive (needs explicit config)
- Different asset loading (custom protocol)
---
## 🚀 Result
**Before:**
```bash
npm run dev # ✅ WASM works
npm run tauri:dev # ❌ WASM fails (toString error)
```
**After:**
```bash
npm run dev # ✅ WASM works
npm run tauri:dev # ✅ WASM works! 🎉
```
---
## 📚 Resources
- [vite-plugin-wasm GitHub](https://github.com/Menci/vite-plugin-wasm)
- [vite-plugin-top-level-await GitHub](https://github.com/Menci/vite-plugin-top-level-await)
- [Tauri Security Documentation](https://tauri.app/v2/reference/config/#securityconfig)
- [WebAssembly with Vite](https://vitejs.dev/guide/features.html#webassembly)
---
## 🎯 Summary
**Problem:** Tauri can't load WASM without proper Vite configuration
**Solution:** Install `vite-plugin-wasm` + `vite-plugin-top-level-await`
**Result:** WASM works in both browser AND Tauri! 🚀

View File

@ -1,319 +0,0 @@
# UI Views Implementation
## ✅ Completed Views
Đã implement 3 views chính với Shadcn-vue components và mobile-first design:
### 1. **WalletView** (`src/views/Wallet/WalletView.vue`)
**Features:**
- ✅ Balance display (Available + Pending)
- ✅ Receiving address with copy button
- ✅ Action buttons (Send, Backup File, Backup Seed)
- ✅ Wallet status indicator
- ✅ Loading states
- ✅ Mobile responsive design
**Components used:**
- Card (CardHeader, CardTitle, CardContent)
- Button (primary, outline variants)
- Label, Separator
- Lucide icons (Wallet, Send, FileDown, Key, Copy, Check)
**Mock data:**
- Balance: `125.45678900 XNT`
- Address: `nep1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh`
**TODO:**
- [ ] Integrate with `useNeptuneWallet` composable
- [ ] Implement send transaction flow
- [ ] Implement backup features with Tauri commands
---
### 2. **UTXOView** (`src/views/UTXO/UTXOView.vue`)
**Features:**
- ✅ Summary cards (Total Count + Total Amount)
- ✅ UTXO list with mobile cards + desktop table
- ✅ Refresh button
- ✅ Loading & empty states
- ✅ Responsive layout (cards on mobile, table on desktop)
**Components used:**
- Card (CardHeader, CardTitle, CardContent)
- Button (outline, icon variants)
- Label, Separator
- Lucide icons (Database, RefreshCw)
**Mock data:**
- 3 UTXOs with hashes, amounts, block heights
- Total: `125.50000000 XNT`
**Layout:**
- **Mobile (<768px):** Individual UTXO cards
- **Desktop (≥768px):** Data table
**TODO:**
- [ ] Integrate with `getUtxos()` API
- [ ] Add pagination for large lists
- [ ] Add sorting/filtering
---
### 3. **NetworkView** (`src/views/Network/NetworkView.vue`)
**Features:**
- ✅ Network information display
- ✅ Current block height
- ✅ Last update time
- ✅ Connection status indicators
- ✅ Refresh button
- ✅ Error state with retry
- ✅ Loading states
**Components used:**
- Card (CardHeader, CardTitle, CardContent)
- Button (outline variant)
- Label, Separator
- Lucide icons (Network, Activity, RefreshCw, AlertCircle)
**Mock data:**
- Network: `Neptune mainnet`
- Block Height: `123,456`
- Status: Connected, Synced
**Auto-refresh:** Ready for 60s polling (commented out)
**TODO:**
- [ ] Integrate with `getBlockHeight()` API
- [ ] Enable auto-refresh polling
- [ ] Add more network stats (peer count, etc.)
---
## 🎨 Design System
### Colors (from Tailwind + Shadcn)
- **Primary:** Royal Blue `oklch(0.488 0.15 264.5)`
- **Background:** White (light) / Dark blue tint (dark)
- **Muted:** Light gray backgrounds
- **Foreground:** Text colors
- **Border:** Subtle borders
- **Destructive:** Error/alert red
### Typography
- **Font:** Montserrat Variable Font
- **Sizes:** text-xs, text-sm, text-base, text-lg, text-2xl, text-4xl
- **Weights:** font-medium, font-semibold, font-bold
### Spacing
- **Padding:** p-3, p-4, p-6
- **Gap:** gap-2, gap-3, gap-4, gap-6
- **Margin:** Tailwind utilities
### Components
- **Card:** Border, shadow, rounded corners
- **Button:** Primary (filled), Outline, Ghost, Icon
- **Icons:** Lucide Vue Next (size-4, size-5, size-6)
---
## 📱 Mobile Optimization
### Responsive Breakpoints
- **Mobile:** < 768px (sm)
- **Desktop:** ≥ 768px (md)
### Mobile Features
- ✅ Touch-optimized buttons (min 44px height)
- ✅ Card-based layouts for mobile
- ✅ Table view for desktop
- ✅ Bottom navigation (4 tabs)
- ✅ Safe area insets for notched devices
- ✅ Smooth scrolling
- ✅ No overscroll
### Layout Structure
```
┌─────────────────────┐
│ Header (56px) │
├─────────────────────┤
│ │
│ Main Content │
│ (scrollable) │
│ │
├─────────────────────┤
│ Bottom Nav (48px) │
└─────────────────────┘
```
---
## 🔄 Router Configuration
**Updated routes:**
```typescript
{
path: '/',
component: Layout,
children: [
{ path: '', name: 'wallet', component: WalletPage }, // Default
{ path: '/utxo', name: 'utxo', component: UTXOPage },
{ path: '/network', name: 'network', component: NetworkPage },
{ path: '/transaction-history', name: 'transaction-history', component: TransactionHistoryPage },
]
}
```
**Bottom Navigation Order:**
1. 💰 Wallet (/) - Default
2. 📦 UTXO (/utxo)
3. 🌐 Network (/network)
4. 📜 History (/transaction-history)
---
## 🚧 Commented Out Logic
### WASM-related code (temporarily disabled)
```typescript
// import { useNeptuneWallet } from '@/composables/useNeptuneWallet'
// const { getBalance, getUtxos, getBlockHeight } = useNeptuneWallet()
```
### API Calls (ready to uncomment)
```typescript
// const loadWalletData = async () => {
// const result = await getBalance()
// availableBalance.value = result.balance
// }
```
### Auto-refresh Polling (ready to enable)
```typescript
// let pollingInterval: number | null = null
// const startPolling = () => { ... }
// onMounted(() => { startPolling() })
```
---
## 🎯 Integration Steps
### Phase 1: Enable API Calls (No WASM)
1. Uncomment `useNeptuneWallet` imports
2. Uncomment API call functions
3. Test with mock API data
4. Remove mock data
### Phase 2: Enable WASM
1. Fix Tauri + WASM loading issues
2. Uncomment WASM-related logic
3. Test wallet generation flow
4. Test full integration
### Phase 3: Implement Tauri Commands
1. `generate_keys_from_seed`
2. `create_keystore`
3. `decrypt_keystore`
4. `build_transaction`
---
## 📊 Current Status
| View | UI | Mock Data | API Ready | WASM Ready | Status |
| ------- | --- | --------- | --------- | ---------- | ----------- |
| Wallet | ✅ | ✅ | 🚧 | ❌ | **UI Done** |
| UTXO | ✅ | ✅ | 🚧 | ❌ | **UI Done** |
| Network | ✅ | ✅ | 🚧 | ❌ | **UI Done** |
**Legend:**
- ✅ Complete
- 🚧 Ready to integrate
- ❌ Blocked on Tauri/WASM
---
## 🧪 Testing
### Manual Testing Steps
1. **Start dev server:** `npm run dev`
2. **Navigate to each view:**
- http://localhost:5173/ → Wallet
- http://localhost:5173/utxo → UTXO
- http://localhost:5173/network → Network
3. **Test responsive:**
- Desktop view (>768px)
- Mobile view (<768px)
- Chrome DevTools mobile emulation
4. **Test interactions:**
- Copy address button
- Refresh buttons
- Bottom navigation
- Dark mode toggle
---
## 📝 Notes
- **Dark Mode:** Fully supported via Shadcn theme variables
- **Icons:** Lucide Vue Next (tree-shakeable)
- **Animations:** Tailwind transitions + CSS animations
- **Accessibility:** ARIA labels, keyboard navigation
- **Performance:** Lazy-loaded routes, optimized re-renders
---
## 🚀 Next Steps
1. ✅ **UI Complete** - All 3 views designed and implemented
2. 🚧 **Fix Tauri WASM** - Resolve CSP and asset loading issues
3. 🚧 **Integrate APIs** - Connect to Neptune node
4. 🚧 **Implement Tauri Commands** - Keystore, transaction signing
5. 🚧 **Add Transaction History View** - List of past transactions
6. 🚧 **E2E Testing** - Full wallet flow testing
---
## 🎨 Screenshots (Recommended)
Take screenshots of:
- [ ] Wallet view (light + dark mode)
- [ ] UTXO view (mobile cards + desktop table)
- [ ] Network view (all states)
- [ ] Bottom navigation active states
Store in: `docs/screenshots/`

View File

@ -14,12 +14,12 @@
<meta name="theme-color" content="#3f51b5" /> <meta name="theme-color" content="#3f51b5" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Neptune Privacy" /> <meta name="apple-mobile-web-app-title" content="Neptune Wallet" />
<!-- App Description --> <!-- App Description -->
<meta <meta
name="description" name="description"
content="Neptune Privacy - Secure cryptocurrency wallet for Neptune network" content="Neptune Wallet - Secure cryptocurrency wallet for Neptune blockchain"
/> />
<!-- Google Fonts - Inter (Modern, clean, mobile-optimized) --> <!-- Google Fonts - Inter (Modern, clean, mobile-optimized) -->
@ -30,7 +30,7 @@
rel="stylesheet" rel="stylesheet"
/> />
<title>Neptune Privacy</title> <title>Neptune Wallet</title>
</head> </head>
<body> <body>

View File

@ -1,28 +1,18 @@
{ {
"name": "neptune-privacy", "name": "neptune-wallet",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"workspaces": [
"packages/*"
],
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc -b && vite build", "build": "vue-tsc -b && vite build",
"preview": "vite preview", "preview": "vite preview",
"type-check": "vue-tsc --build --force", "type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .eslintignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .eslintignore",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,vue,css,scss,json}\"", "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,vue,css,scss,json}\""
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build"
}, },
"dependencies": { "dependencies": {
"@neptune/native": "file:./packages/neptune-native",
"@neptune/wasm": "file:./packages/neptune-wasm",
"@tailwindcss/vite": "^4.1.17", "@tailwindcss/vite": "^4.1.17",
"@tanstack/vue-form": "^1.26.0",
"@tauri-apps/api": "^2.9.0",
"@vueuse/core": "^14.0.0", "@vueuse/core": "^14.0.0",
"axios": "^1.7.9", "axios": "^1.7.9",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@ -35,13 +25,10 @@
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"vue": "^3.5.24", "vue": "^3.5.24",
"vue-i18n": "^10.0.8", "vue-i18n": "^10.0.8",
"vue-router": "^4.5.0", "vue-router": "^4.5.0"
"vue-sonner": "^2.0.9",
"zod": "^4.1.13"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.15.0", "@rushstack/eslint-patch": "^1.15.0",
"@tauri-apps/cli": "^2.9.4",
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.0.0",
@ -56,8 +43,6 @@
"unplugin-auto-import": "^20.2.0", "unplugin-auto-import": "^20.2.0",
"unplugin-vue-components": "^30.0.0", "unplugin-vue-components": "^30.0.0",
"vite": "^7.2.4", "vite": "^7.2.4",
"vite-plugin-top-level-await": "^1.6.0",
"vite-plugin-wasm": "^3.5.0",
"vue-tsc": "^3.1.4" "vue-tsc": "^3.1.4"
} }
} }

View File

@ -1,79 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/* auto-generated by NAPI-RS */
export declare function initNativeModule(): string
export declare function quickVmTest(): string
export declare function getVersion(): string
export declare class WalletManager {
constructor()
/**
* Generate spending key from BIP39 seed phrase
*
* # Arguments
* * `seed_phrase` - Array of words (12, 15, 18, 21, or 24 words)
*
* # Returns
* JSON with spending_key_hex, view_key_hex, receiver_identifier
*/
generateKeysFromSeed(seedPhrase: Array<string>): string
/**
* Generate lock_script_and_witness (transaction signature)
*
* # Arguments
* * `spending_key_hex` - Hex-encoded spending key from generate_keys_from_seed
*
* # Returns
* JSON with lock_script_and_witness in hex format
*/
createLockScriptAndWitness(spendingKeyHex: string): string
/** Derive ViewKey hex (0x-prefixed bincode) from a Generation spending key hex */
spendingKeyToViewKeyHex(spendingKeyHex: string): string
/** Call wallet_getAdditionRecordsFromViewKey and return JSON as string */
getAdditionRecordsFromViewKeyCall(rpcUrl: string, viewKeyHex: string, startBlock: number, endBlock: number | undefined | null, maxSearchDepth: number): Promise<string>
/**
* Build RPC request to get UTXOs from view key
*
* # Arguments
* * `view_key_hex` - Hex-encoded view key from generate_keys_from_seed
* * `start_block` - Starting block height (0 for genesis)
* * `end_block` - Ending block height (current tip)
* * `max_search_depth` - Maximum blocks to search (default: 1000)
*
* # Returns
* JSON-RPC request ready to send
*
* # Note
* Method name format: namespace_method (e.g. wallet_getUtxosFromViewKey)
*/
buildGetUtxosRequest(viewKeyHex: string, startBlock: number, endBlock: number, maxSearchDepth?: number | undefined | null): string
/** Build RPC request to test chain height (for connectivity testing) */
buildTestRpcRequest(): string
/** Build JSON-RPC request to fetch current chain height */
buildChainHeightRequest(): string
/** Build JSON-RPC request to fetch current chain header (tip) */
buildChainHeaderRequest(): string
/** Get network information */
getNetworkInfo(): string
getChainHeightCall(rpcUrl: string): Promise<string>
/** Call node_getState to get server state information */
getStateCall(rpcUrl: string): Promise<string>
/** Call mempool_submitTransaction to broadcast a pre-built transaction */
submitTransactionCall(rpcUrl: string, transactionHex: string): Promise<string>
getUtxosFromViewKeyCall(rpcUrl: string, viewKeyHex: string, startBlock: number, maxSearchDepth?: number | undefined | null): Promise<string>
getArchivalMutatorSet(rpcUrl: string): Promise<string>
/**
* Build JSON-RPC request to find the canonical block that created a UTXO (by addition_record)
* Method: archival_getUtxoCreationBlock
*/
buildGetUtxoCreationBlockRequest(additionRecordHex: string, maxSearchDepth?: number | undefined | null): string
/** Perform JSON-RPC call to find the canonical block that created a UTXO (by addition_record) */
getUtxoCreationBlockCall(rpcUrl: string, additionRecordHex: string, maxSearchDepth?: number | undefined | null): Promise<string>
/** Call wallet_sendWithSpendingKey to build and broadcast transaction */
generateUtxoWithProofCall(rpcUrl: string, utxoHex: string, additionRecordHex: string, senderRandomnessHex: string, receiverPreimageHex: string, maxSearchDepth: string): Promise<string>
}
export declare class SimpleTransactionBuilder {
constructor()
buildTransaction(rpcUrl: string, spendingKeyHex: string, inputAdditionRecords: Array<string>, minBlockHeight: number, outputAddresses: Array<string>, outputAmounts: Array<string>, fee: string): Promise<string>
}

View File

@ -1,319 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/* auto-generated by NAPI-RS */
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
const { platform, arch } = process
let nativeBinding = null
let localFileExisted = false
let loadError = null
function isMusl() {
// For Node 10
if (!process.report || typeof process.report.getReport !== 'function') {
try {
const lddPath = require('child_process').execSync('which ldd').toString().trim()
return readFileSync(lddPath, 'utf8').includes('musl')
} catch (e) {
return true
}
} else {
const { glibcVersionRuntime } = process.report.getReport().header
return !glibcVersionRuntime
}
}
switch (platform) {
case 'android':
switch (arch) {
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'neptune-native.android-arm64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.android-arm64.node')
} else {
nativeBinding = require('neptune-native-android-arm64')
}
} catch (e) {
loadError = e
}
break
case 'arm':
localFileExisted = existsSync(join(__dirname, 'neptune-native.android-arm-eabi.node'))
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.android-arm-eabi.node')
} else {
nativeBinding = require('neptune-native-android-arm-eabi')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Android ${arch}`)
}
break
case 'win32':
switch (arch) {
case 'x64':
localFileExisted = existsSync(
join(__dirname, 'neptune-native.win32-x64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.win32-x64-msvc.node')
} else {
nativeBinding = require('neptune-native-win32-x64-msvc')
}
} catch (e) {
loadError = e
}
break
case 'ia32':
localFileExisted = existsSync(
join(__dirname, 'neptune-native.win32-ia32-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.win32-ia32-msvc.node')
} else {
nativeBinding = require('neptune-native-win32-ia32-msvc')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'neptune-native.win32-arm64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.win32-arm64-msvc.node')
} else {
nativeBinding = require('neptune-native-win32-arm64-msvc')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Windows: ${arch}`)
}
break
case 'darwin':
localFileExisted = existsSync(join(__dirname, 'neptune-native.darwin-universal.node'))
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.darwin-universal.node')
} else {
nativeBinding = require('neptune-native-darwin-universal')
}
break
} catch {}
switch (arch) {
case 'x64':
localFileExisted = existsSync(join(__dirname, 'neptune-native.darwin-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.darwin-x64.node')
} else {
nativeBinding = require('neptune-native-darwin-x64')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'neptune-native.darwin-arm64.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.darwin-arm64.node')
} else {
nativeBinding = require('neptune-native-darwin-arm64')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on macOS: ${arch}`)
}
break
case 'freebsd':
if (arch !== 'x64') {
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
}
localFileExisted = existsSync(join(__dirname, 'neptune-native.freebsd-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.freebsd-x64.node')
} else {
nativeBinding = require('neptune-native-freebsd-x64')
}
} catch (e) {
loadError = e
}
break
case 'linux':
switch (arch) {
case 'x64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-x64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-x64-musl.node')
} else {
nativeBinding = require('neptune-native-linux-x64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-x64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-x64-gnu.node')
} else {
nativeBinding = require('neptune-native-linux-x64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-arm64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-arm64-musl.node')
} else {
nativeBinding = require('neptune-native-linux-arm64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-arm64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-arm64-gnu.node')
} else {
nativeBinding = require('neptune-native-linux-arm64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-arm-musleabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-arm-musleabihf.node')
} else {
nativeBinding = require('neptune-native-linux-arm-musleabihf')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-arm-gnueabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-arm-gnueabihf.node')
} else {
nativeBinding = require('neptune-native-linux-arm-gnueabihf')
}
} catch (e) {
loadError = e
}
}
break
case 'riscv64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-riscv64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-riscv64-musl.node')
} else {
nativeBinding = require('neptune-native-linux-riscv64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-riscv64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-riscv64-gnu.node')
} else {
nativeBinding = require('neptune-native-linux-riscv64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 's390x':
localFileExisted = existsSync(
join(__dirname, 'neptune-native.linux-s390x-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./neptune-native.linux-s390x-gnu.node')
} else {
nativeBinding = require('neptune-native-linux-s390x-gnu')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Linux: ${arch}`)
}
break
default:
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
}
if (!nativeBinding) {
if (loadError) {
throw loadError
}
throw new Error(`Failed to load native binding`)
}
const { initNativeModule, quickVmTest, getVersion, WalletManager, SimpleTransactionBuilder } = nativeBinding
module.exports.initNativeModule = initNativeModule
module.exports.quickVmTest = quickVmTest
module.exports.getVersion = getVersion
module.exports.WalletManager = WalletManager
module.exports.SimpleTransactionBuilder = SimpleTransactionBuilder

View File

@ -1,45 +0,0 @@
{
"name": "@neptune/native",
"version": "0.1.0",
"description": "Native Node.js addon for Neptune transaction building",
"main": "index.js",
"files": [
"index.js",
"index.d.ts",
"*.node"
],
"scripts": {
"build": "npx napi build --platform --release",
"build:debug": "npx napi build --platform",
"prepublishOnly": "napi prepublish -t npm",
"test": "cargo test",
"universal": "napi universal"
},
"napi": {
"name": "neptune-native",
"triples": {
"defaults": true,
"additional": [
"x86_64-unknown-linux-musl",
"aarch64-unknown-linux-gnu",
"aarch64-apple-darwin",
"aarch64-unknown-linux-musl"
]
}
},
"devDependencies": {
"@napi-rs/cli": "^2.18.4"
},
"keywords": [
"neptune",
"blockchain",
"native",
"napi",
"vm",
"proof"
],
"license": "Apache-2.0",
"engines": {
"node": ">= 16"
}
}

View File

View File

@ -1,209 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* Generate a new random seed phrase (18 words, 192 bits entropy)
* This is completely offline - uses browser's crypto.getRandomValues()
*/
export function generate_seed(): string;
/**
* Get view key and address from seed phrase (offline) - COMPATIBLE WITH NEPTUNE-CORE
*
* This derives the view key from a BIP39 seed phrase using neptune-crypto-core.
* The view key can be used with wallet_getUtxosFromViewKey RPC method.
*
* Input: JSON string with seed_phrase array and network ("mainnet" or "testnet")
* Output: JSON string with receiver_identifier, view_key (hex), and address (bech32m)
*
* Example:
* ```javascript
* const result = get_viewkey('["word1", "word2", ...]', "testnet");
* const { receiver_identifier, view_key, address } = JSON.parse(result);
* console.log('View key:', view_key); // Compatible with neptune-core!
* ```
*/
export function get_viewkey(seed_phrase_json: string, network: string): string;
/**
* Get receiving address from seed phrase (offline)
* Input: JSON string with seed_phrase array and network
* Output: bech32m encoded address string
*/
export function address_from_seed(seed_phrase_json: string, network: string): string;
/**
* Validate a seed phrase (offline)
*/
export function validate_seed_phrase(seed_phrase_json: string): boolean;
/**
* Decode view key from hex string (offline)
*/
export function decode_viewkey(view_key_hex: string): string;
/**
* Prepare transaction data for server-side signing
*
* This prepares transaction details but does NOT sign locally.
* Instead, it exports the spending key seed so the server can sign.
*
* Input: JSON string with BuildTxRequest
* Output: JSON string with SignedTxData (contains seed for server signing)
*
* Example:
* ```javascript
* const request = {
* seed_phrase: ["word1", "word2", ...],
* inputs: [{addition_record: "0xabc..."}],
* outputs: [{address: "nep1...", amount: "50.0"}],
* fee: "0.01",
* network: "testnet"
* };
* const txData = build_and_sign_tx(JSON.stringify(request));
* // Now broadcast via JSON-RPC: wallet_broadcastSignedTransaction(txData)
* ```
*/
export function build_and_sign_tx(request_json: string): string;
/**
* Get wallet balance via JSON-RPC (Neptune core ext format)
* Uses wallet_balance method from Wallet namespace
*/
export function get_balance(rpc_url: string): Promise<string>;
/**
* Send transaction via JSON-RPC
* Note: Neptune core ext may not have direct "send" method in public RPC
* This is a placeholder - check actual available methods
*/
export function send_tx_jsonrpc(rpc_url: string, to_address: string, amount: string, fee: string): Promise<string>;
/**
* Get block height via JSON-RPC (Neptune core ext format)
* Uses chain_height method from Chain namespace
*/
export function get_block_height(rpc_url: string): Promise<bigint>;
/**
* Get network info via JSON-RPC (Neptune core ext format)
* Uses node_network method from Node namespace
*/
export function get_network_info(rpc_url: string): Promise<string>;
/**
* Get wallet addresses from Neptune core (PRODUCTION - EXACT Neptune addresses!)
* Returns both generation_address and symmetric_address
*/
export function get_wallet_address(rpc_url: string): Promise<string>;
/**
* Get view key from Neptune core (RECOMMENDED)
* This exports the proper view key format from Neptune core's wallet
* Returns a hex-encoded view key that can be used with wallet_getUtxosFromViewKey
*/
export function get_viewkey_from_neptune(rpc_url: string): Promise<string>;
/**
* Get UTXOs from view key (scan blockchain)
* This calls Neptune core's wallet_getUtxosFromViewKey JSON-RPC method
*
* Input: view_key (hex string from neptune-core format), start_block, end_block (optional)
* Output: JSON string with list of UTXOs
*/
export function get_utxos_from_viewkey(rpc_url: string, view_key_hex: string, start_block: bigint, end_block?: bigint | null): Promise<string>;
/**
* Generate viewkey from BIP39 seed phrase
*
* # Arguments
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
* * `key_index` - Key derivation index (default: 0)
*
* # Returns
* Hex-encoded viewkey compatible with neptune-core
*/
export function generate_viewkey_from_phrase(phrase: string, key_index: bigint): string;
/**
* Generate receiving address from BIP39 seed phrase
*
* # Arguments
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
* * `key_index` - Key derivation index (default: 0)
* * `testnet` - Use testnet network (true) or mainnet (false)
*
* # Returns
* Bech32m-encoded receiving address
*/
export function generate_address_from_phrase(phrase: string, key_index: bigint, testnet: boolean): string;
/**
* Get receiver identifier from viewkey hex
*/
export function get_receiver_id_from_viewkey(viewkey_hex: string): string;
/**
* Debug: Get detailed key derivation info
*/
export function debug_key_derivation(phrase: string, key_index: bigint): string;
/**
* Sign transaction offline (WASM client-side signing)
* This creates lock_script_and_witness WITHOUT exposing spending key to server
*
* # Arguments
* * `phrase` - BIP39 seed phrase (18 words)
* * `utxos_json` - JSON array of UTXOs from get_utxos (with addition_record)
* * `outputs_json` - JSON array of outputs [{address, amount}, ...]
* * `fee` - Transaction fee as string
* * `key_index` - Key derivation index (usually 0)
* * `testnet` - Network selection
*
* # Returns
* JSON containing:
* - lock_script_and_witness: hex-encoded signature
* - view_key: hex-encoded view key
* - inputs: array of addition_records
* - outputs: array of {address, amount}
* - fee: fee string
*/
export function sign_transaction_offline(phrase: string, utxos_json: string, outputs_json: string, fee: string, key_index: bigint, testnet: boolean): string;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly generate_seed: () => [number, number, number, number];
readonly get_viewkey: (a: number, b: number, c: number, d: number) => [number, number, number, number];
readonly address_from_seed: (a: number, b: number, c: number, d: number) => [number, number, number, number];
readonly validate_seed_phrase: (a: number, b: number) => [number, number, number];
readonly decode_viewkey: (a: number, b: number) => [number, number, number, number];
readonly build_and_sign_tx: (a: number, b: number) => [number, number, number, number];
readonly get_balance: (a: number, b: number) => any;
readonly send_tx_jsonrpc: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => any;
readonly get_block_height: (a: number, b: number) => any;
readonly get_network_info: (a: number, b: number) => any;
readonly get_wallet_address: (a: number, b: number) => any;
readonly get_viewkey_from_neptune: (a: number, b: number) => any;
readonly get_utxos_from_viewkey: (a: number, b: number, c: number, d: number, e: bigint, f: number, g: bigint) => any;
readonly generate_viewkey_from_phrase: (a: number, b: number, c: bigint) => [number, number, number, number];
readonly generate_address_from_phrase: (a: number, b: number, c: bigint, d: number) => [number, number, number, number];
readonly get_receiver_id_from_viewkey: (a: number, b: number) => [number, number, number, number];
readonly debug_key_derivation: (a: number, b: number, c: bigint) => [number, number, number, number];
readonly sign_transaction_offline: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: bigint, j: number) => [number, number, number, number];
readonly __wbindgen_exn_store: (a: number) => void;
readonly __externref_table_alloc: () => number;
readonly __wbindgen_export_2: WebAssembly.Table;
readonly __wbindgen_export_3: WebAssembly.Table;
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __externref_table_dealloc: (a: number) => void;
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
readonly closure49_externref_shim: (a: number, b: number, c: any) => void;
readonly closure268_externref_shim: (a: number, b: number, c: any, d: any) => void;
readonly __wbindgen_start: () => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;

View File

@ -1,984 +0,0 @@
let wasm;
function addToExternrefTable0(obj) {
const idx = wasm.__externref_table_alloc();
wasm.__wbindgen_export_2.set(idx, obj);
return idx;
}
function handleError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
const idx = addToExternrefTable0(e);
wasm.__wbindgen_exn_store(idx);
}
}
let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}
function getArrayU8FromWasm0(ptr, len) {
ptr = ptr >>> 0;
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}
function isLikeNone(x) {
return x === undefined || x === null;
}
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(state => {
wasm.__wbindgen_export_3.get(state.dtor)(state.a, state.b)
});
function makeMutClosure(arg0, arg1, dtor, f) {
const state = { a: arg0, b: arg1, cnt: 1, dtor };
const real = (...args) => {
// First up with a closure we increment the internal reference
// count. This ensures that the Rust closure environment won't
// be deallocated while we're invoking it.
state.cnt++;
const a = state.a;
state.a = 0;
try {
return f(a, state.b, ...args);
} finally {
if (--state.cnt === 0) {
wasm.__wbindgen_export_3.get(state.dtor)(a, state.b);
CLOSURE_DTORS.unregister(state);
} else {
state.a = a;
}
}
};
real.original = state;
CLOSURE_DTORS.register(real, state, state);
return real;
}
function debugString(val) {
// primitive types
const type = typeof val;
if (type == 'number' || type == 'boolean' || val == null) {
return `${val}`;
}
if (type == 'string') {
return `"${val}"`;
}
if (type == 'symbol') {
const description = val.description;
if (description == null) {
return 'Symbol';
} else {
return `Symbol(${description})`;
}
}
if (type == 'function') {
const name = val.name;
if (typeof name == 'string' && name.length > 0) {
return `Function(${name})`;
} else {
return 'Function';
}
}
// objects
if (Array.isArray(val)) {
const length = val.length;
let debug = '[';
if (length > 0) {
debug += debugString(val[0]);
}
for(let i = 1; i < length; i++) {
debug += ', ' + debugString(val[i]);
}
debug += ']';
return debug;
}
// Test for built-in
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
let className;
if (builtInMatches && builtInMatches.length > 1) {
className = builtInMatches[1];
} else {
// Failed to match the standard '[object ClassName]'
return toString.call(val);
}
if (className == 'Object') {
// we're a user defined class or Object
// JSON.stringify avoids problems with cycles, and is generally much
// easier than looping through ownProperties of `val`.
try {
return 'Object(' + JSON.stringify(val) + ')';
} catch (_) {
return 'Object';
}
}
// errors
if (val instanceof Error) {
return `${val.name}: ${val.message}\n${val.stack}`;
}
// TODO we could test for more things here, like `Set`s and `Map`s.
return className;
}
let WASM_VECTOR_LEN = 0;
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
: function (arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length
};
});
function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0;
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
let len = arg.length;
let ptr = malloc(len, 1) >>> 0;
const mem = getUint8ArrayMemory0();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
ptr = realloc(ptr, len, offset, 1) >>> 0;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
let cachedDataViewMemory0 = null;
function getDataViewMemory0() {
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
}
return cachedDataViewMemory0;
}
function takeFromExternrefTable0(idx) {
const value = wasm.__wbindgen_export_2.get(idx);
wasm.__externref_table_dealloc(idx);
return value;
}
/**
* Generate a new random seed phrase (18 words, 192 bits entropy)
* This is completely offline - uses browser's crypto.getRandomValues()
* @returns {string}
*/
export function generate_seed() {
let deferred2_0;
let deferred2_1;
try {
const ret = wasm.generate_seed();
var ptr1 = ret[0];
var len1 = ret[1];
if (ret[3]) {
ptr1 = 0; len1 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred2_0 = ptr1;
deferred2_1 = len1;
return getStringFromWasm0(ptr1, len1);
} finally {
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
}
}
/**
* Get view key and address from seed phrase (offline) - COMPATIBLE WITH NEPTUNE-CORE
*
* This derives the view key from a BIP39 seed phrase using neptune-crypto-core.
* The view key can be used with wallet_getUtxosFromViewKey RPC method.
*
* Input: JSON string with seed_phrase array and network ("mainnet" or "testnet")
* Output: JSON string with receiver_identifier, view_key (hex), and address (bech32m)
*
* Example:
* ```javascript
* const result = get_viewkey('["word1", "word2", ...]', "testnet");
* const { receiver_identifier, view_key, address } = JSON.parse(result);
* console.log('View key:', view_key); // Compatible with neptune-core!
* ```
* @param {string} seed_phrase_json
* @param {string} network
* @returns {string}
*/
export function get_viewkey(seed_phrase_json, network) {
let deferred4_0;
let deferred4_1;
try {
const ptr0 = passStringToWasm0(seed_phrase_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(network, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.get_viewkey(ptr0, len0, ptr1, len1);
var ptr3 = ret[0];
var len3 = ret[1];
if (ret[3]) {
ptr3 = 0; len3 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred4_0 = ptr3;
deferred4_1 = len3;
return getStringFromWasm0(ptr3, len3);
} finally {
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
}
}
/**
* Get receiving address from seed phrase (offline)
* Input: JSON string with seed_phrase array and network
* Output: bech32m encoded address string
* @param {string} seed_phrase_json
* @param {string} network
* @returns {string}
*/
export function address_from_seed(seed_phrase_json, network) {
let deferred4_0;
let deferred4_1;
try {
const ptr0 = passStringToWasm0(seed_phrase_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(network, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.address_from_seed(ptr0, len0, ptr1, len1);
var ptr3 = ret[0];
var len3 = ret[1];
if (ret[3]) {
ptr3 = 0; len3 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred4_0 = ptr3;
deferred4_1 = len3;
return getStringFromWasm0(ptr3, len3);
} finally {
wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
}
}
/**
* Validate a seed phrase (offline)
* @param {string} seed_phrase_json
* @returns {boolean}
*/
export function validate_seed_phrase(seed_phrase_json) {
const ptr0 = passStringToWasm0(seed_phrase_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.validate_seed_phrase(ptr0, len0);
if (ret[2]) {
throw takeFromExternrefTable0(ret[1]);
}
return ret[0] !== 0;
}
/**
* Decode view key from hex string (offline)
* @param {string} view_key_hex
* @returns {string}
*/
export function decode_viewkey(view_key_hex) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passStringToWasm0(view_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.decode_viewkey(ptr0, len0);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
/**
* Prepare transaction data for server-side signing
*
* This prepares transaction details but does NOT sign locally.
* Instead, it exports the spending key seed so the server can sign.
*
* Input: JSON string with BuildTxRequest
* Output: JSON string with SignedTxData (contains seed for server signing)
*
* Example:
* ```javascript
* const request = {
* seed_phrase: ["word1", "word2", ...],
* inputs: [{addition_record: "0xabc..."}],
* outputs: [{address: "nep1...", amount: "50.0"}],
* fee: "0.01",
* network: "testnet"
* };
* const txData = build_and_sign_tx(JSON.stringify(request));
* // Now broadcast via JSON-RPC: wallet_broadcastSignedTransaction(txData)
* ```
* @param {string} request_json
* @returns {string}
*/
export function build_and_sign_tx(request_json) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.build_and_sign_tx(ptr0, len0);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
/**
* Get wallet balance via JSON-RPC (Neptune core ext format)
* Uses wallet_balance method from Wallet namespace
* @param {string} rpc_url
* @returns {Promise<string>}
*/
export function get_balance(rpc_url) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.get_balance(ptr0, len0);
return ret;
}
/**
* Send transaction via JSON-RPC
* Note: Neptune core ext may not have direct "send" method in public RPC
* This is a placeholder - check actual available methods
* @param {string} rpc_url
* @param {string} to_address
* @param {string} amount
* @param {string} fee
* @returns {Promise<string>}
*/
export function send_tx_jsonrpc(rpc_url, to_address, amount, fee) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(to_address, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(amount, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
const ptr3 = passStringToWasm0(fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len3 = WASM_VECTOR_LEN;
const ret = wasm.send_tx_jsonrpc(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
return ret;
}
/**
* Get block height via JSON-RPC (Neptune core ext format)
* Uses chain_height method from Chain namespace
* @param {string} rpc_url
* @returns {Promise<bigint>}
*/
export function get_block_height(rpc_url) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.get_block_height(ptr0, len0);
return ret;
}
/**
* Get network info via JSON-RPC (Neptune core ext format)
* Uses node_network method from Node namespace
* @param {string} rpc_url
* @returns {Promise<string>}
*/
export function get_network_info(rpc_url) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.get_network_info(ptr0, len0);
return ret;
}
/**
* Get wallet addresses from Neptune core (PRODUCTION - EXACT Neptune addresses!)
* Returns both generation_address and symmetric_address
* @param {string} rpc_url
* @returns {Promise<string>}
*/
export function get_wallet_address(rpc_url) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.get_wallet_address(ptr0, len0);
return ret;
}
/**
* Get view key from Neptune core (RECOMMENDED)
* This exports the proper view key format from Neptune core's wallet
* Returns a hex-encoded view key that can be used with wallet_getUtxosFromViewKey
* @param {string} rpc_url
* @returns {Promise<string>}
*/
export function get_viewkey_from_neptune(rpc_url) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.get_viewkey_from_neptune(ptr0, len0);
return ret;
}
/**
* Get UTXOs from view key (scan blockchain)
* This calls Neptune core's wallet_getUtxosFromViewKey JSON-RPC method
*
* Input: view_key (hex string from neptune-core format), start_block, end_block (optional)
* Output: JSON string with list of UTXOs
* @param {string} rpc_url
* @param {string} view_key_hex
* @param {bigint} start_block
* @param {bigint | null} [end_block]
* @returns {Promise<string>}
*/
export function get_utxos_from_viewkey(rpc_url, view_key_hex, start_block, end_block) {
const ptr0 = passStringToWasm0(rpc_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(view_key_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ret = wasm.get_utxos_from_viewkey(ptr0, len0, ptr1, len1, start_block, !isLikeNone(end_block), isLikeNone(end_block) ? BigInt(0) : end_block);
return ret;
}
/**
* Generate viewkey from BIP39 seed phrase
*
* # Arguments
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
* * `key_index` - Key derivation index (default: 0)
*
* # Returns
* Hex-encoded viewkey compatible with neptune-core
* @param {string} phrase
* @param {bigint} key_index
* @returns {string}
*/
export function generate_viewkey_from_phrase(phrase, key_index) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.generate_viewkey_from_phrase(ptr0, len0, key_index);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
/**
* Generate receiving address from BIP39 seed phrase
*
* # Arguments
* * `phrase` - Space-separated BIP39 seed phrase (12-24 words)
* * `key_index` - Key derivation index (default: 0)
* * `testnet` - Use testnet network (true) or mainnet (false)
*
* # Returns
* Bech32m-encoded receiving address
* @param {string} phrase
* @param {bigint} key_index
* @param {boolean} testnet
* @returns {string}
*/
export function generate_address_from_phrase(phrase, key_index, testnet) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.generate_address_from_phrase(ptr0, len0, key_index, testnet);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
/**
* Get receiver identifier from viewkey hex
* @param {string} viewkey_hex
* @returns {string}
*/
export function get_receiver_id_from_viewkey(viewkey_hex) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passStringToWasm0(viewkey_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.get_receiver_id_from_viewkey(ptr0, len0);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
/**
* Debug: Get detailed key derivation info
* @param {string} phrase
* @param {bigint} key_index
* @returns {string}
*/
export function debug_key_derivation(phrase, key_index) {
let deferred3_0;
let deferred3_1;
try {
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.debug_key_derivation(ptr0, len0, key_index);
var ptr2 = ret[0];
var len2 = ret[1];
if (ret[3]) {
ptr2 = 0; len2 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred3_0 = ptr2;
deferred3_1 = len2;
return getStringFromWasm0(ptr2, len2);
} finally {
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
}
}
/**
* Sign transaction offline (WASM client-side signing)
* This creates lock_script_and_witness WITHOUT exposing spending key to server
*
* # Arguments
* * `phrase` - BIP39 seed phrase (18 words)
* * `utxos_json` - JSON array of UTXOs from get_utxos (with addition_record)
* * `outputs_json` - JSON array of outputs [{address, amount}, ...]
* * `fee` - Transaction fee as string
* * `key_index` - Key derivation index (usually 0)
* * `testnet` - Network selection
*
* # Returns
* JSON containing:
* - lock_script_and_witness: hex-encoded signature
* - view_key: hex-encoded view key
* - inputs: array of addition_records
* - outputs: array of {address, amount}
* - fee: fee string
* @param {string} phrase
* @param {string} utxos_json
* @param {string} outputs_json
* @param {string} fee
* @param {bigint} key_index
* @param {boolean} testnet
* @returns {string}
*/
export function sign_transaction_offline(phrase, utxos_json, outputs_json, fee, key_index, testnet) {
let deferred6_0;
let deferred6_1;
try {
const ptr0 = passStringToWasm0(phrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(utxos_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(outputs_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
const ptr3 = passStringToWasm0(fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len3 = WASM_VECTOR_LEN;
const ret = wasm.sign_transaction_offline(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, key_index, testnet);
var ptr5 = ret[0];
var len5 = ret[1];
if (ret[3]) {
ptr5 = 0; len5 = 0;
throw takeFromExternrefTable0(ret[2]);
}
deferred6_0 = ptr5;
deferred6_1 = len5;
return getStringFromWasm0(ptr5, len5);
} finally {
wasm.__wbindgen_free(deferred6_0, deferred6_1, 1);
}
}
function __wbg_adapter_24(arg0, arg1, arg2) {
wasm.closure49_externref_shim(arg0, arg1, arg2);
}
function __wbg_adapter_83(arg0, arg1, arg2, arg3) {
wasm.closure268_externref_shim(arg0, arg1, arg2, arg3);
}
const __wbindgen_enum_RequestMode = ["same-origin", "no-cors", "cors", "navigate"];
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) {
const ret = arg0.call(arg1);
return ret;
}, arguments) };
imports.wbg.__wbg_call_7cccdd69e0791ae2 = function() { return handleError(function (arg0, arg1, arg2) {
const ret = arg0.call(arg1, arg2);
return ret;
}, arguments) };
imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) {
console.error(arg0);
};
imports.wbg.__wbg_fetch_b7bf320f681242d2 = function(arg0, arg1) {
const ret = arg0.fetch(arg1);
return ret;
};
imports.wbg.__wbg_getRandomValues_bb689d73e9ab7af6 = function(arg0, arg1) {
crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
};
imports.wbg.__wbg_headers_7852a8ea641c1379 = function(arg0) {
const ret = arg0.headers;
return ret;
};
imports.wbg.__wbg_instanceof_Response_f2cc20d9f7dfd644 = function(arg0) {
let result;
try {
result = arg0 instanceof Response;
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) {
let result;
try {
result = arg0 instanceof Window;
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_log_7fbbb36c3875e666 = function(arg0, arg1) {
console.log(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbg_log_c222819a41e063d3 = function(arg0) {
console.log(arg0);
};
imports.wbg.__wbg_new_23a2665fac83c611 = function(arg0, arg1) {
try {
var state0 = {a: arg0, b: arg1};
var cb0 = (arg0, arg1) => {
const a = state0.a;
state0.a = 0;
try {
return __wbg_adapter_83(a, state0.b, arg0, arg1);
} finally {
state0.a = a;
}
};
const ret = new Promise(cb0);
return ret;
} finally {
state0.a = state0.b = 0;
}
};
imports.wbg.__wbg_new_405e22f390576ce2 = function() {
const ret = new Object();
return ret;
};
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) {
const ret = new Function(getStringFromWasm0(arg0, arg1));
return ret;
};
imports.wbg.__wbg_newwithstrandinit_06c535e0a867c635 = function() { return handleError(function (arg0, arg1, arg2) {
const ret = new Request(getStringFromWasm0(arg0, arg1), arg2);
return ret;
}, arguments) };
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) {
queueMicrotask(arg0);
};
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) {
const ret = arg0.queueMicrotask;
return ret;
};
imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) {
const ret = Promise.resolve(arg0);
return ret;
};
imports.wbg.__wbg_set_11cd83f45504cedf = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
arg0.set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) };
imports.wbg.__wbg_setbody_5923b78a95eedf29 = function(arg0, arg1) {
arg0.body = arg1;
};
imports.wbg.__wbg_setmethod_3c5280fe5d890842 = function(arg0, arg1, arg2) {
arg0.method = getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_setmode_5dc300b865044b65 = function(arg0, arg1) {
arg0.mode = __wbindgen_enum_RequestMode[arg1];
};
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() {
const ret = typeof global === 'undefined' ? null : global;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() {
const ret = typeof globalThis === 'undefined' ? null : globalThis;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() {
const ret = typeof self === 'undefined' ? null : self;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() {
const ret = typeof window === 'undefined' ? null : window;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_text_7805bea50de2af49 = function() { return handleError(function (arg0) {
const ret = arg0.text();
return ret;
}, arguments) };
imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) {
const ret = arg0.then(arg1);
return ret;
};
imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
const ret = arg0.then(arg1, arg2);
return ret;
};
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0);
return ret;
};
imports.wbg.__wbindgen_cb_drop = function(arg0) {
const obj = arg0.original;
if (obj.cnt-- == 1) {
obj.a = 0;
return true;
}
const ret = false;
return ret;
};
imports.wbg.__wbindgen_closure_wrapper214 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 50, __wbg_adapter_24);
return ret;
};
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(arg1);
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbindgen_init_externref_table = function() {
const table = wasm.__wbindgen_export_2;
const offset = table.grow(4);
table.set(0, undefined);
table.set(offset + 0, undefined);
table.set(offset + 1, null);
table.set(offset + 2, true);
table.set(offset + 3, false);
;
};
imports.wbg.__wbindgen_is_function = function(arg0) {
const ret = typeof(arg0) === 'function';
return ret;
};
imports.wbg.__wbindgen_is_undefined = function(arg0) {
const ret = arg0 === undefined;
return ret;
};
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
const obj = arg1;
const ret = typeof(obj) === 'string' ? obj : undefined;
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
const ret = getStringFromWasm0(arg0, arg1);
return ret;
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
return imports;
}
function __wbg_init_memory(imports, memory) {
}
function __wbg_finalize_init(instance, module) {
wasm = instance.exports;
__wbg_init.__wbindgen_wasm_module = module;
cachedDataViewMemory0 = null;
cachedUint8ArrayMemory0 = null;
wasm.__wbindgen_start();
return wasm;
}
function initSync(module) {
if (wasm !== undefined) return wasm;
if (typeof module !== 'undefined') {
if (Object.getPrototypeOf(module) === Object.prototype) {
({module} = module)
} else {
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
}
}
const imports = __wbg_get_imports();
__wbg_init_memory(imports);
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
}
async function __wbg_init(module_or_path) {
if (wasm !== undefined) return wasm;
if (typeof module_or_path !== 'undefined') {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({module_or_path} = module_or_path)
} else {
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
}
}
if (typeof module_or_path === 'undefined') {
module_or_path = new URL('neptune_wasm_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
module_or_path = fetch(module_or_path);
}
__wbg_init_memory(imports);
const { instance, module } = await __wbg_load(await module_or_path, imports);
return __wbg_finalize_init(instance, module);
}
export { initSync };
export default __wbg_init;

View File

@ -1,32 +0,0 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const generate_seed: () => [number, number, number, number];
export const get_viewkey: (a: number, b: number, c: number, d: number) => [number, number, number, number];
export const address_from_seed: (a: number, b: number, c: number, d: number) => [number, number, number, number];
export const validate_seed_phrase: (a: number, b: number) => [number, number, number];
export const decode_viewkey: (a: number, b: number) => [number, number, number, number];
export const build_and_sign_tx: (a: number, b: number) => [number, number, number, number];
export const get_balance: (a: number, b: number) => any;
export const send_tx_jsonrpc: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => any;
export const get_block_height: (a: number, b: number) => any;
export const get_network_info: (a: number, b: number) => any;
export const get_wallet_address: (a: number, b: number) => any;
export const get_viewkey_from_neptune: (a: number, b: number) => any;
export const get_utxos_from_viewkey: (a: number, b: number, c: number, d: number, e: bigint, f: number, g: bigint) => any;
export const generate_viewkey_from_phrase: (a: number, b: number, c: bigint) => [number, number, number, number];
export const generate_address_from_phrase: (a: number, b: number, c: bigint, d: number) => [number, number, number, number];
export const get_receiver_id_from_viewkey: (a: number, b: number) => [number, number, number, number];
export const debug_key_derivation: (a: number, b: number, c: bigint) => [number, number, number, number];
export const sign_transaction_offline: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: bigint, j: number) => [number, number, number, number];
export const __wbindgen_exn_store: (a: number) => void;
export const __externref_table_alloc: () => number;
export const __wbindgen_export_2: WebAssembly.Table;
export const __wbindgen_export_3: WebAssembly.Table;
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __externref_table_dealloc: (a: number) => void;
export const __wbindgen_free: (a: number, b: number, c: number) => void;
export const closure49_externref_shim: (a: number, b: number, c: any) => void;
export const closure268_externref_shim: (a: number, b: number, c: any, d: any) => void;
export const __wbindgen_start: () => void;

View File

@ -1,16 +0,0 @@
{
"name": "@neptune/wasm",
"type": "module",
"version": "0.1.0",
"license": "Apache-2.0",
"files": [
"neptune_wasm_bg.wasm",
"neptune_wasm.js",
"neptune_wasm.d.ts"
],
"main": "neptune_wasm.js",
"types": "neptune_wasm.d.ts",
"sideEffects": [
"./snippets/*"
]
}

765
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
packages:
- 'packages/*'

View File

@ -1,4 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
/target/
/gen/schemas

View File

@ -1,31 +0,0 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.77.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.5.1", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
log = "0.4"
tauri = { version = "2.9.2", features = [] }
tauri-plugin-log = "2"
[target.'cfg(target_os = "android")'.dependencies]
jni = "0.21"
[target.'cfg(target_os = "ios")'.dependencies]
objc = "0.2"

View File

@ -1,3 +0,0 @@
fn main() {
tauri_build::build()
}

View File

@ -1,11 +0,0 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "enables the default permissions",
"windows": [
"main"
],
"permissions": [
"core:default"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<background android:drawable="@color/ic_launcher_background"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#fff</color>
</resources>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -1,25 +0,0 @@
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// Setup logging with rotation and file targets
app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.target(tauri_plugin_log::Target::new(
tauri_plugin_log::TargetKind::Stdout,
))
.target(tauri_plugin_log::Target::new(
tauri_plugin_log::TargetKind::LogDir {
file_name: Some("neptune-privacy".into())
}
))
.max_file_size(10_485_760) // 10MB
.rotation_strategy(tauri_plugin_log::RotationStrategy::KeepAll)
.build(),
)?;
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@ -1,6 +0,0 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
app_lib::run();
}

View File

@ -1,49 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Neptune Privacy",
"version": "0.1.0",
"identifier": "com.neptune.privacy",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm build"
},
"app": {
"windows": [
{
"title": "Neptune Privacy",
"width": 375,
"height": 850,
"minWidth": 375,
"resizable": true,
"fullscreen": false,
"center": true
}
],
"security": {
"csp": null,
"dangerousDisableAssetCspModification": true,
"freezePrototype": false
},
"withGlobalTauri": false,
"macOSPrivateApi": false
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"iOS": {
"minimumSystemVersion": "13.0"
},
"android": {
"minSdkVersion": 24
}
}
}

View File

@ -1,10 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { Toaster } from '@/components/ui/sonner' import { ThemeToggle } from '@/components/ui/theme-toggle'
import neptuneLogo from '@/assets/imgs/neptune_logo.jpg'
</script> </script>
<template> <template>
<router-view /> <div class="min-h-screen bg-background">
<Toaster richColors position="top-center" :duration="2000" closeButton /> <!-- Header with Logo and Theme Toggle -->
<header class="border-b border-border">
<div class="container mx-auto flex items-center justify-between px-4 py-4">
<div class="flex items-center gap-3">
<img
:src="neptuneLogo"
alt="Neptune Wallet Logo"
class="h-12 w-12 rounded-lg object-cover"
/>
<span class="text-xl font-bold text-foreground">Neptune</span>
</div>
<ThemeToggle />
</div>
</header>
<!-- Main Content -->
<main>
<router-view />
</main>
</div>
</template> </template>

View File

@ -1,5 +1,6 @@
import axios, { type AxiosInstance, type AxiosResponse, type AxiosError } from 'axios' import axios, { type AxiosInstance, type AxiosResponse, type AxiosError } from 'axios'
import { STATUS_CODE_SUCCESS } from '@/utils/constants'
const STATUS_CODE_SUCCESS = 200
export const API_URL = import.meta.env.VITE_APP_API || '' export const API_URL = import.meta.env.VITE_APP_API || ''
@ -13,6 +14,17 @@ const instance: AxiosInstance = axios.create({
withCredentials: false, withCredentials: false,
}) })
// Request interceptor
instance.interceptors.request.use(
config => {
// Add request interceptors here (e.g., auth tokens)
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
)
// Response interceptor // Response interceptor
instance.interceptors.response.use( instance.interceptors.response.use(
(response: AxiosResponse) => { (response: AxiosResponse) => {
@ -29,4 +41,10 @@ instance.interceptors.response.use(
} }
) )
// Set locale for API requests
export const setLocaleApi = (locale: string) => {
instance.defaults.headers.common['lang'] = locale
}
export default instance export default instance

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@ -1,18 +0,0 @@
<script setup lang="ts">
import { ArrowLeft } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
const emit = defineEmits<{
click: []
}>()
const handleClick = () => {
emit('click')
}
</script>
<template>
<Button variant="ghost" size="icon-lg" @click="handleClick">
<ArrowLeft />
</Button>
</template>

View File

@ -1 +0,0 @@
export { default as ArrowLeftCommon } from './ArrowLeftCommon.vue'

View File

@ -1,4 +0,0 @@
export * from './layout'
export * from './logo'
export * from './password-form'

View File

@ -1,94 +0,0 @@
<script setup lang="ts">
import { Database, Wallet, History, Network } from 'lucide-vue-next'
const route = useRoute()
const router = useRouter()
// Navigation items for bottom tab bar
const navItems = [
{ name: 'Wallet', icon: Wallet, route: '/', label: 'Wallet' },
{ name: 'UTXO', icon: Database, route: '/utxo', label: 'UTXO' },
{ name: 'History', icon: History, route: '/transaction-history', label: 'History' },
{ name: 'Network', icon: Network, route: '/network', label: 'Network' },
]
const isActiveRoute = (routePath: string) => {
return route.path === routePath
}
</script>
<template>
<div class="flex min-h-screen flex-col bg-background">
<!-- Header -->
<header class="fixed top-0 left-0 right-0 z-10 border-b border-border bg-card">
<div class="flex h-14 items-center justify-between px-4">
<div class="flex items-center gap-3">
<img
src="@/assets/imgs/neptune_logo.jpg"
alt="Neptune"
class="h-8 w-8 rounded-lg object-cover"
/>
<span class="text-lg font-semibold text-foreground">Neptune Privacy</span>
</div>
<ThemeToggle />
</div>
</header>
<!-- Main Content Area (Scrollable) -->
<main class="flex-1 overflow-y-auto pt-14 pb-16">
<router-view />
</main>
<!-- Bottom Navigation Bar -->
<nav
class="safe-area-bottom fixed bottom-0 left-0 right-0 z-10 border-t border-border bg-card shadow-[0_-4px_6px_-1px_rgb(0_0_0/0.1),0_-2px_4px_-2px_rgb(0_0_0/0.1)]"
role="navigation"
aria-label="Main navigation"
>
<div class="grid grid-cols-4">
<button
v-for="item in navItems"
:key="item.name"
type="button"
class="flex flex-col items-center justify-center gap-1 py-2 transition-colors"
:class="[
isActiveRoute(item.route)
? 'text-primary'
: 'text-muted-foreground hover:text-foreground active:text-foreground',
]"
:aria-label="item.label"
:aria-current="isActiveRoute(item.route) ? 'page' : undefined"
@click="router.push(item.route)"
>
<component :is="item.icon" :class="['h-5 w-5', isActiveRoute(item.route) && 'stroke-[2.5]']" />
<span class="text-xs font-medium">{{ item.label }}</span>
</button>
</div>
</nav>
</div>
</template>
<style scoped>
/* Safe area for notched devices (iPhone X, etc.) */
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom, 0);
}
/* Prevent overscroll on iOS */
main {
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}
/* Active tab indicator animation */
button {
position: relative;
-webkit-tap-highlight-color: transparent;
}
/* Ripple effect for better touch feedback */
button:active {
transform: scale(0.95);
}
</style>

View File

@ -1 +0,0 @@
export { default as Layout } from './Layout.vue'

View File

@ -1,25 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useColorMode } from '@vueuse/core'
const mode = useColorMode()
const isDark = computed(() => mode.value === 'dark')
</script>
<template>
<div class="relative">
<div
v-if="isDark"
class="absolute -inset-1 animate-pulse rounded-full bg-linear-to-r from-primary via-accent to-primary opacity-75 blur-xl"
></div>
<div
class="relative flex h-24 w-24 items-center justify-center rounded-full bg-linear-to-br from-primary to-accent shadow-2xl ring-4 ring-background"
>
<img
src="@/assets/imgs/neptune_logo.jpg"
alt="Neptune Privacy"
class="h-20 w-20 rounded-full object-cover"
/>
</div>
</div>
</template>

View File

@ -1 +0,0 @@
export { default as Logo } from './Logo.vue'

View File

@ -1,209 +0,0 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useForm } from '@tanstack/vue-form'
import { z } from 'zod'
import { Eye, EyeOff, Lock, Loader2, ChevronLeft } from 'lucide-vue-next'
import { Card, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
interface Props {
label?: string
placeholder?: string
buttonText?: string
hideBackButton?: boolean
backButtonText?: string
loading?: boolean
error?: boolean
errorMessage?: string
validateFormat?: boolean
autocomplete?: string
}
const props = withDefaults(defineProps<Props>(), {
label: 'Password',
placeholder: 'Enter your password',
buttonText: 'Submit',
backButtonText: 'Back',
hideBackButton: true,
loading: false,
error: false,
errorMessage: 'Incorrect password. Please try again.',
validateFormat: false,
autocomplete: 'current-password',
})
const emit = defineEmits<{
submit: [password: string]
back: []
}>()
const showPassword = ref(false)
// Password strength calculation
const passwordStrength = computed(() => {
const password = form.state.values?.password
if (!password || !props.validateFormat) return { level: 0, text: '', color: '' }
let strength = 0
const checks = {
length: password.length >= 8,
uppercase: /[A-Z]/.test(password),
lowercase: /[a-z]/.test(password),
number: /[0-9]/.test(password),
special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
}
strength = Object.values(checks).filter(Boolean).length
if (strength <= 2) return { level: 1, text: 'Weak', color: 'hsl(var(--destructive))' }
if (strength <= 3) return { level: 2, text: 'Medium', color: 'hsl(48 96% 53%)' } // yellow
if (strength <= 4) return { level: 3, text: 'Good', color: 'hsl(221 83% 53%)' } // blue
return { level: 4, text: 'Strong', color: 'hsl(142 76% 36%)' } // green
})
// Custom password schema with strength validation
const createPasswordSchema = () => {
let schema = z.string().min(1, 'Password is required')
if (props.validateFormat) {
schema = schema
.min(8, 'Password must be at least 8 characters')
.refine((val) => {
const checks = {
uppercase: /[A-Z]/.test(val),
lowercase: /[a-z]/.test(val),
number: /[0-9]/.test(val),
}
const strength = Object.values(checks).filter(Boolean).length
return strength >= 2 // Medium level minimum
}, 'Password is too weak. Use uppercase, lowercase, and numbers.')
}
return schema
}
const form = useForm({
defaultValues: {
password: '',
},
validators: {
onChange: z.object({
password: createPasswordSchema(),
}),
},
onSubmit: async ({ value }) => {
emit('submit', value.password)
},
})
const handleBack = () => {
emit('back')
}
function isInvalid(field: any) {
return field.state.meta.isTouched && !field.state.meta.isValid
}
</script>
<template>
<form @submit.prevent="form.handleSubmit">
<form.Field name="password">
<template #default="{ field }">
<div class="space-y-4">
<div class="space-y-2">
<Label :for="field.name" class="text-base">{{ label }}</Label>
<div class="relative">
<Lock
:size="20"
class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
/>
<Input
:id="field.name"
:name="field.name"
v-model="field.state.value"
:type="showPassword ? 'text' : 'password'"
:placeholder="placeholder"
class="h-12 pl-11 pr-11 text-base"
:class="{ 'border-destructive': error || isInvalid(field) }"
:autocomplete="autocomplete"
@blur="field.handleBlur"
@input="(e: Event) => {
field.handleChange((e.target as HTMLInputElement).value)
}"
/>
<button
type="button"
class="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
@click="showPassword = !showPassword"
>
<Eye v-if="!showPassword" :size="20" />
<EyeOff v-else :size="20" />
</button>
</div>
<!-- Error Message -->
<p v-if="error" class="text-sm text-destructive">
{{ errorMessage }}
</p>
<!-- Validation Error from Zod -->
<p v-else-if="isInvalid(field)" class="text-sm text-destructive">
{{ field.state.meta.errors?.[0]?.message }}
</p>
<!-- Password Strength Indicator -->
<div v-if="validateFormat && field.state.value" class="space-y-2 pt-1">
<div class="flex items-center gap-3">
<div class="h-1 flex-1 overflow-hidden rounded-full bg-muted">
<div
class="h-full transition-all duration-300 ease-in-out"
:style="{
width: `${(passwordStrength.level / 4) * 100}%`,
backgroundColor: passwordStrength.color,
}"
></div>
</div>
<span
class="min-w-[60px] text-right text-xs font-semibold"
:style="{ color: passwordStrength.color }"
>
{{ passwordStrength.text }}
</span>
</div>
<p class="text-xs text-muted-foreground">
Use at least 8 characters with uppercase, lowercase, and numbers.
</p>
</div>
</div>
<!-- Buttons -->
<div class="flex flex-col gap-3">
<Button
type="submit"
size="lg"
class="h-12 w-full text-base font-semibold"
:disabled="!field.state.value || loading"
>
<Loader2 v-if="loading" :size="20" class="mr-2 animate-spin" />
{{ buttonText }}
</Button>
<Button
v-if="!hideBackButton"
type="button"
variant="outline"
size="lg"
class="h-12 w-full text-base"
@click="handleBack"
>
<ChevronLeft :size="20" class="mr-2" />
{{ backButtonText }}
</Button>
</div>
</div>
</template>
</form.Field>
</form>
</template>

View File

@ -1 +0,0 @@
export { default as PasswordForm } from './PasswordForm.vue'

View File

@ -1,17 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import type { AlertVariants } from "."
import { cn } from "@/lib/utils"
import { alertVariants } from "."
const props = defineProps<{
class?: HTMLAttributes["class"]
variant?: AlertVariants["variant"]
}>()
</script>
<template>
<div :class="cn(alertVariants({ variant }), props.class)" role="alert">
<slot />
</div>
</template>

View File

@ -1,14 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div :class="cn('text-sm [&_p]:leading-relaxed', props.class)">
<slot />
</div>
</template>

View File

@ -1,14 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
<slot />
</h5>
</template>

View File

@ -1,24 +0,0 @@
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
export { default as Alert } from "./Alert.vue"
export { default as AlertDescription } from "./AlertDescription.vue"
export { default as AlertTitle } from "./AlertTitle.vue"
export const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
)
export type AlertVariants = VariantProps<typeof alertVariants>

View File

@ -1,17 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import type { BadgeVariants } from "."
import { cn } from "@/lib/utils"
import { badgeVariants } from "."
const props = defineProps<{
variant?: BadgeVariants["variant"]
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div :class="cn(badgeVariants({ variant }), props.class)">
<slot />
</div>
</template>

View File

@ -1,26 +0,0 @@
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
export { default as Badge } from "./Badge.vue"
export const badgeVariants = cva(
"inline-flex gap-1 items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
)
export type BadgeVariants = VariantProps<typeof badgeVariants>

View File

@ -1,21 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div
:class="
cn(
'rounded-xl border bg-card text-card-foreground shadow',
props.class,
)
"
>
<slot />
</div>
</template>

View File

@ -1,14 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<div :class="cn('p-6 pt-0', props.class)">
<slot />
</div>
</template>

Some files were not shown because too many files have changed in this diff Show More