desktop to web wallet version
This commit is contained in:
parent
f08eb965c2
commit
b6522c21ef
51 changed files with 574 additions and 57 deletions
|
|
@ -1,12 +1,34 @@
|
|||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { getCurrentWindow, mockWindow, isTauri } from '../lib/tauri';
|
||||
import { Minus, Square, X } from 'lucide-react';
|
||||
|
||||
type AppWindow = typeof mockWindow;
|
||||
|
||||
/**
|
||||
* Custom title bar for frameless window mode.
|
||||
* Provides drag region and window controls.
|
||||
*/
|
||||
export default function TitleBar() {
|
||||
const appWindow = getCurrentWindow();
|
||||
const [appWindow, setAppWindow] = useState<AppWindow>(mockWindow);
|
||||
|
||||
useEffect(() => {
|
||||
getCurrentWindow().then(setAppWindow);
|
||||
}, []);
|
||||
|
||||
// Hide title bar in browser mode (no native window controls needed)
|
||||
if (!isTauri()) {
|
||||
return (
|
||||
<div className="h-8 flex items-center justify-between bg-gray-900 border-b border-gray-800 select-none">
|
||||
<div className="flex-1 h-full flex items-center px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 rounded bg-gradient-to-br from-synor-400 to-synor-600" />
|
||||
<span className="text-xs font-medium text-gray-400">Synor Wallet</span>
|
||||
<span className="text-xs text-yellow-500 ml-2">(Browser Preview Mode)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
*/
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen, invoke } from '../lib/tauri';
|
||||
|
||||
export interface UpdateInfo {
|
||||
version: string;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { listen } from '../lib/tauri';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useWalletStore } from '../store/wallet';
|
||||
|
||||
|
|
|
|||
503
apps/desktop-wallet/src/lib/tauri.ts
Normal file
503
apps/desktop-wallet/src/lib/tauri.ts
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
/**
|
||||
* Tauri Adapter - Provides mock implementations when running in browser
|
||||
*
|
||||
* This allows the wallet UI to be previewed in a browser without the Rust backend.
|
||||
* When running in Tauri, it uses real invoke() calls.
|
||||
* When running in browser, it returns mock data for UI development/preview.
|
||||
*/
|
||||
|
||||
// Detect if we're running inside Tauri
|
||||
export const isTauri = (): boolean => {
|
||||
return typeof window !== 'undefined' && '__TAURI__' in window;
|
||||
};
|
||||
|
||||
// Mock data generators
|
||||
const mockGenerators = {
|
||||
// Wallet commands
|
||||
create_wallet: async (_args?: { password: string }) => ({
|
||||
mnemonic: 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
||||
address: 'tsynor1qz8v5zp2q0ew9d2k8n7j3x4c5v6b7n8m9k0l1p2q3r4s5t6u7v8w9x0y',
|
||||
}),
|
||||
|
||||
import_wallet: async (_args?: { request: { mnemonic: string; password: string } }) =>
|
||||
'tsynor1qz8v5zp2q0ew9d2k8n7j3x4c5v6b7n8m9k0l1p2q3r4s5t6u7v8w9x0y',
|
||||
|
||||
unlock_wallet: async (_args?: { password: string }) => true,
|
||||
|
||||
lock_wallet: async () => undefined,
|
||||
|
||||
get_balance: async () => ({
|
||||
balance: 12345678900000,
|
||||
balanceHuman: '123,456.789 SYN',
|
||||
pending: 500000000,
|
||||
}),
|
||||
|
||||
get_addresses: async () => [
|
||||
{ address: 'tsynor1qz8v5zp2q0ew9d2k8n7j3x4c5v6b7n8m9k0l1p2q3r4s5t6u7v8w9x0y', index: 0, isChange: false, label: 'Main' },
|
||||
{ address: 'tsynor1qa1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8', index: 1, isChange: false, label: 'Savings' },
|
||||
],
|
||||
|
||||
connect_node: async (_args?: { rpcUrl: string; wsUrl?: string }) => ({
|
||||
connected: true,
|
||||
network: 'testnet',
|
||||
blockHeight: 1234567,
|
||||
peerCount: 8,
|
||||
synced: true,
|
||||
}),
|
||||
|
||||
// Node commands
|
||||
node_start: async () => undefined,
|
||||
node_stop: async () => undefined,
|
||||
node_status: async () => ({
|
||||
isRunning: true,
|
||||
isConnected: true,
|
||||
isSyncing: false,
|
||||
network: 'testnet',
|
||||
blockHeight: 1234567,
|
||||
blueScore: 1234500,
|
||||
peerCount: 8,
|
||||
version: '0.1.0',
|
||||
}),
|
||||
get_peers: async () => [
|
||||
{ id: 'peer1', address: '192.168.1.100:19420', connected: true, latency: 45 },
|
||||
{ id: 'peer2', address: '10.0.0.50:19420', connected: true, latency: 120 },
|
||||
],
|
||||
|
||||
// Mining commands
|
||||
mining_start: async () => undefined,
|
||||
mining_stop: async () => undefined,
|
||||
mining_stats: async () => ({
|
||||
isMining: true,
|
||||
hashrate: 1250000,
|
||||
blocksFound: 3,
|
||||
threads: 4,
|
||||
difficulty: 1234567890,
|
||||
coinbaseAddress: 'tsynor1qz8v5zp2q0ew9d2k8n7j3x4c5v6b7n8m9k0l1p2q3r4s5t6u7v8w9x0y',
|
||||
}),
|
||||
|
||||
// Transaction commands
|
||||
get_transaction_history: async () => [
|
||||
{ txid: 'abc123...', type: 'receive', amount: 1000000000, timestamp: Date.now() - 86400000, confirmations: 100 },
|
||||
{ txid: 'def456...', type: 'send', amount: 500000000, timestamp: Date.now() - 172800000, confirmations: 200 },
|
||||
],
|
||||
create_transaction: async () => ({ txHex: '0x...', fee: 1000 }),
|
||||
sign_transaction: async () => '0x...',
|
||||
broadcast_transaction: async () => 'txid_abc123...',
|
||||
|
||||
// Staking commands
|
||||
get_staking_info: async () => ({
|
||||
isStaking: true,
|
||||
stakedAmount: 5000000000000,
|
||||
pendingRewards: 25000000000,
|
||||
apr: 8.5,
|
||||
validators: [{ address: 'val1', stake: 1000000000000, commission: 5 }],
|
||||
}),
|
||||
|
||||
// Decoy wallets
|
||||
list_decoy_wallets: async () => [
|
||||
{ id: 'decoy1', name: 'Shopping', balance: 10000000000, isPrimary: false, createdAt: Date.now() - 604800000 },
|
||||
{ id: 'decoy2', name: 'Travel', balance: 5000000000, isPrimary: false, createdAt: Date.now() - 1209600000 },
|
||||
],
|
||||
create_decoy_wallet: async () => ({ id: 'new_decoy', name: 'New Decoy', balance: 0, isPrimary: false, createdAt: Date.now() }),
|
||||
delete_decoy_wallet: async () => undefined,
|
||||
|
||||
// Fee analytics
|
||||
get_fee_analytics: async () => ({
|
||||
currentFeeRate: 1,
|
||||
lowFeeRate: 1,
|
||||
mediumFeeRate: 5,
|
||||
highFeeRate: 10,
|
||||
mempoolSize: 1234,
|
||||
averageBlockTime: 1000,
|
||||
feeHistory: Array.from({ length: 24 }, (_, i) => ({ timestamp: Date.now() - i * 3600000, feeRate: Math.random() * 5 + 1 })),
|
||||
}),
|
||||
|
||||
// Watch-only
|
||||
list_watch_addresses: async () => [
|
||||
{ address: 'tsynor1watch1...', label: 'Cold Storage', balance: 100000000000000 },
|
||||
{ address: 'tsynor1watch2...', label: 'Exchange', balance: 50000000000000 },
|
||||
],
|
||||
add_watch_address: async () => undefined,
|
||||
remove_watch_address: async () => undefined,
|
||||
|
||||
// Vaults (Time-locked)
|
||||
list_vaults: async () => [
|
||||
{ id: 'vault1', name: 'Retirement', amount: 50000000000000, unlockTime: Date.now() + 31536000000, status: 'locked' },
|
||||
{ id: 'vault2', name: 'Savings', amount: 10000000000000, unlockTime: Date.now() + 2592000000, status: 'locked' },
|
||||
],
|
||||
create_vault: async () => ({ id: 'new_vault', name: 'New Vault', amount: 0, unlockTime: Date.now() + 86400000, status: 'locked' }),
|
||||
unlock_vault: async () => undefined,
|
||||
|
||||
// Recovery
|
||||
get_recovery_status: async () => ({
|
||||
hasBackup: true,
|
||||
lastBackupDate: Date.now() - 604800000,
|
||||
recoveryMethod: 'mnemonic',
|
||||
socialRecoveryEnabled: false,
|
||||
guardians: [],
|
||||
}),
|
||||
create_backup: async () => 'backup_data_encrypted...',
|
||||
verify_backup: async () => true,
|
||||
|
||||
// Mixer
|
||||
list_mixer_pools: async () => [
|
||||
{ denomination: 100000000, poolSize: 50, minParticipants: 5, fee: 0.001 },
|
||||
{ denomination: 1000000000, poolSize: 30, minParticipants: 5, fee: 0.001 },
|
||||
{ denomination: 10000000000, poolSize: 15, minParticipants: 5, fee: 0.001 },
|
||||
],
|
||||
get_mixer_requests: async () => [
|
||||
{ id: 'mix1', denomination: 1000000000, status: 'pending', createdAt: Date.now() - 3600000 },
|
||||
],
|
||||
create_mix_request: async () => ({ id: 'new_mix', denomination: 1000000000, status: 'pending', createdAt: Date.now() }),
|
||||
cancel_mix_request: async () => undefined,
|
||||
|
||||
// Limit orders
|
||||
list_limit_orders: async () => [
|
||||
{ id: 'order1', pair: 'SYN/BTC', type: 'buy', price: 0.00001, amount: 1000000000, filled: 500000000, status: 'partial' },
|
||||
{ id: 'order2', pair: 'SYN/ETH', type: 'sell', price: 0.0001, amount: 2000000000, filled: 0, status: 'open' },
|
||||
],
|
||||
get_order_book: async () => ({
|
||||
bids: [{ price: 0.00001, amount: 5000000000 }, { price: 0.000009, amount: 10000000000 }],
|
||||
asks: [{ price: 0.000011, amount: 3000000000 }, { price: 0.000012, amount: 8000000000 }],
|
||||
}),
|
||||
create_limit_order: async () => ({ id: 'new_order', pair: 'SYN/BTC', type: 'buy', price: 0.00001, amount: 1000000000, filled: 0, status: 'open' }),
|
||||
cancel_limit_order: async () => undefined,
|
||||
|
||||
// Yield
|
||||
list_yield_opportunities: async () => [
|
||||
{ id: 'yield1', protocol: 'SynorLend', asset: 'SYN', apy: 12.5, tvl: 1000000000000000, risk: 'low' },
|
||||
{ id: 'yield2', protocol: 'SynorFarm', asset: 'SYN-LP', apy: 45.0, tvl: 500000000000000, risk: 'medium' },
|
||||
],
|
||||
list_yield_positions: async () => [
|
||||
{ id: 'pos1', opportunityId: 'yield1', amount: 10000000000000, rewardsEarned: 125000000000, startTime: Date.now() - 2592000000 },
|
||||
],
|
||||
deposit_yield: async () => ({ id: 'new_pos', opportunityId: 'yield1', amount: 5000000000000, rewardsEarned: 0, startTime: Date.now() }),
|
||||
withdraw_yield: async () => undefined,
|
||||
|
||||
// Portfolio
|
||||
get_portfolio_summary: async () => ({
|
||||
totalValueUsd: 125000.50,
|
||||
dayChangeUsd: 2500.25,
|
||||
dayChangePercent: 2.04,
|
||||
totalPnlUsd: 25000.00,
|
||||
totalPnlPercent: 25.0,
|
||||
totalCostBasisUsd: 100000.50,
|
||||
}),
|
||||
list_portfolio_holdings: async () => [
|
||||
{ asset: 'Synor', symbol: 'SYN', balance: 1234567800000000, balanceFormatted: '12,345.678 SYN', valueUsd: 100000, pnlPercent: 25, allocationPercent: 80 },
|
||||
{ asset: 'Bitcoin', symbol: 'BTC', balance: 10000000, balanceFormatted: '0.1 BTC', valueUsd: 25000.50, pnlPercent: 50, allocationPercent: 20 },
|
||||
],
|
||||
get_tax_report: async () => [
|
||||
{ id: 'tx1', timestamp: Date.now() - 86400000, txType: 'buy', asset: 'SYN', amount: 100000000000, totalUsd: 1000, gainLossUsd: undefined, isLongTerm: false },
|
||||
{ id: 'tx2', timestamp: Date.now() - 31536000000, txType: 'sell', asset: 'SYN', amount: 50000000000, totalUsd: 750, gainLossUsd: 250, isLongTerm: true },
|
||||
],
|
||||
export_tax_report: async () => 'Date,Type,Asset,Amount,Total USD,Gain/Loss\n2025-01-01,buy,SYN,100,1000,\n2024-01-01,sell,SYN,50,750,250',
|
||||
|
||||
// Alerts
|
||||
list_alerts: async () => [
|
||||
{ id: 'alert1', type: 'price_above', asset: 'SYN', threshold: 100, enabled: true, triggered: false },
|
||||
{ id: 'alert2', type: 'price_below', asset: 'SYN', threshold: 50, enabled: true, triggered: true },
|
||||
{ id: 'alert3', type: 'balance_below', asset: 'SYN', threshold: 1000, enabled: false, triggered: false },
|
||||
],
|
||||
create_alert: async () => ({ id: 'new_alert', type: 'price_above', asset: 'SYN', threshold: 100, enabled: true, triggered: false }),
|
||||
update_alert: async () => undefined,
|
||||
delete_alert: async () => undefined,
|
||||
|
||||
// CLI
|
||||
execute_cli_command: async (args?: { command: string }) => ({
|
||||
success: true,
|
||||
output: `Executed: ${args?.command || 'help'}\n\nMock CLI output for browser preview.\nAvailable commands: help, balance, send, receive, history`,
|
||||
executionTime: 150,
|
||||
}),
|
||||
|
||||
// RPC Profiles
|
||||
list_rpc_profiles: async () => [
|
||||
{ id: 'mainnet', name: 'Mainnet', rpcUrl: 'https://rpc.synor.network', wsUrl: 'wss://ws.synor.network', isActive: false },
|
||||
{ id: 'testnet', name: 'Testnet', rpcUrl: 'https://testnet.synor.network', wsUrl: 'wss://testnet-ws.synor.network', isActive: true },
|
||||
{ id: 'local', name: 'Local Node', rpcUrl: 'http://localhost:19423', wsUrl: 'ws://localhost:19424', isActive: false },
|
||||
],
|
||||
create_rpc_profile: async () => ({ id: 'new_profile', name: 'New Profile', rpcUrl: '', wsUrl: '', isActive: false }),
|
||||
update_rpc_profile: async () => undefined,
|
||||
delete_rpc_profile: async () => undefined,
|
||||
set_active_rpc_profile: async () => undefined,
|
||||
|
||||
// Address book
|
||||
list_contacts: async () => [
|
||||
{ id: 'contact1', name: 'Alice', address: 'tsynor1alice...', notes: 'Friend' },
|
||||
{ id: 'contact2', name: 'Bob', address: 'tsynor1bob...', notes: 'Business partner' },
|
||||
],
|
||||
add_contact: async () => ({ id: 'new_contact', name: 'New Contact', address: '', notes: '' }),
|
||||
update_contact: async () => undefined,
|
||||
delete_contact: async () => undefined,
|
||||
|
||||
// Multi-sig
|
||||
list_multisig_wallets: async () => [
|
||||
{ id: 'ms1', name: 'Team Wallet', threshold: 2, signers: ['addr1', 'addr2', 'addr3'], balance: 100000000000000 },
|
||||
],
|
||||
create_multisig_wallet: async () => ({ id: 'new_ms', name: 'New Multisig', threshold: 2, signers: [], balance: 0 }),
|
||||
|
||||
// Backup
|
||||
get_backup_status: async () => ({
|
||||
hasLocalBackup: true,
|
||||
hasCloudBackup: false,
|
||||
lastBackupDate: Date.now() - 604800000,
|
||||
}),
|
||||
create_local_backup: async () => 'backup_created',
|
||||
restore_from_backup: async () => true,
|
||||
|
||||
// QR
|
||||
generate_qr_code: async () => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
|
||||
|
||||
// Hardware wallet
|
||||
list_hardware_wallets: async () => [],
|
||||
connect_hardware_wallet: async () => ({ id: 'hw1', type: 'ledger', connected: true }),
|
||||
|
||||
// Portfolio commands (snake_case for backend compatibility)
|
||||
portfolio_get_summary: async () => ({
|
||||
total_value_usd: 125000.50,
|
||||
day_change_usd: 2500.25,
|
||||
day_change_percent: 2.04,
|
||||
total_pnl_usd: 25000.00,
|
||||
total_pnl_percent: 25.0,
|
||||
total_cost_basis_usd: 100000.50,
|
||||
}),
|
||||
portfolio_get_holdings: async () => [
|
||||
{ asset: 'Synor', symbol: 'SYN', balance: 1234567800000000, balance_formatted: '12,345.678 SYN', price_usd: 8.1, value_usd: 100000, cost_basis_usd: 80000, pnl_usd: 20000, pnl_percent: 25, allocation_percent: 80 },
|
||||
{ asset: 'Bitcoin', symbol: 'BTC', balance: 10000000, balance_formatted: '0.1 BTC', price_usd: 250000, value_usd: 25000.50, cost_basis_usd: 16700, pnl_usd: 8300.50, pnl_percent: 50, allocation_percent: 20 },
|
||||
],
|
||||
portfolio_get_tax_report: async () => [
|
||||
{ id: 'tx1', timestamp: Date.now() - 86400000, tx_type: 'buy', asset: 'SYN', amount: 100000000000, price_usd: 10, total_usd: 1000, cost_basis_usd: 1000, gain_loss_usd: null, is_long_term: false },
|
||||
{ id: 'tx2', timestamp: Date.now() - 31536000000, tx_type: 'sell', asset: 'SYN', amount: 50000000000, price_usd: 15, total_usd: 750, cost_basis_usd: 500, gain_loss_usd: 250, is_long_term: true },
|
||||
],
|
||||
portfolio_export_tax_report: async () => 'Date,Type,Asset,Amount,Total USD,Gain/Loss\n2025-01-01,buy,SYN,100,1000,\n2024-01-01,sell,SYN,50,750,250',
|
||||
portfolio_get_history: async () => Array.from({ length: 30 }, (_, i) => ({
|
||||
timestamp: Date.now() - i * 86400000,
|
||||
value_usd: 100000 + Math.random() * 50000,
|
||||
})),
|
||||
|
||||
// Wallet manager commands
|
||||
wallets_list: async () => [
|
||||
{ id: 'wallet1', name: 'Main Wallet', address: 'tsynor1qz8v5zp2q0ew9d2k8n7j3x4c5v6b7n8m9k0l1p2q3r4s5t6u7v8w9x0y', isActive: true, createdAt: Date.now() - 86400000 * 30 },
|
||||
],
|
||||
wallets_create: async () => ({ id: 'new_wallet', name: 'New Wallet', address: 'tsynor1new...', isActive: false, createdAt: Date.now() }),
|
||||
wallets_import: async () => ({ id: 'imported', name: 'Imported Wallet', address: 'tsynor1imp...', isActive: false, createdAt: Date.now() }),
|
||||
wallets_switch: async () => undefined,
|
||||
wallets_delete: async () => undefined,
|
||||
wallets_rename: async () => undefined,
|
||||
|
||||
// Yield commands (snake_case)
|
||||
yield_list_opportunities: async () => [
|
||||
{ id: 'yield1', protocol: 'SynorLend', asset: 'SYN', apy: 12.5, tvl: 1000000000000000, risk: 'low', min_deposit: 100000000 },
|
||||
{ id: 'yield2', protocol: 'SynorFarm', asset: 'SYN-LP', apy: 45.0, tvl: 500000000000000, risk: 'medium', min_deposit: 500000000 },
|
||||
],
|
||||
yield_list_positions: async () => [
|
||||
{ id: 'pos1', opportunity_id: 'yield1', amount: 10000000000000, rewards_earned: 125000000000, start_time: Date.now() - 2592000000 },
|
||||
],
|
||||
yield_deposit: async () => ({ id: 'new_pos', opportunity_id: 'yield1', amount: 5000000000000, rewards_earned: 0, start_time: Date.now() }),
|
||||
yield_withdraw: async () => undefined,
|
||||
|
||||
// Alerts commands
|
||||
alerts_list: async () => [
|
||||
{ id: 'alert1', alert_type: 'price_above', asset: 'SYN', threshold: 100, enabled: true, triggered: false, created_at: Date.now() - 86400000 },
|
||||
{ id: 'alert2', alert_type: 'price_below', asset: 'SYN', threshold: 50, enabled: true, triggered: true, created_at: Date.now() - 172800000 },
|
||||
],
|
||||
alerts_create: async () => ({ id: 'new_alert', alert_type: 'price_above', asset: 'SYN', threshold: 100, enabled: true, triggered: false, created_at: Date.now() }),
|
||||
alerts_update: async () => undefined,
|
||||
alerts_delete: async () => undefined,
|
||||
|
||||
// CLI commands
|
||||
cli_execute: async (args?: { command: string }) => ({
|
||||
success: true,
|
||||
output: `$ ${args?.command || 'help'}\n\n[Mock CLI Output]\nCommand executed successfully in browser preview mode.\n\nAvailable commands:\n help - Show this help\n balance - Show wallet balance\n send - Send SYN to an address\n receive - Show receive address\n history - Transaction history`,
|
||||
execution_time: 150,
|
||||
}),
|
||||
|
||||
// Mixer commands (snake_case)
|
||||
mixer_get_denominations: async () => [100000000, 1000000000, 10000000000, 100000000000],
|
||||
mixer_list_pools: async () => [
|
||||
{ denomination: 100000000, pool_size: 50, min_participants: 5, fee: 0.001, status: 'active' },
|
||||
{ denomination: 1000000000, pool_size: 30, min_participants: 5, fee: 0.001, status: 'active' },
|
||||
{ denomination: 10000000000, pool_size: 15, min_participants: 5, fee: 0.001, status: 'active' },
|
||||
],
|
||||
mixer_get_pool_status: async () => ({ denomination: 1000000000, pool_size: 30, participants: 12, status: 'active' }),
|
||||
mixer_get_requests: async () => [
|
||||
{ id: 'mix1', denomination: 1000000000, status: 'pending', created_at: Date.now() - 3600000 },
|
||||
],
|
||||
mixer_create_request: async () => ({ id: 'new_mix', denomination: 1000000000, status: 'pending', created_at: Date.now() }),
|
||||
mixer_cancel_request: async () => undefined,
|
||||
|
||||
// Limit orders (snake_case)
|
||||
limit_order_list: async () => [
|
||||
{ id: 'order1', pair: 'SYN/BTC', order_type: 'buy', price: 0.00001, amount: 1000000000, filled: 500000000, status: 'partial', created_at: Date.now() - 86400000 },
|
||||
{ id: 'order2', pair: 'SYN/ETH', order_type: 'sell', price: 0.0001, amount: 2000000000, filled: 0, status: 'open', created_at: Date.now() - 3600000 },
|
||||
],
|
||||
limit_order_get_orderbook: async () => ({
|
||||
bids: [{ price: 0.00001, amount: 5000000000 }, { price: 0.000009, amount: 10000000000 }],
|
||||
asks: [{ price: 0.000011, amount: 3000000000 }, { price: 0.000012, amount: 8000000000 }],
|
||||
}),
|
||||
limit_order_create: async () => ({ id: 'new_order', pair: 'SYN/BTC', order_type: 'buy', price: 0.00001, amount: 1000000000, filled: 0, status: 'open', created_at: Date.now() }),
|
||||
limit_order_cancel: async () => undefined,
|
||||
|
||||
// RPC Profiles (snake_case)
|
||||
rpc_profiles_list: async () => [
|
||||
{ id: 'mainnet', name: 'Mainnet', rpc_url: 'https://rpc.synor.network', ws_url: 'wss://ws.synor.network', is_active: false },
|
||||
{ id: 'testnet', name: 'Testnet', rpc_url: 'https://testnet.synor.network', ws_url: 'wss://testnet-ws.synor.network', is_active: true },
|
||||
{ id: 'local', name: 'Local Node', rpc_url: 'http://localhost:19423', ws_url: 'ws://localhost:19424', is_active: false },
|
||||
],
|
||||
rpc_profiles_create: async () => ({ id: 'new_profile', name: 'New Profile', rpc_url: '', ws_url: '', is_active: false }),
|
||||
rpc_profiles_update: async () => undefined,
|
||||
rpc_profiles_delete: async () => undefined,
|
||||
rpc_profiles_set_active: async () => undefined,
|
||||
|
||||
// Vaults (snake_case)
|
||||
vault_list: async () => [
|
||||
{ id: 'vault1', name: 'Retirement', amount: 50000000000000, unlock_time: Date.now() + 31536000000, status: 'locked', created_at: Date.now() - 86400000 * 365 },
|
||||
{ id: 'vault2', name: 'Savings', amount: 10000000000000, unlock_time: Date.now() + 2592000000, status: 'locked', created_at: Date.now() - 86400000 * 30 },
|
||||
],
|
||||
vault_get_summary: async () => ({
|
||||
total_locked: 60000000000000,
|
||||
total_vaults: 2,
|
||||
next_unlock: Date.now() + 2592000000,
|
||||
}),
|
||||
vault_create: async () => ({ id: 'new_vault', name: 'New Vault', amount: 0, unlock_time: Date.now() + 86400000, status: 'locked', created_at: Date.now() }),
|
||||
vault_withdraw: async () => 'txid_vault_withdraw...',
|
||||
vault_time_remaining: async () => 2592000000,
|
||||
|
||||
// Recovery
|
||||
recovery_get_status: async () => ({
|
||||
has_backup: true,
|
||||
last_backup_date: Date.now() - 604800000,
|
||||
recovery_method: 'mnemonic',
|
||||
social_recovery_enabled: false,
|
||||
guardians: [],
|
||||
}),
|
||||
recovery_create_backup: async () => 'backup_data_encrypted...',
|
||||
recovery_verify_backup: async () => true,
|
||||
|
||||
// Decoy wallets (snake_case)
|
||||
decoy_is_enabled: async () => true,
|
||||
decoy_list: async () => [
|
||||
{ id: 'decoy1', name: 'Shopping', balance: 10000000000, is_primary: false, created_at: Date.now() - 604800000 },
|
||||
{ id: 'decoy2', name: 'Travel', balance: 5000000000, is_primary: false, created_at: Date.now() - 1209600000 },
|
||||
],
|
||||
decoy_create: async () => ({ id: 'new_decoy', name: 'New Decoy', balance: 0, is_primary: false, created_at: Date.now() }),
|
||||
decoy_delete: async () => undefined,
|
||||
decoy_transfer: async () => undefined,
|
||||
|
||||
// Fee analytics
|
||||
fee_get_analytics: async () => ({
|
||||
current_fee_rate: 1,
|
||||
low_fee_rate: 1,
|
||||
medium_fee_rate: 5,
|
||||
high_fee_rate: 10,
|
||||
mempool_size: 1234,
|
||||
average_block_time: 1000,
|
||||
fee_history: Array.from({ length: 24 }, (_, i) => ({
|
||||
timestamp: Date.now() - i * 3600000,
|
||||
fee_rate: Math.random() * 5 + 1,
|
||||
})),
|
||||
}),
|
||||
fee_calculate: async () => 1500,
|
||||
|
||||
// Watch-only
|
||||
watch_only_list: async () => [
|
||||
{ address: 'tsynor1watch1abc123...', label: 'Cold Storage', balance: 100000000000000 },
|
||||
{ address: 'tsynor1watch2def456...', label: 'Exchange', balance: 50000000000000 },
|
||||
],
|
||||
watch_only_add: async () => undefined,
|
||||
watch_only_remove: async () => undefined,
|
||||
|
||||
// Default fallback for unknown commands
|
||||
default: async (cmd: string) => {
|
||||
console.warn(`[Mock] Unknown command: ${cmd}`);
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
type MockGenerators = typeof mockGenerators;
|
||||
type CommandName = keyof MockGenerators;
|
||||
|
||||
/**
|
||||
* Invoke a Tauri command with automatic mock fallback for browser
|
||||
*/
|
||||
export async function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
|
||||
if (isTauri()) {
|
||||
// Running in Tauri - use real invoke
|
||||
const { invoke: tauriInvoke } = await import('@tauri-apps/api/core');
|
||||
return tauriInvoke<T>(cmd, args);
|
||||
}
|
||||
|
||||
// Running in browser - use mock
|
||||
console.log(`[Mock] ${cmd}`, args || '');
|
||||
|
||||
const generator = mockGenerators[cmd as CommandName] || mockGenerators.default;
|
||||
const result = await generator(args as never);
|
||||
|
||||
// Simulate network latency
|
||||
await new Promise(resolve => setTimeout(resolve, 100 + Math.random() * 200));
|
||||
|
||||
return result as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to Tauri events with mock fallback
|
||||
*/
|
||||
export async function listen<T>(
|
||||
event: string,
|
||||
handler: (payload: { payload: T }) => void
|
||||
): Promise<() => void> {
|
||||
if (isTauri()) {
|
||||
const { listen: tauriListen } = await import('@tauri-apps/api/event');
|
||||
return tauriListen<T>(event, handler);
|
||||
}
|
||||
|
||||
// In browser, set up mock event emitter
|
||||
console.log(`[Mock] Listening to event: ${event}`);
|
||||
|
||||
// Return a no-op unsubscribe function
|
||||
return () => {
|
||||
console.log(`[Mock] Unsubscribed from event: ${event}`);
|
||||
};
|
||||
}
|
||||
|
||||
// Export a flag for components to check
|
||||
export const BROWSER_MOCK_MODE = !isTauri();
|
||||
|
||||
// Type for unlisten function
|
||||
export type UnlistenFn = () => void;
|
||||
|
||||
/**
|
||||
* Mock window API for browser
|
||||
*/
|
||||
export const mockWindow = {
|
||||
async minimize() {
|
||||
console.log('[Mock] Window minimize');
|
||||
},
|
||||
async maximize() {
|
||||
console.log('[Mock] Window maximize');
|
||||
},
|
||||
async close() {
|
||||
console.log('[Mock] Window close');
|
||||
},
|
||||
async toggleMaximize() {
|
||||
console.log('[Mock] Window toggleMaximize');
|
||||
},
|
||||
async setTitle(title: string) {
|
||||
console.log('[Mock] Window setTitle:', title);
|
||||
document.title = title;
|
||||
},
|
||||
async isMaximized() {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current window with mock fallback
|
||||
*/
|
||||
export async function getCurrentWindow() {
|
||||
if (isTauri()) {
|
||||
const { getCurrentWindow: tauriGetCurrentWindow } = await import('@tauri-apps/api/window');
|
||||
return tauriGetCurrentWindow();
|
||||
}
|
||||
return mockWindow;
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import {
|
|||
Wallet,
|
||||
} from 'lucide-react';
|
||||
import { useDecoyStore, DecoyWallet } from '../../store/decoy';
|
||||
import { LoadingSpinner } from '../../components/LoadingStates';
|
||||
|
||||
function SetupDecoyModal({ onClose }: { onClose: () => void }) {
|
||||
const { setup, isLoading } = useDecoyStore();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { useEffect, useState, useMemo } from 'react';
|
|||
import {
|
||||
Activity,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Clock,
|
||||
Zap,
|
||||
RefreshCw,
|
||||
|
|
@ -15,11 +14,9 @@ import {
|
|||
} from 'lucide-react';
|
||||
import {
|
||||
useFeeAnalyticsStore,
|
||||
useSelectedRecommendation,
|
||||
getCongestionColor,
|
||||
getCongestionBgColor,
|
||||
formatDuration,
|
||||
formatFeeRate,
|
||||
FeeRecommendation,
|
||||
} from '../../store/feeAnalytics';
|
||||
import { LoadingSpinner } from '../../components/LoadingStates';
|
||||
|
|
@ -215,7 +212,7 @@ function FeeHistoryChart() {
|
|||
* Fee calculator component
|
||||
*/
|
||||
function FeeCalculator() {
|
||||
const { analytics, selectedTier, calculateFee } = useFeeAnalyticsStore();
|
||||
const { analytics, selectedTier } = useFeeAnalyticsStore();
|
||||
const [txSize, setTxSize] = useState(250); // Default tx size
|
||||
const [calculatedFee, setCalculatedFee] = useState<number | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
import {
|
||||
ArrowUpRight,
|
||||
ArrowDownLeft,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
Upload,
|
||||
RefreshCw,
|
||||
} from 'lucide-react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../../lib/tauri';
|
||||
|
||||
interface PaymentRequest {
|
||||
address: string;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState } from 'react';
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import { Copy, Check, Plus, QrCode } from 'lucide-react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
import { useWalletStore } from '../store/wallet';
|
||||
|
||||
export default function Receive() {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
CheckCircle,
|
||||
Clock,
|
||||
Users,
|
||||
Settings,
|
||||
Mail,
|
||||
Wallet,
|
||||
Info,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState } from 'react';
|
||||
import { Server, Plus, Trash2, CheckCircle, RefreshCw, Info, Wifi, WifiOff } from 'lucide-react';
|
||||
import { Server, Plus, Trash2, CheckCircle, RefreshCw, Info, Wifi } from 'lucide-react';
|
||||
|
||||
interface RpcProfile {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
import { Send as SendIcon, AlertTriangle, Check } from 'lucide-react';
|
||||
import { useWalletStore } from '../store/wallet';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
import {
|
||||
Server,
|
||||
Shield,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface PriceAlert {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* A recipient in a batch transaction
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface CliResult {
|
||||
command: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Sanitized error logging
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* A decoy wallet for plausible deniability
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Mempool statistics from the network
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface LimitOrder {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { invoke, listen, type UnlistenFn } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Sanitized error logging
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
// Types matching backend
|
||||
export interface MixPoolStatus {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Sanitized error logging
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { invoke, listen, type UnlistenFn } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Sanitized error logging
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface PluginInfo {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface PortfolioSummary {
|
||||
totalValueUsd: number;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* A guardian for social recovery
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface RpcProfile {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Sanitized error logging
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface TxOutput {
|
||||
address: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Time-locked vault
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Sanitized error logging - prevents sensitive data from being logged
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Summary info for a wallet
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
/**
|
||||
* Watch-only address entry
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
export interface YieldOpportunity {
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { create } from 'zustand';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { invoke } from '../lib/tauri';
|
||||
|
||||
function logError(context: string, error: unknown): void {
|
||||
if (import.meta.env.PROD) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue