Commit Graph

1344 Commits

Author SHA1 Message Date
hailin a609600cd8 feat(fiat-withdrawal): add complete fiat withdrawal system
实现完整的法币提现功能,支持银行卡、支付宝、微信三种收款方式。
此功能与现有的区块链划转功能完全独立,互不影响。

## 后端 (wallet-service)

### 数据库
- 新增 `fiat_withdrawal_orders` 表存储法币提现订单
- 与现有 `withdrawal_orders` 表(区块链划转)完全分离
- 添加完整索引支持高效查询

### 领域层
- 新增 `FiatWithdrawalStatus` 枚举(与 WithdrawalStatus 独立)
  - 流程: PENDING -> FROZEN -> REVIEWING -> APPROVED -> PAYING -> COMPLETED
  - 或 REJECTED / FAILED / CANCELLED
- 新增 `PaymentMethod` 枚举: BANK_CARD / ALIPAY / WECHAT
- 新增 `FiatWithdrawalOrder` 聚合根
- 新增 `IFiatWithdrawalOrderRepository` 仓储接口
- 新增 `FIAT_WITHDRAWAL` 账本流水类型

### 应用层
- 新增 `FiatWithdrawalApplicationService` 处理业务逻辑
  - 发送短信验证码
  - 申请法币提现(冻结余额)
  - 提交审核
  - 审核通过/驳回
  - 开始打款
  - 完成打款

### API层
- 新增 `FiatWithdrawalController` 提供用户端API
  - POST /wallet/fiat-withdrawal/send-sms - 发送验证码
  - POST /wallet/fiat-withdrawal - 申请提现
  - GET /wallet/fiat-withdrawal - 获取提现记录
- 新增内部API供管理端调用
  - GET /api/v1/wallets/fiat-withdrawals - 查询订单
  - POST /api/v1/wallets/fiat-withdrawals/:orderNo/review - 审核
  - POST /api/v1/wallets/fiat-withdrawals/:orderNo/start-payment - 开始打款
  - POST /api/v1/wallets/fiat-withdrawals/:orderNo/complete-payment - 完成打款

## 前端 (admin-web)

- 新增法币提现审核管理页面 `/withdrawals`
- 支持按状态分 Tab 查看订单
- 支持审核通过/驳回
- 支持打款操作
- 支持查看订单详情

## 前端 (mobile-app)

- 新增 `WithdrawFiatPage` 法币提现页面
  - 支持选择银行卡/支付宝/微信
  - 输入收款账户信息
- 新增 `WithdrawFiatConfirmPage` 确认页面
  - 短信验证码验证
  - 密码验证
- 在 `WalletService` 中添加法币提现相关方法和模型

## 重要说明

此功能与现有的区块链划转功能 (withdraw_usdt_page.dart) 完全独立:
- 独立的数据库表
- 独立的聚合根
- 独立的状态枚举
- 独立的API端点
- 独立的前端页面

原有的区块链划转功能保持不变,不受任何影响。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 06:39:11 -08:00
hailin d614d18e97 Revert "feat(withdrawal): implement fiat withdrawal with bank/alipay/wechat"
This reverts commit 288d894746.
2026-01-03 05:44:43 -08:00
hailin 288d894746 feat(withdrawal): implement fiat withdrawal with bank/alipay/wechat
Add complete fiat withdrawal feature that allows users to withdraw
green credits (绿积分) to their bank card, Alipay, or WeChat account
with 1:1 CNY conversion. Key changes:

Backend (wallet-service):
- Update Prisma schema with fiat withdrawal fields (paymentMethod,
  bankName, bankCardNo, cardHolderName, alipay*, wechat*, review fields)
- Rewrite withdrawal status enum for fiat flow: PENDING → FROZEN →
  REVIEWING → APPROVED → PAYING → COMPLETED (or REJECTED/FAILED)
- Add PaymentMethod enum: BANK_CARD, ALIPAY, WECHAT
- Update WithdrawalOrderAggregate with new fiat withdrawal methods
- Add review/payment workflow methods in WalletApplicationService
- Add internal API endpoints for admin withdrawal management
- Remove blockchain withdrawal event handler (no longer needed)

Frontend (admin-web):
- Add withdrawal review management page at /withdrawals
- Add tabs for reviewing/approved/paying order states
- Add withdrawal service and React Query hooks
- Add types for withdrawal orders and payment methods
- Add sidebar menu item for withdrawal review

