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:
Siavash Sameni
2026-05-24 20:21:29 +04:00
parent 940ad0c655
commit 873a57874e
3 changed files with 561 additions and 183 deletions

View File

@@ -513,7 +513,7 @@
"title": "Implement bot command and notification foundation",
"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.",
"status": "in-progress",
"status": "done",
"dependencies": [
1,
2
@@ -521,7 +521,7 @@
"priority": "high",
"testStrategy": "See Telegram-native PRD acceptance criteria.",
"parentId": "undefined",
"updatedAt": "2026-05-24T09:18:15.004Z"
"updatedAt": "2026-05-24T13:46:14.458Z"
},
{
"id": 4,
@@ -634,12 +634,12 @@
"updatedAt": "2026-05-24T11:59:32.372Z"
}
],
"updatedAt": "2026-05-24T11:59:32.372Z"
"updatedAt": "2026-05-24T13:46:14.458Z"
}
],
"metadata": {
"version": "1.0.0",
"lastModified": "2026-05-24T11:59:32.372Z",
"lastModified": "2026-05-24T13:46:14.458Z",
"taskCount": 5,
"completedCount": 4,
"tags": [
@@ -647,4 +647,4 @@
]
}
}
}
}

View 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

View File

@@ -126,6 +126,148 @@ Backend:
- EVM still requires signature verification.
- 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
Backend:
@@ -166,60 +308,57 @@ Public smoke checks completed during the session:
## 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`
- `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`
Remaining frontend work:
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`.
- React Hook Form resolver typing mismatches.
- Invalid icon name `solar:wallet-money-bold`.
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.
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.
- duplicate email rejection.
- resend profile email verification.
- verify profile email code.
- TON wallet save.
- EVM wallet signature requirement still enforced.
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:
### 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.
- Store wallet proof timestamp/provider.
- Decide whether Telegram Wallet should be required for payments or only optional profile wallet metadata.
Expected route intent:
### 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:
- 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.
If Telegram shows network errors, open `https://amn.gg/?debug=1` and check the debug panel API/Socket fields.
### 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.
## 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`
- `src/services/user/userController.ts`
- `src/services/user/userControllerRoutes.ts`
Modified files:
- `src/models/User.ts` — added `walletProofVerified`, `walletProofTimestamp` fields
- `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`
- `__tests__/user-profile-email-wallet.test.ts` (27 tests)
Frontend current modified/untracked files:
### Frontend — Session 3 Changes (Uncommitted)
- `next.config.ts`
- `package.json`
- `yarn.lock`
- `public/tonconnect-manifest.json`
- `src/actions/account.ts`
- `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`
Modified:
- `src/sections/account/account-wallet-connection.tsx` — added `EvmWalletStubCard` component
- `src/sections/account/view/account-wallet-view.tsx` — renders `EvmWalletStubCard`
- `src/sections/telegram/telegram-mini-app-shell.tsx``dir="ltr"` on all containers, smart `displayName` fallback
- `src/socket/components/socket-status.tsx` — suppressed on `/telegram` paths
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`
- `00 - Overview/Tech Stack.md`
- `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`
- `07 - Development/PRD - TON Wallet Ownership Proof.md` — status updated to `backend-implemented`, acceptance criteria checked
- `08 - Operations/Handoff - Telegram Mini App Debug - 2026-05-24.md` — this file
Recommended commit split:
### Recommended Commit Split
1. Backend commit:
- user profile email verification endpoints
- wallet type/provider model fields
- TON wallet address support
1. Backend commit A — user/email/wallet:
- `src/models/User.ts`
- `src/services/user/userController.ts`
- `src/services/user/userControllerRoutes.ts`
- `src/services/user/userRoutes.ts`
- `__tests__/user-profile-email-wallet.test.ts`
2. Frontend commit:
- debug stats panel
- TonConnect provider and manifest
- Telegram Wallet connect/save UI
- profile email verification UI/actions
- disabled Next dev indicator
- All frontend files above.
3. Vault commit:
- existing task/audit docs
- this handoff document
- All vault files above.
Before committing, rerun:
```bash
cd /Users/manwe/CascadeProjects/escrow/backend
git diff --check
npm run typecheck --if-present
npx jest --testPathPattern='user-profile-email-wallet|telegram-bot-service' --runInBand
cd /Users/manwe/CascadeProjects/escrow/frontend
git diff --check
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
npx tsc --noEmit -p tsconfig.json
```
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
1. Run the app in Telegram and open profile with `?debug=1` if needed.
2. Confirm debug panel socket state matches `https://amn.gg/socket.io/?EIO=4&transport=polling`.
3. Test Telegram-created user account creation as buyer without email.
4. Add profile email, verify confirmation email delivery, and submit the code.
5. Connect Telegram Wallet and confirm backend stores `profile.walletType="ton"`.
6. Add targeted backend Jest tests for the new endpoints.
7. Fix existing frontend typecheck blockers or document them as accepted debt.
8. Commit backend, frontend, and vault changes separately.
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. Manually test profile email update + verification flow in the Telegram Mini App.
3. Recheck Pangolin/Newt routing and confirm Mini App loads via `https://amn.gg/telegram/`.
4. Build Telegram Mini App shell for marketplace workflows (Taskmaster task 5.4).
5. Add frontend tests for `updateWalletAddress` and `TelegramDebugPanel`.
6. Rebuild/redeploy frontend container after dependency changes (`@tonconnect/ui-react`, `@ton/core`).
7. Commit pending vault changes (PRD file, updated handoff).
## 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`
- 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.
If the menu button URL still points to an old URL, update it via BotFather → Bot Settings → Menu Button.
### 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`
- Backend proxied by path under the same host.
### 3. ~~Finish Email Verification Test Coverage~~ ✓ Done
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
- `/api/*` -> backend container/service
- `/socket.io/*` -> backend container/service with WebSocket support
- `/uploads/*` -> backend container/service
- `/health` -> backend container/service
### 4. ~~Finish Wallet Test Coverage~~ ✓ Done (format-only)
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`.
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.
Ownership proof tests will be added when TonConnect proof verification is implemented (see item 5).
### 3. Finish Email Verification Test Coverage
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:
Recommended frontend tests still pending:
- `updateWalletAddress` sends legacy EVM payload unchanged when no options are passed.
- `updateWalletAddress` sends TON payload without signature when `{ walletType: "ton" }` is passed.
- `TelegramDebugPanel` renders socket/auth/TG fields when debug is enabled.
- 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.
- Pass the proof request into TonConnect.
- Verify the returned proof server-side using TON address public key/proof data.
- Store proof metadata, timestamp, wallet provider, and verified status.
- Expire or rotate proof when needed.
- Add `yarn add @ton/ton @ton/crypto tweetnacl` to backend.
- Add `POST /api/user/wallet-address/ton-proof/challenge` (generates TTL nonce).
- Update `PATCH /api/user/wallet-address` to verify ed25519 proof when `tonProof` is present.
- Add `profile.walletProofVerified` and `profile.walletProofTimestamp` to User model.
- 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
The backend now supports email changes and confirmation codes, but the profile UI should be manually checked in Telegram:
Backend and tests done. Manual Telegram Mini App test not yet done:
- Start with a Telegram-created buyer without email.
- Add email in profile.
- Confirm the page does not discard the email.
- Add email in profile, confirm page does not discard it.
- Confirm warning/verification controls appear.
- Confirm Resend works.
- Enter verification code.
- Confirm user session refresh shows verified state.
- Confirm Resend works and email arrives from `noreply@amn.gg`.
- Enter verification code, confirm `isEmailVerified=true`.
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
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.
All 13 pre-existing frontend TypeScript errors fixed. `npx tsc --noEmit` passes clean. Add it to the frontend verification gate.
### 8. Deployment/Container Follow-Up