Added migration_lock.toml for planting-service, reward-service,
referral-service, and authorization-service to ensure Prisma
can properly track migration provider.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement reliable event delivery using Outbox Pattern with consumer confirmation:
## planting-service (Producer)
- Add OutboxEvent table with status: PENDING → SENT → CONFIRMED
- Add OutboxRepository with transaction support and timeout handling
- Add OutboxPublisherService with polling, timeout check, and retry
- Add EventAckController to receive consumer confirmations
- Update UnitOfWork to save outbox events atomically with business data
- Update PlantingApplicationService to use outbox pattern
- Update PoolInjectionService to use outbox pattern
## Consumer Services
- Add EventAckPublisher to reward-service, referral-service, authorization-service
- Update event handlers to send acknowledgment after successful processing
## Event Flow
1. Business data + outbox events saved in same transaction
2. OutboxPublisher polls and sends to Kafka, marks as SENT
3. Consumer processes event and sends ack to planting.events.ack
4. EventAckController receives ack and marks as CONFIRMED
5. Timeout check resets SENT→PENDING for retry (max 5 times)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add initial migration for wallet-service (wallet_accounts, ledger_entries, deposit/settlement/withdrawal orders)
- Add initial migration for planting-service (planting_orders, fund_allocations, positions, batches, events)
- Both services now have migrations that will be automatically applied on container startup via `prisma migrate deploy`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add /api/v1/wallet route to Kong gateway to support:
- GET /wallet/my-wallet
- POST /wallet/claim-rewards
- POST /wallet/settle
- POST /wallet/withdraw
- GET /wallet/withdrawals
- GET /wallet/ledger/my-ledger
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change BoxFit.cover to BoxFit.fitWidth for guide page images
- Add screen info logging (resolution, pixel ratio, aspect ratio)
- Add detailed avatar loading logs in frontend and backend
- Log avatarUrl from DB and API response during recovery
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added debug logs in:
- ProfilePage._loadUserData() - local storage data
- ProfilePage._loadMeData() - API response and sync conditions
- ProfilePage._buildAvatarContent() - avatar rendering decision
- UserApplicationService.getMe() - backend avatarUrl value
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When recovering account via mnemonic or phone, check if avatarUrl is null
and regenerate a new random avatar SVG if needed. This fixes the bug where
users see no avatar after recovering their account.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Downloads route was getting /api/v1 prefix, making it inaccessible at /downloads
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set BASE_URL to public API gateway address so mobile app can download
APK files from correct URL instead of localhost.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Mobile app was calling /api/app/version/check but this route was not
configured in Kong gateway. Added admin-mobile-version route to forward
these requests to admin-service.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Changelog: allow empty value, return default "No changelog provided"
- MinOsVersion: allow empty value and single number format (e.g., "24" -> "24.0")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- admin-service: provide default empty string for optional changelog
- profile page: fix map literal syntax for referrals list
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- VersionName value object now accepts x.y.z.w format
- changelog field is now optional in upload version DTO
- profile page: ensure expired section has full width
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allow version names like 1.0.0.4 in addition to the standard x.y.z format.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The balance display was incorrect because decimals were hardcoded to 18,
but USDT uses 6 decimals. Now reads decimals() from the ERC20 contract.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sessionTimeout remains at 5 minutes (needed for long MPC operations),
but rebalanceTimeout only affects joining consumer group, not processing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Root cause: When mpc-service restarts, the old Kafka consumer doesn't
properly disconnect, causing the broker to wait for sessionTimeout
(~5 minutes) before completing rebalance. This blocks app startup.
Solution: Enable NestJS shutdown hooks with app.enableShutdownHooks().
This ensures onModuleDestroy() is called on SIGTERM/SIGINT, which calls
consumer.disconnect() and allows immediate rebalance on next startup.
Also reverted the "don't await consumer.run()" workaround since the
proper fix is graceful shutdown.
Sources:
- https://github.com/tulios/kafkajs/issues/807
- https://kafka.js.org/docs/consuming🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
consumer.run() never resolves as it runs continuously. Awaiting it
blocks onApplicationBootstrap which prevents app.listen() from being
called, causing the service to never start listening on port 3006.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Restore Dockerfile to use external docker-entrypoint.sh file
- The inline echo script caused issues on Linux (start.sh not created properly)
- Change from prisma migrate deploy to prisma db push for schema flexibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add SystemAccount domain in authorization-service for managing regional/company accounts
- Implement fund allocation service in planting-service with multi-tier distribution
- Add WithdrawalOrder aggregate in wallet-service with full lifecycle management
- Create internal wallet controller for cross-service fund allocation
- Add Kafka event publishing for withdrawal requests
- Implement unit-of-work pattern for transactional consistency
- Update Prisma schemas with withdrawal order and system account tables
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Kong routes for identity-service /me, referral-service, and authorization-service
- Create AuthorizationService in Flutter for fetching user authorizations
- Extend ReferralService with getMyReferralInfo() and getDirectReferrals() methods
- Update profile_page.dart to display real team stats from APIs
- Fix authorization-service JWT strategy to accept identity-service token format
- Add decimal.js dependency to authorization-service
- Add prisma migration file for authorization-service
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
KAVA now uses EVM-compatible 0x addresses instead of Cosmos bech32 format.
DST continues to use Cosmos bech32 format (dst1...).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Backend Changes
### referral-service
- Add accountSequence field to ReferralRelationship aggregate for cross-service user identification
- Add findByAccountSequence() method to repository interface and implementation
- Update CreateReferralRelationshipCommand to accept accountSequence and inviterAccountSequence
- Modify ReferralService to support looking up inviter by accountSequence
- Update event handler to listen to identity.UserAccountAutoCreated and identity.UserAccountCreated topics
- Add initial database migration with all tables including accountSequence field
- Update DTO and controller to support new parameters
### identity-service
- Add inviterSequence field to MeResult interface
- Update getMe() method to return inviterSequence from user account
- Update MeResponseDto to include inviterSequence field
## Frontend Changes (mobile-app)
### API & Storage
- Add /me endpoint constant in api_endpoints.dart
- Add inviterSequence key in storage_keys.dart
- Add MeResponse and WalletAddressInfo classes in account_service.dart
- Add getMe() method to fetch complete user info including inviter
- Add getInviterSequence() method to retrieve from local storage
### Profile Page
- Update profile_page.dart to load referrer info from API
- Add _loadMeData() method to call getMe() API
- Display inviterSequence (referrer serial number) dynamically
## Flow Summary
1. User creates account with optional inviterReferralCode
2. identity-service validates and saves inviterSequence
3. identity-service publishes UserAccountAutoCreated/UserAccountCreated event
4. referral-service listens and creates referral relationship using inviterAccountSequence
5. Mobile app calls GET /me to display inviter info in profile page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Address Derivation Changes
- Change KAVA from Cosmos bech32 (kava1...) to EVM format (0x...)
- KAVA now uses same EVM address as BSC for deposit monitoring
- Add KAVA to evmChains set for automatic monitoring registration
## Database Schema Updates (Migration: 20241208000000)
- MonitoredAddress: add address_type, account_sequence, system_account_type,
system_account_id, region_code columns
- DepositTransaction: add address_type, account_sequence, system_account_type,
system_account_id columns
- Make user_id nullable for system account support
- Create recovery_mnemonics table for account recovery
- Add indexes: idx_account_sequence, idx_type_active, idx_system_account_type,
idx_deposit_account, and recovery_mnemonics indexes
## New Features
- Withdrawal request handler and Kafka consumer
- Test USDT deployment scripts for KAVA and BSC
- Smart contracts for TestUSDT token
## Infrastructure Updates
- Update mappers for new schema fields
- Update application and infrastructure modules
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace random username generation with fixed format
- Username now uses accountSequence: '榴莲女皇{序列号}号'
- Keep random durian-themed SVG avatar generation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
blockchain-service:
- Add accountSequence to monitored_addresses and deposit_transactions
- Support BSC/KAVA testnet via NETWORK_MODE environment variable
- Add chain config service with testnet RPC endpoints
- Update deposit detection with accountSequence propagation
wallet-service:
- Add accountSequence to wallet_accounts and ledger_entries
- Fix JWT strategy to match identity-service token format
- Update deposit handling with accountSequence correlation
- Add repository methods for accountSequence-based queries
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit fixes three critical bugs that prevented the wallet creation
flow from completing successfully:
1. mpc-service: extraPayload not included in Kafka messages
- KeygenCompletedEvent's extraPayload (containing userId, accountSequence,
username, derivedAddresses) was being set dynamically but not serialized
- identity-service received events without userId and skipped processing
- Fix: Merge extraPayload into the published payload in event-publisher
2. mpc-service: KAFKA_BROKERS hostname mismatch
- mpc-service used KAFKA_BROKERS=rwa-kafka:29092
- Kafka advertises itself as kafka:29092 in cluster metadata
- During consumer group rebalance, mpc-service couldn't connect to
the coordinator address returned by Kafka
- Fix: Use kafka:29092 to match Kafka's advertised listener
3. blockchain-service: recovery_mnemonics table missing
- RecoveryMnemonic model exists in schema.prisma but not in migration
- prisma migrate deploy found no pending migrations
- Address derivation failed with "table does not exist" error
- Fix: Use prisma db push instead of migrate deploy to sync schema
Tested: E2E flow now completes successfully
- POST /user/auto-create creates account
- MPC keygen completes and publishes event with extraPayload
- blockchain-service derives addresses and saves recovery mnemonic
- GET /user/wallet returns status=ready with 3 addresses and mnemonic
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- mpc-service: remove duplicate index idx_ms_username on mpc_shares.username
(keep only unique index uq_ms_username since username is unique)
- identity-service: remove redundant migration 20241206000000_add_device_hardware_info
(fields already exist in initial migration 20241204000000_init)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes across all three services to properly associate recovery mnemonics
with account sequence numbers instead of user IDs, following DDD principles:
identity-service:
- Add accountSequence to MpcKeygenRequestedEvent payload
- Pass accountSequence when publishing keygen request
- Remove direct access to recoveryMnemonic table (now in blockchain-service)
- Call blockchain-service for mnemonic backup marking
- BlockchainWalletHandler no longer saves mnemonic (stored in blockchain-service)
mpc-service:
- Add accountSequence to KeygenRequestedPayload interface
- Pass accountSequence through to blockchain-service when deriving addresses
- Include accountSequence in KeygenCompleted event extraPayload
blockchain-service:
- Add accountSequence to derive-address API and internal interfaces
- Add accountSequence to KeygenCompletedPayload extraPayload
- Add PUT /internal/mnemonic/backup API for marking mnemonic as backed up
- Store recovery mnemonic with accountSequence association
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add RecoveryMnemonic model to blockchain-service with accountSequence
- Add MnemonicVerificationService for mnemonic verification logic
- Update verify-mnemonic-hash endpoint to accept accountSequence
- Remove PrismaService dependency from identity-service handler
- identity-service now calls blockchain-service for mnemonic verification
This follows DDD principles: blockchain-service owns all mnemonic-related
data and operations, identity-service only handles account identity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The mnemonic recovery now uses stored hash comparison via blockchain-service
instead of trying to derive addresses from mnemonic (which never matched
because MPC wallet addresses are not derived from mnemonics).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix mnemonic verification by connecting to blockchain-service on port 3012
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sync MINIO_SECRET_KEY default value with MinIO server config
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
CDN domain has nginx host header issue, use minio.szaiai.com directly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update MINIO_ENDPOINT from localhost to 192.168.1.100 (Server A)
- Add MinIO environment variables to docker-compose.yml
- Set MINIO_PUBLIC_URL to https://cdn.szaiai.com for CDN access
- Add MinIO config section to backend/services/.env.example
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove SSL configuration from nginx config file. SSL will be added
automatically by certbot when running ./install.sh --ssl
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>