Frontend (mobile-app):
- Add withdrawFiat() method to WalletService
- Add PaymentMethod enum with BANK_CARD/ALIPAY/WECHAT
- Create new WithdrawFiatPage for fiat withdrawal input
- Create WithdrawFiatConfirmPage with SMS + password verification
- Add routes for /withdraw/fiat and /withdraw/fiat/confirm
- Keep existing withdraw/usdt (划转) pages unchanged

Note: The existing withdraw_usdt_page.dart is for point-to-point
transfer (划转), which is a different feature from fiat withdrawal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 05:28:05 -08:00
hailin 036696878f feat(settlement): implement settle-to-balance with detailed source tracking
Add complete settlement-to-balance feature that transfers settleable
earnings directly to wallet USDT balance (no currency swap). Key changes:

Backend (wallet-service):
- Add SettleToBalanceCommand for settlement operations
- Add settleToBalance method to WalletAccountAggregate
- Add settleToBalance application service with ledger recording
- Add internal API endpoint POST /api/v1/wallets/settle-to-balance

Backend (reward-service):
- Add settleToBalance client method for wallet-service communication
- Add settleRewardsToBalance application service method
- Add user-facing API endpoint POST /rewards/settle-to-balance
- Build detailed settlement memo with source user tracking per reward

Frontend (mobile-app):
- Add SettleToBalanceResult model class
- Add settleToBalance() method to RewardService
- Update pending_actions_page to handle SETTLE_REWARDS action
- Add completion detection via settleableUsdt balance check

Settlement memo now includes detailed breakdown by right type with
source user accountSequence for each reward entry, e.g.:
  结算 1000.00 绿积分到钱包余额
  涉及 5 笔奖励
    - SHARE_RIGHT: 500.00 绿积分
        来自 D2512120001: 288.00 绿积分
        来自 D2512120002: 212.00 绿积分

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 04:29:38 -08:00
hailin cbbef170e8 feat(pending-actions): display accountSequence alongside userId
- Add accountSequence field to PendingActionResponseDto
- Add helper methods to fetch accountSequence from UserAccount
- Update queryActions and getAction to include accountSequence
- Update admin-web table and detail view to show both fields
- accountSequence displayed prominently, userId shown as secondary info

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 21:33:03 -08:00
hailin 13dd42d2be fix(mobile-app): fix pending action completion detection
- Change FORCE_KYC check from isCompleted to level1.verified
  (FORCE_KYC only requires real-name verification, not all KYC levels)
