Backend (authorization-service):
- Add QueryAuthorizationsDto for query parameters (roleType, keyword, includeRevoked, page, limit)
- Add queryAuthorizations method to fetch all authorizations with user info
- Add GET /admin/authorizations endpoint for listing authorizations
- Add POST /admin/authorizations/:id/revoke endpoint for revoking authorization
Frontend (admin-web):
- Add authorization.types.ts with RoleType, Authorization, and request types
- Add authorizationService.ts for API calls (list, revoke, grant operations)
- Add useAuthorizations.ts React Query hooks
- Update authorization page to use real API data instead of mock data
- Add loading/error states, pagination, and revoke reason display
- Add new styles for loading, error, pagination, and date columns
The authorization management page now displays all authorized users
from the database with support for filtering by role type, status,
and keyword search.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
- 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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
- 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>
- 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>
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>
- 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>
Backend changes (session-coordinator):
- Add PublishParticipantJoined method to JoinSessionMessageRouterClient interface
- Implement PublishParticipantJoined in MessageRouterClient to broadcast events
- Call PublishParticipantJoined in join_session.go after participant joins
- Add detailed logging for debugging event broadcast
Android changes (service-party-android):
- Add detailed logging in TssRepository for session event handling
- Add detailed logging in MainViewModel for participant_joined processing
- Log activeSession state, event matching, and participant updates
This enables the initiator's waiting screen to receive real-time updates
when participants join the session, matching the expected behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Created PortraitCaptureActivity that extends CaptureActivity
- Registered it in AndroidManifest.xml with screenOrientation="portrait"
- Updated JoinKeygenScreen and CoSignJoinScreen to use the portrait activity
- Also simplified keygen join logic to match Electron exactly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>