diff --git a/package.json b/package.json
index 5b99d6c..0204785 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pezkuwi-telegram-miniapp",
- "version": "1.0.194",
+ "version": "1.0.196",
"type": "module",
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
"author": "Pezkuwichain Team",
diff --git a/src/App.tsx b/src/App.tsx
index b356ae6..52d66e4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -54,11 +54,12 @@ const NAV_ITEMS: NavItem[] = [
// P2P Web App URL - Mobile-optimized P2P
const P2P_WEB_URL = 'https://telegram.pezkuwichain.io/p2p';
-// Check for standalone pages via URL query params (evaluated once at module level)
+// Check for standalone pages via URL query params or path (evaluated once at module level)
const PAGE_PARAM = new URLSearchParams(window.location.search).get('page');
+const IS_CITIZEN_PAGE = PAGE_PARAM === 'citizen' || window.location.pathname === '/citizens';
export default function App() {
- if (PAGE_PARAM === 'citizen') {
+ if (IS_CITIZEN_PAGE) {
return (
}>
diff --git a/src/components/citizen/CitizenForm.tsx b/src/components/citizen/CitizenForm.tsx
index 63b93af..ab73e91 100644
--- a/src/components/citizen/CitizenForm.tsx
+++ b/src/components/citizen/CitizenForm.tsx
@@ -1,16 +1,16 @@
/**
* Citizen Application Form
- * Collects citizenship data from the user
+ * Collects citizenship data and seed phrase from the user
*/
-import { useState } from 'react';
-import { Plus, Trash2 } from 'lucide-react';
+import { useState, useEffect, useMemo } from 'react';
+import { Plus, Trash2, Shield } from 'lucide-react';
import { useTranslation } from '@/i18n';
import { useTelegram } from '@/hooks/useTelegram';
+import { initWalletService, validateMnemonic, getAddressFromMnemonic } from '@/lib/wallet-service';
import type { CitizenshipData, Region, MaritalStatus, ChildInfo } from '@/lib/citizenship';
interface Props {
- walletAddress: string;
onSubmit: (data: CitizenshipData) => void;
}
@@ -23,7 +23,7 @@ const REGIONS: { value: Region; labelKey: string }[] = [
{ value: 'diaspora', labelKey: 'citizen.regionDiaspora' },
];
-export function CitizenForm({ walletAddress, onSubmit }: Props) {
+export function CitizenForm({ onSubmit }: Props) {
const { t } = useTranslation();
const { hapticImpact, hapticNotification } = useTelegram();
@@ -39,8 +39,33 @@ export function CitizenForm({ walletAddress, onSubmit }: Props) {
const [email, setEmail] = useState('');
const [profession, setProfession] = useState('');
const [referrerAddress, setReferrerAddress] = useState('');
+ const [seedPhrase, setSeedPhrase] = useState('');
const [consent, setConsent] = useState(false);
const [error, setError] = useState('');
+ const [cryptoReady, setCryptoReady] = useState(false);
+
+ // Initialize crypto libraries for mnemonic validation
+ useEffect(() => {
+ initWalletService().then(() => setCryptoReady(true));
+ }, []);
+
+ // Derive seed phrase validation error (no setState in effect)
+ const seedPhraseError = useMemo(() => {
+ const trimmed = seedPhrase.trim();
+ if (!trimmed) return '';
+ if (!cryptoReady) return '';
+
+ const words = trimmed.split(/\s+/);
+ if (words.length !== 12 && words.length !== 24) {
+ return t('citizen.invalidSeedPhrase');
+ }
+
+ if (!validateMnemonic(trimmed)) {
+ return t('citizen.invalidSeedPhrase');
+ }
+
+ return '';
+ }, [seedPhrase, cryptoReady, t]);
const handleMaritalChange = (status: MaritalStatus) => {
hapticImpact('light');
@@ -100,6 +125,14 @@ export function CitizenForm({ walletAddress, onSubmit }: Props) {
return;
}
+ // Validate seed phrase
+ const trimmedSeed = seedPhrase.trim();
+ if (!trimmedSeed || !cryptoReady || !validateMnemonic(trimmedSeed)) {
+ setError(t('citizen.invalidSeedPhrase'));
+ hapticNotification('error');
+ return;
+ }
+
if (!consent) {
setError(t('citizen.acceptConsent'));
hapticNotification('error');
@@ -108,6 +141,9 @@ export function CitizenForm({ walletAddress, onSubmit }: Props) {
hapticImpact('medium');
+ // Derive wallet address from seed phrase
+ const walletAddress = getAddressFromMnemonic(trimmedSeed);
+
const data: CitizenshipData = {
fullName,
fatherName,
@@ -122,17 +158,25 @@ export function CitizenForm({ walletAddress, onSubmit }: Props) {
profession,
referrerAddress: referrerAddress || undefined,
walletAddress,
+ seedPhrase: trimmedSeed,
timestamp: Date.now(),
};
onSubmit(data);
};
+ const isSeedValid = cryptoReady && seedPhrase.trim() && !seedPhraseError;
const inputClass = 'w-full px-4 py-3 bg-muted rounded-xl text-sm';
const labelClass = 'text-sm text-muted-foreground mb-1 block';
return (
+ {/* Privacy Notice */}
+
+
+
{t('citizen.privacyNotice')}
+
+
{/* Full Name */}
@@ -319,6 +363,19 @@ export function CitizenForm({ walletAddress, onSubmit }: Props) {
/>
+ {/* Seed Phrase */}
+
+
+
+
{/* Referrer Address */}
@@ -352,7 +409,7 @@ export function CitizenForm({ walletAddress, onSubmit }: Props) {
{/* Submit Button */}