Update docs for TON proof backend, Mini App fixes, and EVM wallet stub
PRD - TON Wallet Ownership Proof.md:
- Status updated from ready-to-implement -> backend-implemented.
- Added Implementation Status section documenting what is complete
(challenge endpoint, tonProofService.ts, User model fields, 15 tests)
and what remains (frontend proof wiring, verified badge).
- Acceptance criteria updated: backend items checked, frontend pending.
Handoff - Telegram Mini App Debug - 2026-05-24.md:
- New Implemented sections for session 3:
- TON Wallet Ownership Proof backend (full detail of tonProofService,
userController changes, User model fields, 15 unit tests).
- Telegram Mini App shell dir="ltr" + smart displayName fallback.
- Socket status suppressed on /telegram paths.
- EVM WalletConnect stub card (disabled until project ID configured).
- Known Issues: TON proof updated to "frontend wiring pending";
EVM WalletConnect section added with activation steps.
- Current Git State updated to Session 3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -513,7 +513,7 @@
|
|||||||
"title": "Implement bot command and notification foundation",
|
"title": "Implement bot command and notification foundation",
|
||||||
"description": "Create the Telegram bot backend for commands, inline keyboards, callback queries, deep links, and outbound notifications.",
|
"description": "Create the Telegram bot backend for commands, inline keyboards, callback queries, deep links, and outbound notifications.",
|
||||||
"details": "Support start/help/link/status/request/offer/payment/dispute/settings basics. Use short opaque IDs or signed tokens for callback payloads. Process incoming updates idempotently with rate limits. Respect notification preferences, quiet/error states, failed delivery, blocked bot, and retry observability.",
|
"details": "Support start/help/link/status/request/offer/payment/dispute/settings basics. Use short opaque IDs or signed tokens for callback payloads. Process incoming updates idempotently with rate limits. Respect notification preferences, quiet/error states, failed delivery, blocked bot, and retry observability.",
|
||||||
"status": "in-progress",
|
"status": "done",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
1,
|
1,
|
||||||
2
|
2
|
||||||
@@ -521,7 +521,7 @@
|
|||||||
"priority": "high",
|
"priority": "high",
|
||||||
"testStrategy": "See Telegram-native PRD acceptance criteria.",
|
"testStrategy": "See Telegram-native PRD acceptance criteria.",
|
||||||
"parentId": "undefined",
|
"parentId": "undefined",
|
||||||
"updatedAt": "2026-05-24T09:18:15.004Z"
|
"updatedAt": "2026-05-24T13:46:14.458Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
@@ -634,12 +634,12 @@
|
|||||||
"updatedAt": "2026-05-24T11:59:32.372Z"
|
"updatedAt": "2026-05-24T11:59:32.372Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"updatedAt": "2026-05-24T11:59:32.372Z"
|
"updatedAt": "2026-05-24T13:46:14.458Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lastModified": "2026-05-24T11:59:32.372Z",
|
"lastModified": "2026-05-24T13:46:14.458Z",
|
||||||
"taskCount": 5,
|
"taskCount": 5,
|
||||||
"completedCount": 4,
|
"completedCount": 4,
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
311
07 - Development/PRD - TON Wallet Ownership Proof.md
Normal file
311
07 - Development/PRD - TON Wallet Ownership Proof.md
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
---
|
||||||
|
title: PRD — TON Wallet Ownership Proof
|
||||||
|
tags: [prd, ton, wallet, security, telegram]
|
||||||
|
created: 2026-05-24
|
||||||
|
status: backend-implemented
|
||||||
|
priority: high
|
||||||
|
---
|
||||||
|
|
||||||
|
# PRD — TON Wallet Ownership Proof
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
TON wallet connect was shipped as a first-pass integration: the backend validates address format and stores it, but does not require the user to prove they control the private key behind the address. Any user can submit any valid-looking TON address and it will be saved to their profile.
|
||||||
|
|
||||||
|
This matters for:
|
||||||
|
- **Seller payout routing** — if a seller's wallet address was spoofed, funds go to the wrong address.
|
||||||
|
- **On-chain proof of identity** — downstream features (NFT-gated access, on-chain reputation) require a real ownership proof.
|
||||||
|
- **Compliance** — KYC/AML flows that tie a real-world identity to a wallet need proof, not self-report.
|
||||||
|
|
||||||
|
Until proof is implemented, saved TON wallet addresses are self-reported metadata only and must not be used for automatic payout routing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Replace the format-only TON wallet save with a full TonConnect ownership proof flow:
|
||||||
|
- Backend issues a challenge nonce scoped to the user session.
|
||||||
|
- Frontend passes the challenge to TonConnect UI, which asks the user to sign it.
|
||||||
|
- Backend verifies the returned proof against the wallet's public key.
|
||||||
|
- On success, stores `walletProofVerified: true`, proof timestamp, and wallet provider.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- On-chain transaction signing or payment initiation (separate feature).
|
||||||
|
- Requiring proof for EVM wallets (already covered by `ethers.verifyMessage`).
|
||||||
|
- TON DNS or NFT resolution.
|
||||||
|
- Mandatory wallet for all users (wallet remains optional profile metadata).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Stories
|
||||||
|
|
||||||
|
| As a… | I want to… | So that… |
|
||||||
|
|-------|-----------|----------|
|
||||||
|
| Seller | Connect my Telegram Wallet with proof | Payments route to the right address |
|
||||||
|
| Buyer | Know my counterparty's wallet is verified | I trust the seller's payout address |
|
||||||
|
| Admin | See `walletProofVerified` on user profiles | I can audit wallet verification status |
|
||||||
|
| Developer | Have a clear nonce/challenge API | I can build additional proof-gated features |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend Backend TonConnect
|
||||||
|
| | |
|
||||||
|
|-- POST /wallet-address/ton-proof/challenge --> |
|
||||||
|
| |-- generate nonce |
|
||||||
|
| |-- store nonce (TTL 5 min) |
|
||||||
|
|<-- { payload, domain, timestamp } -- |
|
||||||
|
| | |
|
||||||
|
|-- tonConnectUI.connectWallet({ tonProof: payload }) ---------->|
|
||||||
|
| | |-- user signs proof
|
||||||
|
|<-- proof object (wallet, stateInit, signature) ---------------|
|
||||||
|
| | |
|
||||||
|
|-- PATCH /wallet-address { walletType:'ton', tonProof } --> |
|
||||||
|
| |-- verify proof |
|
||||||
|
| | - reconstruct message |
|
||||||
|
| | - verify against pubkey |
|
||||||
|
| | - check domain + nonce |
|
||||||
|
| |-- store wallet + proof meta |
|
||||||
|
|<-- { walletProofVerified: true } -- |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backend Changes
|
||||||
|
|
||||||
|
### 1. New endpoint: `POST /api/user/wallet-address/ton-proof/challenge`
|
||||||
|
|
||||||
|
**Auth:** Bearer JWT required.
|
||||||
|
|
||||||
|
**Request body:** none.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"payload": "aGV4LWVuY29kZWQtbm9uY2U...",
|
||||||
|
"domain": "amn.gg",
|
||||||
|
"timestamp": 1716560000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
- Generate 32-byte random nonce, hex-encode.
|
||||||
|
- Store in a TTL map (or Redis `SET NX EX 300`) keyed by `userId`.
|
||||||
|
- Return `{ payload: nonce, domain: "amn.gg", timestamp: now }`.
|
||||||
|
- One active challenge per user — issuing a new one invalidates the old.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Updated: `PATCH /api/user/wallet-address`
|
||||||
|
|
||||||
|
**When `walletType = 'ton'` and a `tonProof` object is present:**
|
||||||
|
|
||||||
|
Accept additional body field:
|
||||||
|
```ts
|
||||||
|
tonProof?: {
|
||||||
|
timestamp: number;
|
||||||
|
domain: { lengthBytes: number; value: string };
|
||||||
|
signature: string; // base64
|
||||||
|
payload: string; // the nonce from challenge
|
||||||
|
stateInit?: string; // base64, for wallets not yet deployed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verification logic (using `@ton/ton` and `@ton/crypto`):**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Address, Cell, beginCell, contractAddress } from '@ton/ton';
|
||||||
|
import nacl from 'tweetnacl';
|
||||||
|
|
||||||
|
// 1. Validate nonce matches stored challenge for this user
|
||||||
|
// 2. Validate timestamp is within 5-minute window
|
||||||
|
// 3. Validate domain === 'amn.gg'
|
||||||
|
// 4. Reconstruct the signed message:
|
||||||
|
const message = Buffer.concat([
|
||||||
|
Buffer.from('ton-proof-item-v2/'),
|
||||||
|
Buffer.from(new Uint32Array([domain.length]).buffer),
|
||||||
|
Buffer.from(domain, 'utf8'),
|
||||||
|
Buffer.from(new Uint64Array([timestamp]).buffer),
|
||||||
|
Buffer.from(payload, 'hex'),
|
||||||
|
]);
|
||||||
|
const hash = Buffer.from(sha256(Buffer.concat([
|
||||||
|
Buffer.from([0xff, 0xff]),
|
||||||
|
Buffer.from('ton-connect'),
|
||||||
|
sha256(message),
|
||||||
|
])));
|
||||||
|
// 5. Recover public key from stateInit if wallet not deployed,
|
||||||
|
// or fetch from blockchain (tonweb/lite-client) if deployed.
|
||||||
|
// 6. Verify: nacl.sign.detached.verify(hash, sig, pubkey)
|
||||||
|
// 7. Verify: Address.parse(walletAddress) matches derived address
|
||||||
|
```
|
||||||
|
|
||||||
|
**On success:**
|
||||||
|
- Store wallet fields: `profile.walletAddress`, `profile.walletType = 'ton'`, `profile.walletProvider`.
|
||||||
|
- Set new fields: `profile.walletProofVerified = true`, `profile.walletProofTimestamp = new Date()`.
|
||||||
|
- Clear the stored nonce.
|
||||||
|
|
||||||
|
**On failure:**
|
||||||
|
- Return `400 INVALID_TON_PROOF`.
|
||||||
|
- Do not update wallet fields.
|
||||||
|
- Do not clear the nonce (allow retry within TTL).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. User model additions
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// models/User.ts — inside profile
|
||||||
|
walletProofVerified?: boolean;
|
||||||
|
walletProofTimestamp?: Date;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Dependencies to add
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn add @ton/ton @ton/crypto tweetnacl
|
||||||
|
```
|
||||||
|
|
||||||
|
`@ton/ton` is already a known dependency (the `@ton/core` frontend package is related). The backend needs `@ton/ton` for address parsing and cell operations, and `tweetnacl` for ed25519 verification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frontend Changes
|
||||||
|
|
||||||
|
### 1. Fetch challenge before connecting
|
||||||
|
|
||||||
|
In `TonWalletProvider` (or the connect handler in `account-wallet-connection.tsx`):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Before calling tonConnectUI.connectWallet()
|
||||||
|
const { payload } = await api.post('/api/user/wallet-address/ton-proof/challenge');
|
||||||
|
|
||||||
|
// Pass proof request to TonConnect
|
||||||
|
const proof: ConnectAdditionalRequest = {
|
||||||
|
tonProof: payload,
|
||||||
|
};
|
||||||
|
await tonConnectUI.connectWallet(proof);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Extract and send proof after connection
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// After connection, wallet.connectItems.tonProof is populated
|
||||||
|
const wallet = tonConnectUI.wallet;
|
||||||
|
if (!wallet?.connectItems?.tonProof || 'error' in wallet.connectItems.tonProof) {
|
||||||
|
throw new Error('TON proof not returned by wallet');
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.patch('/api/user/wallet-address', {
|
||||||
|
walletAddress: wallet.account.address,
|
||||||
|
walletType: 'ton',
|
||||||
|
walletProvider: wallet.device.appName,
|
||||||
|
tonProof: wallet.connectItems.tonProof.proof,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Show proof badge on wallet page
|
||||||
|
|
||||||
|
When `profile.walletProofVerified = true`, show a "Verified" badge next to the wallet address. When false (or absent), show "Unverified" with a "Verify ownership" button.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Model
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Addition to profile sub-schema
|
||||||
|
walletProofVerified?: boolean; // true after successful TonConnect proof
|
||||||
|
walletProofTimestamp?: Date; // when proof was last verified
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing fields retain their meaning:
|
||||||
|
- `walletAddress` — the TON address (friendly format)
|
||||||
|
- `walletType` — `'ton'` | `'evm'`
|
||||||
|
- `walletProvider` — e.g. `'Telegram Wallet'`, `'Tonkeeper'`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
| Method | Path | Auth | Purpose |
|
||||||
|
|--------|------|------|---------|
|
||||||
|
| POST | `/api/user/wallet-address/ton-proof/challenge` | JWT | Get nonce for proof |
|
||||||
|
| PATCH | `/api/user/wallet-address` | JWT | Save wallet + verify proof |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- Challenge endpoint returns `{ payload, domain, timestamp }`.
|
||||||
|
- Challenge is bound to userId (different users get different nonces).
|
||||||
|
- Challenge expires: re-issuing invalidates previous.
|
||||||
|
- Wallet save with valid proof sets `walletProofVerified = true`.
|
||||||
|
- Wallet save with expired nonce returns 400.
|
||||||
|
- Wallet save with wrong domain returns 400.
|
||||||
|
- Wallet save with bad signature returns 400.
|
||||||
|
- Wallet save without `tonProof` object sets `walletProofVerified = false` (legacy format-only path).
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- `connectWallet` is called with `tonProof: payload`.
|
||||||
|
- If proof is absent in response, shows error and does not call save endpoint.
|
||||||
|
- If backend returns 400, shows "Wallet verification failed" toast.
|
||||||
|
- Verified wallet shows badge; unverified shows "Verify" prompt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration
|
||||||
|
|
||||||
|
Existing users with a saved TON wallet address will have `walletProofVerified` absent (undefined / falsy). They should be prompted to re-verify when they next visit the wallet page. No automatic migration needed — the address is retained, only proof status changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Status
|
||||||
|
|
||||||
|
### Backend — Complete (2026-05-24)
|
||||||
|
|
||||||
|
- `POST /api/user/wallet-address/ton-proof/challenge` implemented in `userController.ts`.
|
||||||
|
- Full ed25519 verification in `src/services/user/tonProofService.ts`:
|
||||||
|
- In-memory TTL challenge store (5-minute TTL per userId).
|
||||||
|
- Nonce cleared on success only (allows retry on failure).
|
||||||
|
- Verification: nonce match → expiry → domain → timestamp ±300s → stateInit hash matches address → extract pubkey → reconstruct signed message → `nacl.sign.detached.verify`.
|
||||||
|
- Public key extraction handles standard wallet v3/v4 data layout (skip 64 bits seqno+subwallet, read 32-byte pubkey).
|
||||||
|
- User model fields added: `profile.walletProofVerified`, `profile.walletProofTimestamp`.
|
||||||
|
- Route registered: `POST /api/user/wallet-address/ton-proof/challenge`.
|
||||||
|
- `@ton/ton@16.2.4`, `@ton/crypto@3.3.0`, `tweetnacl@1.0.3` added to backend `package.json`.
|
||||||
|
- 15 backend unit tests in `__tests__/ton-proof.test.ts` — all passing.
|
||||||
|
|
||||||
|
### Frontend — Pending
|
||||||
|
|
||||||
|
- Challenge fetch + `setConnectRequestParameters` + proof extraction not yet wired.
|
||||||
|
- Verified/unverified badge not yet rendered.
|
||||||
|
- EVM wallet stub card added (see below).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] `POST /api/user/wallet-address/ton-proof/challenge` returns a valid nonce object.
|
||||||
|
- [ ] `PATCH /api/user/wallet-address` with a real TonConnect proof from Telegram Wallet succeeds and sets `walletProofVerified: true`.
|
||||||
|
- [x] A replay of the same proof is rejected.
|
||||||
|
- [x] An expired nonce is rejected.
|
||||||
|
- [x] A proof with a mismatched domain is rejected.
|
||||||
|
- [ ] The wallet page shows "Verified" badge when `walletProofVerified = true`.
|
||||||
|
- [ ] The wallet page shows "Verify ownership" CTA when wallet is saved but unverified.
|
||||||
|
- [x] Backend tests cover all rejection cases with mocked proofs.
|
||||||
|
- [x] `@ton/ton` version is pinned in `package.json` (TON SDK breaks semver occasionally).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [[Handoff - Telegram Mini App Debug - 2026-05-24]]
|
||||||
|
- [[Security Audit - 2026-05-24]] — TON wallet is currently self-reported metadata
|
||||||
|
- [[Data Model Overview]]
|
||||||
|
- TON Connect docs: https://docs.ton.org/develop/dapps/ton-connect/sign
|
||||||
@@ -126,6 +126,148 @@ Backend:
|
|||||||
- EVM still requires signature verification.
|
- EVM still requires signature verification.
|
||||||
- TON currently validates address format only and skips EVM signature.
|
- TON currently validates address format only and skips EVM signature.
|
||||||
|
|
||||||
|
### Frontend Typecheck — All Pre-Existing Errors Resolved
|
||||||
|
|
||||||
|
All 13 pre-existing TypeScript errors in the frontend were fixed. `npx tsc --noEmit` now passes clean and can be used as a release gate.
|
||||||
|
|
||||||
|
Fixes applied:
|
||||||
|
|
||||||
|
- `src/web3/components/provider-payment.tsx` — changed invalid icon `solar:wallet-money-bold` to `solar:wallet-bold`.
|
||||||
|
- `src/components/payment/shkeeper-payment-widget.tsx` — moved `sellerId` from `metadata` to top-level payload (matches `IPaymentIntentPayload`).
|
||||||
|
- `src/sections/address/address-form.tsx` — added `as unknown as Resolver<AddressFormData>` double cast for Zod v4 + `@hookform/resolvers` v5 type mismatch.
|
||||||
|
- `src/sections/address/address-new-form.tsx` — same resolver cast fix.
|
||||||
|
- `src/sections/request-template/request-template-checkout-payment.tsx` — same resolver cast fix.
|
||||||
|
- `src/sections/request-template/request-template-new-edit-form.tsx` — added explicit `useForm<RequestTemplateSchemaType>` generic and resolver cast.
|
||||||
|
|
||||||
|
### Backend Tests for Email Verification and Wallet
|
||||||
|
|
||||||
|
Created `backend/__tests__/user-profile-email-wallet.test.ts` — 27 tests, all passing.
|
||||||
|
|
||||||
|
Coverage:
|
||||||
|
|
||||||
|
- Profile email update stores normalized email, sets `isEmailVerified=false`, returns `verificationEmailQueued`.
|
||||||
|
- Duplicate email rejected with 409.
|
||||||
|
- Resend verification: fails when no email, fails when already verified, succeeds otherwise.
|
||||||
|
- Verify email code: rejects malformed, rejects expired/wrong, sets `isEmailVerified=true` on correct code.
|
||||||
|
- TON wallet save: accepts valid friendly address, stores `walletType`, `walletAddress`, `walletProvider`.
|
||||||
|
- TON wallet save: rejects malformed address.
|
||||||
|
- EVM wallet save: rejects missing signature, rejects wrong signature.
|
||||||
|
|
||||||
|
`emailService` is mocked — no Resend calls are made during tests.
|
||||||
|
|
||||||
|
### Email Delivery Confirmed
|
||||||
|
|
||||||
|
Resend SMTP relay verified end-to-end. Configuration: port 465 SSL (`SMTP_SECURE=true`), sender `noreply@amn.gg`. Test email delivered successfully during session.
|
||||||
|
|
||||||
|
### Telegram Bot Command Handlers and Notification Foundation (Task 5.3)
|
||||||
|
|
||||||
|
Created `backend/src/services/telegram/botService.ts` (~480 lines).
|
||||||
|
|
||||||
|
Implemented:
|
||||||
|
|
||||||
|
- `sendBotMessage(chatId, text, options)` — raw Telegram Bot API call with inline keyboard support.
|
||||||
|
- `answerCallbackQuery(id, text?)` — acknowledges callback queries.
|
||||||
|
- `sendTelegramNotification(telegramUserId, text, options)` — looks up active `TelegramLink`, sends message, detects 403 (bot blocked by user) and marks link as `status: 'blocked'` to prevent future sends.
|
||||||
|
- `dispatchBotUpdate(update)` — routes incoming webhook updates to command handlers or callback handler.
|
||||||
|
- Command handlers: `/start` (welcome + Mini App button), `/help`, `/link`, `/status`, `/requests`, `/offers`, `/payments`, `/settings`.
|
||||||
|
- Per-chat rate limiter (10 messages per 10-second window) to prevent abuse.
|
||||||
|
- Mini App buttons use `web_app` inline keyboard type pointing to `https://amn.gg/telegram/`.
|
||||||
|
|
||||||
|
Modified `telegramService.ts`:
|
||||||
|
|
||||||
|
- Expanded `TelegramWebhookUpdate` type to include `message.from`, `message.chat`, `callback_query.message`.
|
||||||
|
- Wired `handleTelegramWebhook` to fire `dispatchBotUpdate` asynchronously (dynamic import) so Telegram always receives HTTP 200 immediately.
|
||||||
|
|
||||||
|
Updated `index.ts`:
|
||||||
|
|
||||||
|
- Exports: `sendTelegramNotification`, `sendBotMessage`, `NotificationDeliveryResult`, `BotSendOptions`, `InlineKeyboard`.
|
||||||
|
|
||||||
|
Created `backend/__tests__/telegram-bot-service.test.ts` — 17 tests, all passing.
|
||||||
|
|
||||||
|
Committed: "Implement Telegram bot command handlers and notification foundation (task 5.3)" — 4 files, 1092 insertions.
|
||||||
|
|
||||||
|
### Telegram Bot Commands Registered
|
||||||
|
|
||||||
|
Registered bot commands with Telegram Bot API (`setMyCommands`):
|
||||||
|
|
||||||
|
- `/start` — Open Amanat escrow marketplace
|
||||||
|
- `/help` — Show help and command list
|
||||||
|
- `/link` — Link your Amanat account
|
||||||
|
- `/status` — Check your active escrow status
|
||||||
|
- `/requests` — Browse service requests
|
||||||
|
- `/offers` — View your offers
|
||||||
|
- `/payments` — Check payment status
|
||||||
|
- `/settings` — Account settings
|
||||||
|
|
||||||
|
### PRD — TON Wallet Ownership Proof
|
||||||
|
|
||||||
|
Created `nick-doc/07 - Development/PRD - TON Wallet Ownership Proof.md`.
|
||||||
|
|
||||||
|
Covers: challenge nonce endpoint, TonConnect proof request flow, backend ed25519 verification with `@ton/ton` + `tweetnacl`, new User model fields, frontend badge/CTA, migration notes, acceptance criteria, and tests.
|
||||||
|
|
||||||
|
### TON Wallet Ownership Proof — Backend Implemented
|
||||||
|
|
||||||
|
Full backend implementation completed in session 3.
|
||||||
|
|
||||||
|
New file: `backend/src/services/user/tonProofService.ts`
|
||||||
|
|
||||||
|
- In-memory TTL challenge store keyed by `userId` (5-minute TTL).
|
||||||
|
- `generateChallenge(userId)` — generates 32-byte hex nonce, returns `{ payload, domain, timestamp }`.
|
||||||
|
- `verifyTonProof(userId, walletAddress, proof)` — full ed25519 verification:
|
||||||
|
1. Nonce match and expiry check.
|
||||||
|
2. Domain must equal `amn.gg`.
|
||||||
|
3. Timestamp within ±300 s of now.
|
||||||
|
4. `stateInit` required; its hash must match the claimed wallet address hash (anti-substitution).
|
||||||
|
5. Public key extracted from wallet data cell (skip 64-bit seqno+subwallet_id, read 32-byte pubkey — works for v3/v4/v5 standard wallets).
|
||||||
|
6. Signed message reconstructed: `ton-proof-item-v2/ + workChain (BE int32) + address.hash + domainLen (LE uint32) + domain + timestamp (LE int64) + payload (hex)`.
|
||||||
|
7. `sha256(0xff0xff + "ton-connect" + sha256(message))` — verified with `nacl.sign.detached.verify`.
|
||||||
|
8. Nonce cleared only on success (allows retry on failure within TTL).
|
||||||
|
|
||||||
|
Modified `backend/src/services/user/userController.ts`:
|
||||||
|
- `updateWalletAddress` extracts optional `tonProof` from body; calls `verifyTonProof` when present; sets `profile.walletProofVerified` and `profile.walletProofTimestamp`; returns `INVALID_TON_PROOF` on failure.
|
||||||
|
- New `getTonProofChallenge` handler calls `generateChallenge(userId)` and returns the nonce object.
|
||||||
|
|
||||||
|
Modified `backend/src/services/user/userControllerRoutes.ts`:
|
||||||
|
- `POST /api/user/wallet-address/ton-proof/challenge` registered (authenticated).
|
||||||
|
|
||||||
|
Modified `backend/src/models/User.ts`:
|
||||||
|
- Added `walletProofVerified?: boolean` and `walletProofTimestamp?: Date` to both IUser interface and Mongoose schema.
|
||||||
|
|
||||||
|
Added `@ton/ton@16.2.4`, `@ton/crypto@3.3.0`, `tweetnacl@1.0.3` to backend `package.json`.
|
||||||
|
|
||||||
|
New file: `backend/__tests__/ton-proof.test.ts` — 15 tests, all passing:
|
||||||
|
- Challenge generation (uniqueness, TTL invalidation).
|
||||||
|
- Valid proof accepted, nonce cleared (replay rejected).
|
||||||
|
- Rejection cases: no challenge, nonce mismatch, wrong domain, expired timestamp, missing stateInit, stateInit/address mismatch, bad signature, wrong keypair.
|
||||||
|
- Nonce preserved on failure (allows retry).
|
||||||
|
|
||||||
|
### Telegram Mini App Shell — LTR Fix and Smart Display Name
|
||||||
|
|
||||||
|
Modified `frontend/src/sections/telegram/telegram-mini-app-shell.tsx`:
|
||||||
|
|
||||||
|
- Added `dir="ltr"` to all three Container states (linked, unlinked, unsupported) so English content does not inherit the page-level RTL direction.
|
||||||
|
- Added smart `displayName` logic: falls back from Telegram name → auth user name → `"there"` if Telegram returns a generic or empty name (e.g., the literal string `"Telegram user"`).
|
||||||
|
|
||||||
|
### Socket Status — Suppressed Inside Telegram Mini App
|
||||||
|
|
||||||
|
Modified `frontend/src/socket/components/socket-status.tsx`:
|
||||||
|
|
||||||
|
- Added `usePathname` import; added path-guard `if (pathname?.startsWith('/telegram')) return null`.
|
||||||
|
- The "قطع" (disconnected) chip that briefly appeared on Mini App load was caused by normal Socket.IO connection latency (~0.5 s before handshake). Suppressing it on `/telegram` routes eliminates the false alarm without hiding useful status on other pages.
|
||||||
|
|
||||||
|
### EVM WalletConnect Stub Card
|
||||||
|
|
||||||
|
Telegram Wallet is TON-only. Request Network requires EVM. MetaMask is not available in Telegram; WalletConnect is the right approach but requires a project ID from `cloud.walletconnect.com`.
|
||||||
|
|
||||||
|
Added `EvmWalletStubCard` to `frontend/src/sections/account/account-wallet-connection.tsx`:
|
||||||
|
|
||||||
|
- Always rendered below the TON wallet card on the Account › Wallet page.
|
||||||
|
- Shows WalletConnect logo, "به زودی" (coming soon) chip, and explanation.
|
||||||
|
- Connect button is disabled when `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` is not set; tooltip tells developer what to configure.
|
||||||
|
- Button activates automatically when the env var is added — wire the wagmi `walletConnect()` connector (already commented out in `src/web3/config.ts`) at that point.
|
||||||
|
|
||||||
|
Rendered from `frontend/src/sections/account/view/account-wallet-view.tsx`.
|
||||||
|
|
||||||
## Verification Completed
|
## Verification Completed
|
||||||
|
|
||||||
Backend:
|
Backend:
|
||||||
@@ -166,60 +308,57 @@ Public smoke checks completed during the session:
|
|||||||
|
|
||||||
## Known Issues Left
|
## Known Issues Left
|
||||||
|
|
||||||
### Frontend Typecheck Still Has Existing Failures
|
### TON Wallet Ownership Proof — Frontend Wiring Pending
|
||||||
|
|
||||||
`frontend npx tsc --noEmit -p tsconfig.json` still fails in pre-existing areas not introduced by this Telegram/debug pass:
|
Backend is fully implemented and tested (see "Implemented" section above, and PRD `07 - Development/PRD - TON Wallet Ownership Proof.md` — status: `backend-implemented`).
|
||||||
|
|
||||||
- `src/components/payment/shkeeper-payment-widget.tsx`
|
Remaining frontend work:
|
||||||
- `src/sections/address/address-form.tsx`
|
|
||||||
- `src/sections/address/address-new-form.tsx`
|
|
||||||
- `src/sections/request-template/request-template-checkout-payment.tsx`
|
|
||||||
- `src/sections/request-template/request-template-new-edit-form.tsx`
|
|
||||||
- `src/web3/components/provider-payment.tsx`
|
|
||||||
|
|
||||||
Representative issues:
|
1. Add `getTonProofChallenge` action in `src/actions/account.ts` (calls `POST /api/user/wallet-address/ton-proof/challenge`).
|
||||||
|
2. In `handleConnectTelegramWallet` in `account-wallet-connection.tsx`:
|
||||||
|
- Fetch challenge nonce before opening modal.
|
||||||
|
- Call `tonConnectUI.setConnectRequestParameters({ state: 'ready', value: { tonProof: payload } })`.
|
||||||
|
- After connection, read `tonWallet.connectItems?.tonProof?.proof` and send to `PATCH /api/user/wallet-address` with `tonProof` field.
|
||||||
|
3. Show "Verified ✅" badge when `walletProofVerified: true`; show "Verify ownership" CTA when wallet is saved but unverified.
|
||||||
|
|
||||||
- Missing `sellerId` in `IPaymentIntentPayload`.
|
Until the frontend proof flow is wired, the backend accepts wallet saves without proof (legacy path — `walletProofVerified` will be absent/false). TON wallet addresses saved without proof must not be used for automatic payout routing.
|
||||||
- React Hook Form resolver typing mismatches.
|
|
||||||
- Invalid icon name `solar:wallet-money-bold`.
|
|
||||||
|
|
||||||
Next agent should decide whether to clean these before treating global frontend typecheck as a release gate.
|
### EVM Wallet (WalletConnect) Not Yet Functional
|
||||||
|
|
||||||
### Backend Broad Test Pattern Is Noisy
|
A stub card is now rendered on the wallet page with a disabled connect button. To activate:
|
||||||
|
|
||||||
A broad backend Jest pattern for `user|auth|telegram` pulled in unrelated payment/shkeeper/basic suites and failed with pre-existing app/test setup problems such as supertest app undefined and expected status mismatch.
|
1. Register at `cloud.walletconnect.com` (free), get a project ID.
|
||||||
|
2. Add `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=<id>` to `.env.local` (and server env).
|
||||||
|
3. Uncomment the `walletConnect()` connector in `src/web3/config.ts`.
|
||||||
|
4. Wire the connect handler in `EvmWalletStubCard` to open the WalletConnect modal.
|
||||||
|
|
||||||
Focused backend typecheck passed. The next agent should add targeted backend tests for:
|
### Profile Update UX Needs Manual Telegram Test
|
||||||
|
|
||||||
- `PUT /api/user/profile` email update.
|
The backend email verification endpoints are implemented and covered by tests. The UI flow in the actual Telegram Mini App has not been end-to-end verified manually:
|
||||||
- duplicate email rejection.
|
|
||||||
- resend profile email verification.
|
|
||||||
- verify profile email code.
|
|
||||||
- TON wallet save.
|
|
||||||
- EVM wallet signature requirement still enforced.
|
|
||||||
|
|
||||||
### TON Wallet Signature Model Is Not Final
|
- Start with a Telegram-created buyer without email.
|
||||||
|
- Add email in profile.
|
||||||
|
- Confirm warning/verification controls appear.
|
||||||
|
- Confirm Resend works.
|
||||||
|
- Enter verification code.
|
||||||
|
- Confirm session refresh shows verified state.
|
||||||
|
|
||||||
TON wallet save currently validates address shape only.
|
### Pangolin/Newt Routing Not Re-Verified
|
||||||
|
|
||||||
This is acceptable as a first Telegram Wallet integration smoke path, but not sufficient as final wallet ownership proof.
|
The desired public shape is one URL:
|
||||||
|
|
||||||
Next steps:
|
- Browser and Telegram app: `https://amn.gg`
|
||||||
|
- Backend proxied by path under the same host.
|
||||||
|
|
||||||
- Use TonConnect proof payload/signature validation server-side.
|
Expected route intent:
|
||||||
- Store wallet proof timestamp/provider.
|
|
||||||
- Decide whether Telegram Wallet should be required for payments or only optional profile wallet metadata.
|
|
||||||
|
|
||||||
### Email Delivery Needs Runtime Confirmation
|
- `/*` → frontend container/service
|
||||||
|
- `/api/*` → backend container/service
|
||||||
|
- `/socket.io/*` → backend container/service with WebSocket support
|
||||||
|
- `/uploads/*` → backend container/service
|
||||||
|
- `/health` → backend container/service
|
||||||
|
|
||||||
Backend is wired to call `emailService.sendVerificationCodeEmail`, and Resend support was configured earlier in the session, but the live flow should still be verified end-to-end:
|
If Telegram shows network errors, open `https://amn.gg/?debug=1` and check the debug panel API/Socket fields.
|
||||||
|
|
||||||
- Add an email from the Telegram-created profile.
|
|
||||||
- Confirm the API returns `verificationEmailQueued: true`.
|
|
||||||
- Confirm the message arrives from the configured `@amn.gg` sender.
|
|
||||||
- Submit code and confirm `isEmailVerified=true`.
|
|
||||||
|
|
||||||
Do not put API keys or bot tokens in docs or commits.
|
|
||||||
|
|
||||||
### Package Manager State
|
### Package Manager State
|
||||||
|
|
||||||
@@ -235,204 +374,132 @@ During dependency installation, the mounted `node_modules` was temporarily broke
|
|||||||
|
|
||||||
If the container shows stale Next errors in logs, check for a later successful startup before treating them as current.
|
If the container shows stale Next errors in logs, check for a later successful startup before treating them as current.
|
||||||
|
|
||||||
## Current Git State At Handoff
|
## Current Git State At Handoff (Updated 2026-05-24 Session 3)
|
||||||
|
|
||||||
Backend and frontend contain uncommitted changes for this Telegram/debug/email/wallet pass. They have not been committed yet.
|
### Backend — Session 3 Changes (Uncommitted)
|
||||||
|
|
||||||
Backend current modified files:
|
New files:
|
||||||
|
- `src/services/user/tonProofService.ts` — full TON proof verification service
|
||||||
|
- `__tests__/ton-proof.test.ts` — 15 unit tests (all passing)
|
||||||
|
|
||||||
- `src/models/User.ts`
|
Modified files:
|
||||||
- `src/services/user/userController.ts`
|
- `src/models/User.ts` — added `walletProofVerified`, `walletProofTimestamp` fields
|
||||||
- `src/services/user/userControllerRoutes.ts`
|
- `src/services/user/userController.ts` — TON proof challenge handler + proof verification in wallet save
|
||||||
|
- `src/services/user/userControllerRoutes.ts` — challenge endpoint registered
|
||||||
|
- `package.json` / `package-lock.json` — `@ton/ton`, `@ton/crypto`, `tweetnacl` added
|
||||||
|
|
||||||
|
Still uncommitted from earlier sessions:
|
||||||
- `src/services/user/userRoutes.ts`
|
- `src/services/user/userRoutes.ts`
|
||||||
|
- `__tests__/user-profile-email-wallet.test.ts` (27 tests)
|
||||||
|
|
||||||
Frontend current modified/untracked files:
|
### Frontend — Session 3 Changes (Uncommitted)
|
||||||
|
|
||||||
- `next.config.ts`
|
Modified:
|
||||||
- `package.json`
|
- `src/sections/account/account-wallet-connection.tsx` — added `EvmWalletStubCard` component
|
||||||
- `yarn.lock`
|
- `src/sections/account/view/account-wallet-view.tsx` — renders `EvmWalletStubCard`
|
||||||
- `public/tonconnect-manifest.json`
|
- `src/sections/telegram/telegram-mini-app-shell.tsx` — `dir="ltr"` on all containers, smart `displayName` fallback
|
||||||
- `src/actions/account.ts`
|
- `src/socket/components/socket-status.tsx` — suppressed on `/telegram` paths
|
||||||
- `src/app/layout.tsx`
|
|
||||||
- `src/auth/context/jwt/auth-provider.tsx`
|
|
||||||
- `src/auth/types.ts`
|
|
||||||
- `src/components/debug/telegram-debug-panel.tsx`
|
|
||||||
- `src/lib/axios.ts`
|
|
||||||
- `src/sections/account/account-general.tsx`
|
|
||||||
- `src/sections/account/account-wallet-connection.tsx`
|
|
||||||
- `src/types/user.ts`
|
|
||||||
- `src/web3/tonconnect-provider.tsx`
|
|
||||||
|
|
||||||
Vault also has older uncommitted audit/doc updates from prior task work, plus this handoff file.
|
Other files still uncommitted from earlier sessions (listed in Session 2 handoff).
|
||||||
|
|
||||||
Vault current modified/untracked files at handoff:
|
### Vault — Session 3 Changes (Uncommitted)
|
||||||
|
|
||||||
- `00 - Overview/System Overview.md`
|
- `07 - Development/PRD - TON Wallet Ownership Proof.md` — status updated to `backend-implemented`, acceptance criteria checked
|
||||||
- `00 - Overview/Tech Stack.md`
|
- `08 - Operations/Handoff - Telegram Mini App Debug - 2026-05-24.md` — this file
|
||||||
- `01 - Architecture/Backend Architecture.md`
|
|
||||||
- `01 - Architecture/Security Architecture.md`
|
|
||||||
- `02 - Data Models/Data Model Overview.md`
|
|
||||||
- `03 - API Reference/Authentication API.md`
|
|
||||||
- `04 - Flows/Registration Flow.md`
|
|
||||||
- `08 - Operations/Handoff - Telegram Mini App Debug - 2026-05-24.md`
|
|
||||||
- `09 - Audits/Audit Index - 2026-05-24.md`
|
|
||||||
- `09 - Audits/Logic Audit - 2026-05-24.md`
|
|
||||||
- `09 - Audits/Performance Audit - 2026-05-24.md`
|
|
||||||
- `09 - Audits/Security Audit - 2026-05-24.md`
|
|
||||||
|
|
||||||
Recommended commit split:
|
### Recommended Commit Split
|
||||||
|
|
||||||
1. Backend commit:
|
1. Backend commit A — user/email/wallet:
|
||||||
- user profile email verification endpoints
|
- `src/models/User.ts`
|
||||||
- wallet type/provider model fields
|
- `src/services/user/userController.ts`
|
||||||
- TON wallet address support
|
- `src/services/user/userControllerRoutes.ts`
|
||||||
|
- `src/services/user/userRoutes.ts`
|
||||||
|
- `__tests__/user-profile-email-wallet.test.ts`
|
||||||
|
|
||||||
2. Frontend commit:
|
2. Frontend commit:
|
||||||
- debug stats panel
|
- All frontend files above.
|
||||||
- TonConnect provider and manifest
|
|
||||||
- Telegram Wallet connect/save UI
|
|
||||||
- profile email verification UI/actions
|
|
||||||
- disabled Next dev indicator
|
|
||||||
|
|
||||||
3. Vault commit:
|
3. Vault commit:
|
||||||
- existing task/audit docs
|
- All vault files above.
|
||||||
- this handoff document
|
|
||||||
|
|
||||||
Before committing, rerun:
|
Before committing, rerun:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/manwe/CascadeProjects/escrow/backend
|
cd /Users/manwe/CascadeProjects/escrow/backend
|
||||||
git diff --check
|
|
||||||
npm run typecheck --if-present
|
npm run typecheck --if-present
|
||||||
|
npx jest --testPathPattern='user-profile-email-wallet|telegram-bot-service' --runInBand
|
||||||
|
|
||||||
cd /Users/manwe/CascadeProjects/escrow/frontend
|
cd /Users/manwe/CascadeProjects/escrow/frontend
|
||||||
git diff --check
|
npx tsc --noEmit -p tsconfig.json
|
||||||
npm test -- __tests__/account-test/account-actions.test.ts __tests__/auth/telegram-auth-action.test.ts __tests__/sections/telegram/telegram-mini-app-shell.test.tsx --runInBand
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then commit only the relevant files for each repository. Do not include secrets, local `.env` files, or regenerated `package-lock.json` churn.
|
Do not include secrets, local `.env` files, or regenerated `package-lock.json` churn.
|
||||||
|
|
||||||
## Suggested Next Steps
|
## Suggested Next Steps
|
||||||
|
|
||||||
1. Run the app in Telegram and open profile with `?debug=1` if needed.
|
1. Implement TON wallet ownership proof per `07 - Development/PRD - TON Wallet Ownership Proof.md`. This is the only remaining security-critical backend item.
|
||||||
2. Confirm debug panel socket state matches `https://amn.gg/socket.io/?EIO=4&transport=polling`.
|
2. Manually test profile email update + verification flow in the Telegram Mini App.
|
||||||
3. Test Telegram-created user account creation as buyer without email.
|
3. Recheck Pangolin/Newt routing and confirm Mini App loads via `https://amn.gg/telegram/`.
|
||||||
4. Add profile email, verify confirmation email delivery, and submit the code.
|
4. Build Telegram Mini App shell for marketplace workflows (Taskmaster task 5.4).
|
||||||
5. Connect Telegram Wallet and confirm backend stores `profile.walletType="ton"`.
|
5. Add frontend tests for `updateWalletAddress` and `TelegramDebugPanel`.
|
||||||
6. Add targeted backend Jest tests for the new endpoints.
|
6. Rebuild/redeploy frontend container after dependency changes (`@tonconnect/ui-react`, `@ton/core`).
|
||||||
7. Fix existing frontend typecheck blockers or document them as accepted debt.
|
7. Commit pending vault changes (PRD file, updated handoff).
|
||||||
8. Commit backend, frontend, and vault changes separately.
|
|
||||||
|
|
||||||
## Detailed Remaining Work
|
## Detailed Remaining Work
|
||||||
|
|
||||||
### 1. Confirm Telegram Bot Web App URL
|
### 1. ~~Confirm Telegram Bot Web App URL~~ ✓ Done
|
||||||
|
|
||||||
Make sure the Telegram bot opens the consolidated production URL:
|
Bot commands registered via `setMyCommands`. Mini App buttons in `/start` and `/help` use `web_app` type pointing to `https://amn.gg/telegram/`. No BotFather URL change was needed for command dispatch.
|
||||||
|
|
||||||
- Target URL: `https://amn.gg`
|
If the menu button URL still points to an old URL, update it via BotFather → Bot Settings → Menu Button.
|
||||||
- Bot username seen in the app: `@amnescrow_Bot`
|
|
||||||
- Mini App return URL configured for TonConnect: `https://t.me/amnescrow_Bot/escrow`
|
|
||||||
|
|
||||||
If the Telegram button still opens an old frontend URL, update the bot menu/button/web app URL through BotFather or the bot startup/config code, depending on how this bot is registered.
|
|
||||||
|
|
||||||
### 2. Recheck Pangolin/Newt Routing
|
### 2. Recheck Pangolin/Newt Routing
|
||||||
|
|
||||||
The desired public shape is one URL:
|
Not touched this session. See "Known Issues Left" section above for routing checklist.
|
||||||
|
|
||||||
- Browser and Telegram app: `https://amn.gg`
|
### 3. ~~Finish Email Verification Test Coverage~~ ✓ Done
|
||||||
- Backend proxied by path under the same host.
|
|
||||||
|
|
||||||
Expected route intent:
|
27 tests in `backend/__tests__/user-profile-email-wallet.test.ts` cover all email verification cases. See "Implemented" section above.
|
||||||
|
|
||||||
- `/*` -> frontend container/service
|
### 4. ~~Finish Wallet Test Coverage~~ ✓ Done (format-only)
|
||||||
- `/api/*` -> backend container/service
|
|
||||||
- `/socket.io/*` -> backend container/service with WebSocket support
|
|
||||||
- `/uploads/*` -> backend container/service
|
|
||||||
- `/health` -> backend container/service
|
|
||||||
|
|
||||||
If Telegram shows network errors:
|
Backend tests for TON format validation and EVM signature enforcement are in `user-profile-email-wallet.test.ts`.
|
||||||
|
|
||||||
1. Open `https://amn.gg/?debug=1`.
|
Ownership proof tests will be added when TonConnect proof verification is implemented (see item 5).
|
||||||
2. Check `API`, `Socket URL`, and `Socket` fields in the debug panel.
|
|
||||||
3. Hit `https://amn.gg/socket.io/?EIO=4&transport=polling` directly and confirm HTTP 200 with a Socket.IO polling handshake.
|
|
||||||
4. Check newt target health in Pangolin.
|
|
||||||
|
|
||||||
### 3. Finish Email Verification Test Coverage
|
Recommended frontend tests still pending:
|
||||||
|
|
||||||
The code path exists but targeted backend tests still need to be written.
|
|
||||||
|
|
||||||
Recommended backend tests:
|
|
||||||
|
|
||||||
- Profile email update stores normalized email and sets `isEmailVerified=false`.
|
|
||||||
- Profile email update rejects duplicate email.
|
|
||||||
- Profile email update queues an email verification code and calls email service.
|
|
||||||
- Resend verification fails when user has no email.
|
|
||||||
- Resend verification fails when email is already verified.
|
|
||||||
- Resend verification refreshes code/expires and sends email.
|
|
||||||
- Verify email rejects malformed code.
|
|
||||||
- Verify email rejects expired/wrong code.
|
|
||||||
- Verify email sets `isEmailVerified=true` and clears code fields.
|
|
||||||
|
|
||||||
Mock `emailService.sendVerificationCodeEmail` in these tests so Resend is not called.
|
|
||||||
|
|
||||||
### 4. Finish Wallet Test Coverage
|
|
||||||
|
|
||||||
Recommended backend tests:
|
|
||||||
|
|
||||||
- `PATCH /api/user/wallet-address` with `walletType="ton"` accepts a valid friendly TON address.
|
|
||||||
- TON wallet save stores `profile.walletAddress`, `profile.walletType`, and `profile.walletProvider`.
|
|
||||||
- TON wallet save rejects malformed TON addresses.
|
|
||||||
- EVM wallet save still rejects missing signature/message.
|
|
||||||
- EVM wallet save still rejects a signature that does not recover the submitted address.
|
|
||||||
- Legacy `/api/users/wallet-address` behavior remains compatible.
|
|
||||||
|
|
||||||
Recommended frontend tests:
|
|
||||||
|
|
||||||
- `updateWalletAddress` sends legacy EVM payload unchanged when no options are passed.
|
- `updateWalletAddress` sends legacy EVM payload unchanged when no options are passed.
|
||||||
- `updateWalletAddress` sends TON payload without signature when `{ walletType: "ton" }` is passed.
|
- `updateWalletAddress` sends TON payload without signature when `{ walletType: "ton" }` is passed.
|
||||||
- `TelegramDebugPanel` renders socket/auth/TG fields when debug is enabled.
|
- `TelegramDebugPanel` renders socket/auth/TG fields when debug is enabled.
|
||||||
- Account profile page renders email verification controls for an unverified email.
|
- Account profile page renders email verification controls for an unverified email.
|
||||||
|
|
||||||
### 5. Upgrade TON Ownership Proof
|
### 5. Implement TON Ownership Proof
|
||||||
|
|
||||||
Current TON wallet support is intentionally minimal: it validates address format and stores the address.
|
PRD written: `07 - Development/PRD - TON Wallet Ownership Proof.md`.
|
||||||
|
|
||||||
Before using TON wallet as strong proof of ownership, implement TonConnect proof verification:
|
Implementation not started. See PRD for full spec. Summary:
|
||||||
|
|
||||||
- Generate a backend nonce/payload for wallet proof.
|
- Add `yarn add @ton/ton @ton/crypto tweetnacl` to backend.
|
||||||
- Pass the proof request into TonConnect.
|
- Add `POST /api/user/wallet-address/ton-proof/challenge` (generates TTL nonce).
|
||||||
- Verify the returned proof server-side using TON address public key/proof data.
|
- Update `PATCH /api/user/wallet-address` to verify ed25519 proof when `tonProof` is present.
|
||||||
- Store proof metadata, timestamp, wallet provider, and verified status.
|
- Add `profile.walletProofVerified` and `profile.walletProofTimestamp` to User model.
|
||||||
- Expire or rotate proof when needed.
|
- Frontend: request proof via `tonConnectUI.connectWallet({ tonProof: payload })`, send proof to backend.
|
||||||
|
- Frontend: show "Verified" badge or "Verify ownership" CTA.
|
||||||
|
|
||||||
Until this is done, treat saved TON wallet as user-supplied wallet metadata, not a cryptographic ownership guarantee.
|
### 6. Check Profile Update UX in Telegram
|
||||||
|
|
||||||
### 6. Check Profile Update UX
|
Backend and tests done. Manual Telegram Mini App test not yet done:
|
||||||
|
|
||||||
The backend now supports email changes and confirmation codes, but the profile UI should be manually checked in Telegram:
|
|
||||||
|
|
||||||
- Start with a Telegram-created buyer without email.
|
- Start with a Telegram-created buyer without email.
|
||||||
- Add email in profile.
|
- Add email in profile, confirm page does not discard it.
|
||||||
- Confirm the page does not discard the email.
|
|
||||||
- Confirm warning/verification controls appear.
|
- Confirm warning/verification controls appear.
|
||||||
- Confirm Resend works.
|
- Confirm Resend works and email arrives from `noreply@amn.gg`.
|
||||||
- Enter verification code.
|
- Enter verification code, confirm `isEmailVerified=true`.
|
||||||
- Confirm user session refresh shows verified state.
|
|
||||||
|
|
||||||
If the UI toast says success but no email arrives, inspect backend email logs and Resend domain/sender configuration.
|
### 7. ~~Resolve Existing Typecheck Debt~~ ✓ Done
|
||||||
|
|
||||||
### 7. Resolve Existing Typecheck Debt
|
All 13 pre-existing frontend TypeScript errors fixed. `npx tsc --noEmit` passes clean. Add it to the frontend verification gate.
|
||||||
|
|
||||||
Do not assume the global frontend typecheck failure is caused by this pass. It was already failing in other modules.
|
|
||||||
|
|
||||||
Known blockers to resolve or explicitly defer:
|
|
||||||
|
|
||||||
- `shkeeper-payment-widget.tsx`: payload type expects `sellerId`.
|
|
||||||
- address form files: React Hook Form resolver type mismatches.
|
|
||||||
- request template form files: resolver/data typing issues.
|
|
||||||
- `provider-payment.tsx`: invalid icon id `solar:wallet-money-bold`.
|
|
||||||
|
|
||||||
After resolving these, make `npx tsc --noEmit -p tsconfig.json` part of the normal frontend verification gate.
|
|
||||||
|
|
||||||
### 8. Deployment/Container Follow-Up
|
### 8. Deployment/Container Follow-Up
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user