- Add post-navigation re-check for FORCE_KYC and BIND_PHONE actions
  (handles cases where user completes action but page doesn't return true)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 21:09:35 -08:00
hailin c5c4e1667e fix(mobile-app): fix layout constraint error in pending actions page
Wrap ElevatedButton in SizedBox(width: 72) to prevent
BoxConstraints infinite width error in Row layout.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 21:02:15 -08:00
hailin f5f0ff2822 fix(mobile-app): correctly parse nested API response for pending actions
The API returns a nested structure {success, data: {code, data: [...]}}
but the service was only checking for {actions: [...]} format.

Now correctly extracts the actions list from data.data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 20:57:17 -08:00
hailin f7913cd04e chore: temporarily disable KYC and contract check logs
Comment out debugPrint statements in pending actions and contract
check services to reduce log noise during development.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 20:51:13 -08:00
hailin 789d921fd7 fix(pending-actions): support accountSequence for user lookup
Allow admin to create pending actions using accountSequence (e.g.,
D25122700022) instead of requiring numeric userId.

- Add findUserByIdOrSequence helper method
- Update createAction to use helper
- Update batchCreateActions to use helper
- Update queryActions to support accountSequence filter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 20:39:54 -08:00
hailin 47a7e4a4da feat(pending-actions): enhance multi-select creation and add pre-check
Admin Web:
- Redesign create modal to support multi-select action types
- Add drag-and-drop ordering for execution sequence
- Auto-calculate priority based on order (first = highest)
- Add @dnd-kit dependencies for sortable functionality

Flutter Mobile App:
- Add pre-check logic before executing pending actions
- Auto-complete FORCE_KYC if KYC already verified
- Auto-complete BIND_PHONE if phone already bound
- Skip unnecessary user interactions for completed tasks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 20:23:15 -08:00
hailin 06d3489b49 fix(admin-web): fix nested data access in pendingActionService
API returns nested structure: { success, data: { code, message, data: {...} } }
After apiClient interceptor unwraps response.data, we still need to access
.data.data to get the actual business data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 19:18:54 -08:00
hailin ed463d67ab fix(admin-web): fix API response data access in pendingActionService
The apiClient interceptor already unwraps response.data, so the service
was accessing .data on the already-unwrapped response. Fixed by properly
casting the response type to access the nested data field.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 19:08:57 -08:00
hailin 8c8a049f77 fix(admin-web): handle undefined data in dashboard hooks
Add null-safe access and fallback to empty arrays to prevent
"Cannot read properties of undefined" errors when API returns
unexpected data structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 19:05:54 -08:00
hailin 2e7de8a1ef feat(contracts): add DurianUSDT ERC-20 token contract
Add fixed-supply ERC-20 token contract for Durian USDT (dUSDT):
- Total supply: 1 trillion tokens with 6 decimals
- No minting capability - all tokens minted at deployment
- Includes compile and deploy scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 19:02:19 -08:00
hailin 582e80b750 fix(pending-actions): add @Public() decorator to AdminPendingActionController
Skip JWT auth for admin pending-actions endpoints since admin-web
authenticates through a different mechanism (admin-service tokens).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 19:00:24 -08:00
hailin ff038f31f9 fix(pending-actions): fix API response handling and add Kong route
- Fix pending_action_service.dart to access response.data instead of response
- Add Kong route for /api/v1/admin/pending-actions to identity-service

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 18:33:58 -08:00
hailin 28e0396a65 feat(pending-actions): add user pending actions system
Add a fully optional pending actions system that allows admins to configure
specific tasks that users must complete after login.

Backend (identity-service):
- Add UserPendingAction model to Prisma schema
- Add migration for user_pending_actions table
- Add PendingActionService with full CRUD operations
- Add user-facing API (GET list, POST complete)
- Add admin API (CRUD, batch create)

Admin Web:
- Add pending actions management page
- Support single/batch create, edit, cancel, delete
- View action details including completion time
- Filter by userId, actionCode, status

Flutter Mobile App:
- Add PendingActionService and PendingActionCheckService
- Add PendingActionsPage for forced task execution
- Integrate into splash_page login flow
- Users must complete all pending tasks in priority order

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 18:22:51 -08:00
hailin 04a8c56ad6 fix(identity): use correct Aliyun API for ID card verification
Change API from Id2MetaStandardVerify to Id2MetaVerify for two-factor
identity verification (name + ID card number). The previous API was
returning error 440 (no permission).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 08:05:08 -08:00
hailin e2cf3c3d7e fix(admin-service): 修复通知查询时publishedAt为null的问题
问题:当 publishedAt 为 NULL(表示立即发布)时,Prisma 的
`publishedAt: { lte: now }` 条件不匹配,导致通知无法显示

修复:将查询条件改为 OR 逻辑:
- publishedAt 为 null(立即发布)
- publishedAt <= now(定时发布且已到时间)

影响的方法:
- findNotificationsForUser
- countUnreadForUser
- markAllAsRead

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 05:56:43 -08:00
hailin fea0b42223 fix(admin-service): 修复维护拦截器路径检测和错误处理
问题:添加系统维护检测后站内通知功能失效

修复:
1. 使用 request.url 获取完整路径(包含 /api/v1 前缀)
2. 同时支持带前缀和不带前缀的路径检测
3. 添加 try-catch 错误处理,数据库错误时放行请求而非阻断
4. 添加日志记录便于调试

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 05:01:32 -08:00
hailin c392142562 feat(blockchain): 切换到dUSDT(绿积分)合约 - KAVA主网
合约信息:
- 地址: 0xA9F3A35dBa8699c8C681D8db03F0c1A8CEB9D7c3
- 名称: Durian USDT (dUSDT)
- 精度: 6位
- 网络: KAVA EVM Mainnet (Chain ID: 2222)
- 链接: https://kavascan.com/address/0xA9F3A35dBa8699c8C681D8db03F0c1A8CEB9D7c3

修改:
- blockchain.config.ts: 更新默认合约地址
- chain-config.service.ts: 更新默认合约地址
- docker-compose.yml: NETWORK_MODE改为mainnet,配置KAVA主网
- .env.example: 更新合约地址和注释
- KAVA_NETWORK.md: 标注dUSDT为当前使用合约

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 04:31:11 -08:00
hailin 8173e1f973 feat: "同僚"改为"同伴" + KYC从三要素改为二要素
mobile-app:
- profile_page.dart: 将所有"同僚"改为"同伴"

identity-service:
- 层级1实名认证从三要素(姓名+身份证+手机号)改为二要素(姓名+身份证号)
- 使用阿里云 Id2MetaStandardVerify API
- 二要素验证直接调用真实API,不使用mock
- 保留三要素验证方法(verifyIdCardThreeFactor)备用

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 04:20:42 -08:00
hailin 47e4ef2b33 feat(android): add share export and import functionality
Add ability to backup wallet shares to files and restore from backups:

- Add ShareBackup data class in Models.kt for backup format
- Add exportShareBackup() and importShareBackup() in TssRepository
- Add export/import state and methods in MainViewModel
- Add file picker integration in MainActivity using ActivityResultContracts
- Add import FAB button in WalletsScreen
- Export saves as .tss-backup file with address and timestamp in filename
- Import validates backup format and checks for duplicate wallets

The backup file contains all necessary data to restore a wallet share:
sessionId, publicKey, encryptedShare, threshold, partyIndex, address.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 03:35:37 -08:00
hailin 9f33e375d0 fix(android): add @OptIn annotation for experimental FilterChip API
Add @OptIn(ExperimentalMaterial3Api::class) to TransferInputScreen
composable to fix compilation error for FilterChip and FilterChipDefaults.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 03:06:32 -08:00
hailin 9b9612bd5f feat(token): add Green Points (绿积分) ERC-20 token support
Add support for the dUSDT token "绿积分" (Green Points) on both Android
and Electron applications:

Android changes:
- Add TokenType enum and GreenPointsToken config in Models.kt
- Implement ERC-20 balance fetching and transfer encoding in TssRepository
- Update TransactionUtils with ERC-20 transfer support
- Add dual balance display (KAVA + 绿积分) in WalletsScreen
- Add token type selector in TransferScreen

Electron changes:
- Add TokenType and GREEN_POINTS_TOKEN config in transaction.ts
- Implement fetchGreenPointsBalance and ERC-20 transfer encoding
- Update Home.tsx with dual balance display and token selector
- Add token selector styles in Home.module.css

Token contract: 0xA9F3A35dBa8699c8C681D8db03F0c1A8CEB9D7c3 (Kava mainnet)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 03:01:05 -08:00
hailin b3822e48eb fix(android): decode Base64 signature before broadcasting transaction
The TSS native bridge returns signatures in Base64 format, but the
broadcast function expected hex format. Added Base64 decoding in
broadcastTransaction() to properly parse r, s, v components.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 22:44:44 -08:00
hailin 2365a50b1b feat(tss): add real-time round progress from msg.Type() parsing
Extract current round number from tss-lib message type string using
regex pattern `Round(\d+)`. This enables real-time progress updates
(1/4, 2/4... for keygen, 1/9, 2/9... for signing) instead of only
showing completion status.

Changes across all three platforms:
- tss-wasm/main.go: Add extractRoundFromMessageType() and call
  OnProgress with parsed round on each outgoing message
- service-party-android/tsslib/tsslib.go: Same implementation for
  Android gomobile binding
- service-party-app/tss-party/main.go: Same implementation for
  Electron subprocess, with isKeygen parameter to distinguish
  keygen (4 rounds) vs signing (9 rounds)

Safe fallback: Returns 0 if parsing fails, which doesn't affect
protocol execution - only UI display.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 22:41:51 -08:00
hailin f8de55e671 fix(android): reset isLoading after signing completes to enable broadcast button
The broadcast button was disabled because isLoading remained true after
signing completed. Added isLoading = false reset in startSigningProcess
after waitForSignature succeeds or fails.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 22:21:30 -08:00
hailin 001f0ac480 fix(android): remove 0x prefix from messageHash before TSS sign
TSS native library expects pure hex string without 0x prefix.
Fix both startSigning (initiator) and executeSignAsJoiner (joiner) functions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 22:04:18 -08:00
hailin 0bd764e1d1 fix(android): ensure session event subscription active before creating sign session
Add ensureSessionEventSubscriptionActive() call at the start of createSignSession()
to prevent race condition where session_started event arrives before subscription
is ready. Also add debug logging for _signSessionId and pendingSignInitiatorInfo
in event callback to help diagnose sign initiator event matching issues.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 21:45:18 -08:00
hailin ecd7a2a2dc fix(android): clear pendingSessionId in resetSessionStatus to fix stale session matching
The resetSessionStatus() function was not clearing pendingSessionId,
causing events from new sessions to be ignored because pendingSessionId
still held the old session ID.

Added:
- Clear pendingSessionId = null in resetSessionStatus()
- Clear _currentSession.value = null in resetSessionStatus()
- Added debug logging for session state clearing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 21:11:24 -08:00
hailin e865153e8e fix(android): refresh session event subscription when joining sign session
The session event gRPC stream may silently disconnect without triggering
onError or onCompleted callbacks. This causes session_started events to
be lost, preventing the sign process from starting.

Changes:
- Add ensureSessionEventSubscriptionActive() to refresh event subscription
- Call it in joinSignSessionViaGrpc for sign joiner
- Call it in createSignSession for sign initiator after auto-join

This ensures a fresh event stream connection before waiting for events.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 20:47:13 -08:00
hailin da76037d04 fix(tss-wasm): correct signing rounds from 6 to 9
GG20 signing protocol has 9 rounds, not 6. This aligns WASM with
Electron (tss-party/main.go:717) and Android (tsslib/tsslib.go:477).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 20:24:47 -08:00
hailin 16e1e9159c fix(android): sign initiator event handling to match Electron flow
Changes:
- Add sign initiator handling for participant_joined events in MainViewModel
- Add sign initiator handling for session_started events in MainViewModel
- Add sign initiator handling for all_joined events in MainViewModel
- Set pendingSessionId in TssRepository.createSignSession for event matching
- Refactor initiateSignSession to wait for session_started instead of starting immediately
- Add PendingSignInitiatorInfo data class to store pending sign info
- Add sessionAlreadyInProgress flag to SignSessionResult for immediate trigger case

This fixes the issue where sign initiator couldn't detect when other parties
joined the signing session, making Android flow 100% consistent with Electron.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 20:14:32 -08:00
hailin fd56de5c00 fix(android): enable real-time progress updates for keygen/sign rounds
Connect TssNativeBridge.progress Flow to UI through:
- Add progressCallback in TssRepository with startProgressCollection/stopProgressCollection
- Subscribe to native bridge progress in keygen and sign methods
- Add setProgressCallback in MainViewModel to update appropriate round state
- Progress now flows: Go Native → TssNativeBridge → TssRepository → MainViewModel → UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 19:45:31 -08:00
hailin 3576af0f25 feat(android): add 5-minute countdown timer UI for keygen/sign sessions
Displays remaining time during the 5-minute polling timeout:
- Shows countdown in CreateWalletScreen (SessionScreen)
- Shows countdown in JoinKeygenScreen (JoiningScreen, KeygenProgressScreen)
- Shows countdown in CoSignJoinScreen (JoiningScreen, SigningProgressScreen)
- Format: mm:ss with Timer icon in tertiary container card
- Countdown starts on all_joined event and stops on session start/cancel

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 19:33:20 -08:00
hailin b30017f3a7 fix(android): prevent memory leaks from detached coroutine scopes
Critical fixes to prevent app crashes when screens are kept open for extended periods:

- Add repositoryScope with SupervisorJob for structured concurrency in TssRepository
- Replace detached CoroutineScope(Dispatchers.IO).launch with repositoryScope.launch:
  - Session event subscription (line 206)
  - Session status polling (line 291)
  - Message routing (line 1508)
- Add cleanup() method to properly cancel all jobs and repositoryScope
- Update disconnect() to also cancel sessionStatusPollingJob
- Update MainViewModel.onCleared() to call repository.cleanup()

This ensures all background coroutines are properly cancelled when the ViewModel
is cleared, preventing memory accumulation over time.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 19:05:13 -08:00
hailin fc86af918f feat(android): add 5-minute polling timeout mechanism for keygen/sign
Implements Electron's checkAndTriggerKeygen() polling fallback:
- Adds polling every 2 seconds with 5-minute timeout
- Triggers keygen/sign via synthetic session_started event on in_progress status
- Handles gRPC stream disconnection when app goes to background
- Shows timeout error in UI via existing error mechanism

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 18:51:02 -08:00
hailin ad79679ee2 feat(ui): add QR code display for invite code in signing session
Android TransferScreen:
- Add QR code display above invite code text
- Import QRCodeWriter and related components
- Add generateInviteQRCode helper function
- Update hint text to mention scanning

Electron CoSignSession:
- Import QRCodeSVG from qrcode.react
- Add QR code above invite code text with proper styling
- Center QR code and update hint text

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 18:37:18 -08:00
hailin ed55be2b86 fix(android): transfer flow improvements and message_hash format fix
Transfer Screen improvements:
- Add QR code scanning for recipient address (using zxing library)
- Support EIP-681 URI format (ethereum:0x...) and plain address
- Remove password requirement - TSS wallets don't need passwords
- Remove unused onScanQrCode callback parameter

WalletsScreen changes:
- Simplify onTransfer callback to only pass shareId
- Remove TransferDialog - now navigates directly to TransferScreen
- Remove unused state variables (showTransferDialog, transferWallet)

Bug fix:
- Remove 0x prefix from message_hash before sending to API
- Backend expects pure hex, not 0x-prefixed hex string

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 18:18:29 -08:00
hailin 480251b85f fix(android): remove incorrect Participant import
Participant class is already imported via domain.model.* wildcard import,
no need for separate import from data.repository (where it doesn't exist).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:03:31 -08:00
hailin ee1cfe082d fix(android): resolve compilation errors for walletName and Participant
- TssRepository: Use address-based wallet name since ShareRecordEntity
  doesn't have wallet_name field (unlike Electron's ShareRecord)
- MainViewModel: Add missing Participant import and simplify type reference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:00:36 -08:00
hailin 04eeadf7a7 fix(android): co-sign flow consistency with Electron + state reset
Changes:
- Fix Android state not resetting after successful keygen/join
  - Add resetSessionStatus() method in TssRepository
  - Call reset on success navigation in MainActivity

- Make Android co-sign flow 100% consistent with Electron:
  - Get keygen session status for participants list
  - Filter out co-managed-party-* (server backup parties)
  - Auto-join via gRPC after creating sign session
  - Start message routing BEFORE signing (prepareForSign)
  - Use gRPC response partyIndex instead of local share
  - Use original keygen thresholdN instead of signingParties.size
  - Pass parties list in join sign flow

- Update SignSessionInfoResponse to include parties array
- Update validateSignInviteCode to parse parties from API response

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 10:52:13 -08:00
hailin 7346b3518a fix(electron): auto-navigate to home after keygen completion
Previously, after keygen completed, the Session page would just update the
status to 'completed' but not navigate away. Users had to manually click
the "Return Home" button. This could result in a white screen if the button
wasn't visible or clickable.

Now the page auto-navigates to home after 2 seconds, giving users time to
see the completion status and public key before redirecting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 10:22:42 -08:00
hailin 549b21f298 fix(message-router): prevent subscription race condition on gRPC reconnect
When a party re-subscribes (e.g., Android reconnects), the old gRPC stream's
defer Unsubscribe() was accidentally removing the NEW subscription from the
subscribers map, causing the party to miss session_started events.

Fix:
- Subscribe() now returns the channel to the caller
- Unsubscribe() now takes the channel and only removes if it matches
- This prevents older streams from removing newer subscriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 10:04:11 -08:00
hailin b7fc488dcf feat(android): add persistent partyId storage matching Electron behavior
- Add AppSettingEntity and AppSettingDao to Database.kt for key-value storage
- Add database migration (version 1 → 2) to create app_settings table
- Modify TssRepository.registerParty() to load/create partyId from database
- PartyId is now persisted across app restarts, matching Electron's getOrCreatePartyId()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 09:50:30 -08:00
hailin e2451874ea fix(android): add logging for session event subscription debugging
- Add warning log when parties miss session event broadcast (message-router)
- Add logging for subscribeSessionEvents to detect null asyncStub
- Add sessionStatusPollingJob field for future fallback polling mechanism

This helps diagnose why Android parties are not receiving session_started events.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 09:23:38 -08:00
hailin eb3f71fa2e fix(android): fix session_started event race condition with pendingSessionId
Problem:
- Android initiator/joiner could miss session_started events due to race condition
- Events arriving between joinSession() and _currentSession.value assignment were ignored
- This caused keygen timeout because parties never started the TSS protocol

Solution:
- Add pendingSessionId field set BEFORE joinSession() call
- Modify startSessionEventSubscription() to match events against both activeSession and pendingSessionId
- Clear pendingSessionId on session completion, failure, or cancellation

This ensures session_started events are correctly processed even if they arrive
before _currentSession is fully initialized.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 09:09:52 -08:00
hailin cc56b8fadf fix(android): fetch session status after creation to show all participants
- Add getPartyId() method to TssRepository
- Call getSessionStatus after createKeygenSession to fetch all participants
  including server-party-co-managed that have already auto-joined
- This matches Electron's behavior of calling getSessionStatus on session page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 08:51:49 -08:00