Commit Graph
317 Commits
Author SHA1 Message Date
SatoshiQaziMuhammed a20ea4584b Replace the Events tile with KurdAI and point Loto at games.pex.mom (#57)
* Replace the Events tile with KurdAI, opening kurdishtts.dks.news

The Events tile on the mobile home, in the side menu and on the desktop landing
page opened kurdishtts.pezkiwi.app, which no longer answers. The service it
pointed to lives at kurdishtts.dks.news and is the Kurdish voice tool, not an
events page, so the tile is renamed KurdAI in every language, links there, and
gets a microphone icon. The calendar icon it used had no other user and is
removed.

* Point the Loto tile at games.pex.mom

loto.pex.mom now redirects there; linking the new address directly saves the
hop.
2026-10-18 07:45:26 +04:00
SatoshiQaziMuhammed 74db90a5d4 Send the Rewshenbir button to rewshenbir.pezkiwi.app (#38)
* Send the Rewshenbir button to rewshenbir.pezkiwi.app

The app grid, the mobile home and the landing page's pallet list all linked
https://rewshenbir.pezkuwi.app -- pezkuwi, not pezkiwi. That name does not
resolve, so every reader who tapped Rewshenbir reached nothing. The sibling
links (bereketli, buy-sell, kurdishtts) already use pezkiwi.app.

* Keep the source of the interim Rewshenbir landing page in the repository

rewshenbir.pezkiwi.app now answers with a page built from the design canvas
(tokens, thesis, the RewshenbirMark logo) instead of nothing. It lives in the
repository so it is not a file that exists only on the host.
2026-10-01 15:53:08 +04:00
SatoshiQaziMuhammed 772cb72c73 Serve the whitepaper from one place, and stop cloning what cannot be cloned
Three changes, all pointing the same way: one published document, fed from one source.

`web/public/Whitepaper.pdf` was a second copy of a document that also lives on
pezkuwichain.io, in the landing tree, on three servers and in the repository the
document describes. The copies had diverged; the one bundled here was built in June and
still named a licence the project does not use and a pallet the tree does not contain.
A copy in a bundle is the hardest kind to keep current, because nothing fails when it
goes stale. It is removed, and the documentation card now links to the canonical URL, so
this application shows whatever is published rather than whatever was bundled. The
deployed hosts redirect `/Whitepaper.pdf` to the same place, so existing links keep
working.

The web build also began by cloning `pezkuwi-sdk`, first from the self-hosted GitLab and
then from GitHub. Neither is reachable: the GitLab path answers 500 to both the browser
and `git ls-remote`, and the GitHub repository is disabled. Nothing here changed -- the
workflow had not run since the second of August and the sources rotted underneath it.
The clone fed `generate-docs-structure.cjs`, which already treats the SDK as optional
and completes without it, and whose output is not served: the in-app documentation route
redirects to docs.pezkuwichain.io, which is why `.gitignore` leaves those artifacts
untracked. Swapping in another URL would not have helped either, since the run is
unauthenticated and the surviving repository is private. The step is removed.

Finally, `npm audit` reported five advisories against production dependencies, one high.
None arrived with this branch; a month of them surfaced at once on the first run.
`npm audit fix` without `--force` clears the high one and leaves two moderate, below what
the gate measures, and moves only `package-lock.json` -- no declared range changes.
`--force` was not used: it has broken this workspace's Solana dependencies before.

Verified: full pipeline green -- Web App, Backend Indexer, Migration test, Security
Audit, CodeQL and the deploy-gate wiring check.
2026-09-04 03:25:35 +04:00
SatoshiQaziMuhammed 338472629d fix(auth): put the captcha challenge where the user is looking (#33)
Driving the real login page in a browser showed what shipping this actually
looks like when Cloudflare decides a visitor must click: the submit button sits
on 'Signing in...' while an unnoticed 'Verify you are human' checkbox waits in
the bottom-right corner. Nothing tells the user it is there. They conclude login
is broken, and they are not wrong.

Two changes:

- The challenge is centred over a dimmed backdrop while it is visible, and
  returns to the corner once answered. Detected by measuring the container
  rather than by Turnstile's interactive callbacks: those callbacks are not in
  the documentation this was written against, and a callback name that turns out
  to be wrong fails silently — leaving the challenge in the corner, which is the
  bug being fixed.

- A 90 second cap on waiting for a token. Without it, a challenge nobody answers
  leaves the button spinning forever with no error and no way back. Every caller
  already surfaces a rejection to the user.

The ResizeObserver fallback polls instead of returning a no-op. Doing nothing
there would restore the original bug on those browsers and do it without an
error, which is the worst way for a fix to be missing.
2026-08-01 06:16:44 -07:00
SatoshiQaziMuhammed c26eeee12c feat(auth): require a captcha on the endpoints that send mail (#32)
Signup had no bot protection. The account itself was never the prize — GoTrue
refuses a session until the address is confirmed, and 0 of the 2 unconfirmed
accounts on record ever signed in. The prize is our mail: /signup and /recover
send to whatever address the caller types, so a stranger could make
pezkuwichain.io mail thousands of people who never asked for it. Sending
reputation takes months to rebuild and every product on the domain shares it.

Rate limiting alone was not enough. GoTrue defaults to 30 emails/hour, which is
720 a day to attacker-chosen recipients; tightening it to 15 halves the number
without changing what is possible.

Verification runs inside GoTrue, not here. It POSTs to siteverify with the
widget secret before /signup, /recover, /resend, /magiclink, /otp and /token
with grant_type=password. Adding our own check in the frontend or a proxy would
protect nothing, because /auth/v1/signup is reachable directly.

One shared widget in execute mode rather than one per form: some of these are
buttons with nowhere to put a checkbox, and interaction-only appearance keeps
the challenge invisible unless Cloudflare asks for one. Tokens are single-use,
so each call renders fresh and tears down the previous widget.

Six call sites, matching the six protected endpoints — password login included,
since /token with grant_type=password is enforced. Refresh-token grants are
exempt, so open sessions are unaffected.

Site key travels as a build arg like the WalletConnect id: it is public by
construction, visible in the bundle. Only the secret is privileged and it lives
in GoTrue's env on the auth host.

Ships ahead of enforcement. Turning captcha on server-side before the frontend
sends tokens would break every login and signup in the same instant.
2026-08-01 03:45:51 -07:00
Satoshi Qazi Muhammed e8dd9f6c99 feat(auth): actually ask for 2FA at login
2FA could be enabled but was never enforced. TwoFactorSetup wrote a secret and
backup codes, and login never checked any of it — TwoFactorVerify existed as a
component referenced from nowhere. Turning 2FA on protected nothing.

Optional, as decided: a user without 2FA is never blocked, and a failure while
checking is treated as "not required" so a transient error cannot become a login
wall for people who never enabled it.

The challenge is enforced in ProtectedRoute rather than only after the password
form, so a deep link or a restored tab cannot walk straight past it. Telegram
sessions are exempt — they authenticate through signed initData, so there is no
password to second-factor.

Recovery is by backup code, which the verify endpoint already supported and
consumes on use. Cancelling the challenge signs the user out; leaving them signed
in but unchallenged would defeat the point of asking.

Verification is remembered in sessionStorage, not localStorage: 2FA is asked once
per browser session and again after the tab closes. Persisting it would mean
being challenged once per device, ever.

Scope, stated plainly rather than implied: this is a client-side gate. It stops
someone holding the password from reaching the app in a browser, which is what
2FA is for here. It is not server-side enforcement — privileged operations still
verify authority independently (wallet signature for admin actions, JWT for an
account's own data), and those remain what actually protects funds and state.
2026-07-31 22:02:40 -07:00
Satoshi Qazi Muhammed c0e1326c4b fix(security): remove the dead email-verification function
The function was never used and carried a real hole while sitting there.

`send` took an email address from the request body, did not check who was
calling, and returned the verification token in the response — with a
`// Remove in production` comment on that line. Anyone could mint a token for
another account's address and immediately use it. `Access-Control-Allow-Origin`
was `*`.

It was never reachable in practice: nothing calls `send`, so no token was ever
created, so `verify` could never succeed. The database confirms it —
`email_verification_tokens` has zero rows, ever.

Real email verification runs through Supabase Auth, not this function.
EmailVerification.tsx handles `type=signup` / `email_change` via the URL hash and
`getSession()`, and Dashboard.tsx resends through `auth.resend()`. Those paths
are untouched. The `?token=` branch that called this function was dead code
fed by a token nothing produced, so it goes with it.

Removing it also took the `verifying` state with it: `setVerifying` had no
remaining callers, so the spinner could never render and every `!verifying`
guard was permanently true — conditions that read as if the value could vary.

Fixing this properly instead of deleting it would mean building the email
delivery the function never had ("In production, send email via email service"),
duplicating what Supabase Auth already does correctly.
2026-07-31 11:05:01 -07:00
Satoshi Qazi Muhammed 08fd9d2b34 i18n(ar,ckb,fa,kmr): complete multisig operations and bridge withdrawal copy
Brings the remaining four locales up to the same coverage as en and tr: the 14
multisigOps strings, wallet.multisigOperations, and the corrected withdrawal
wording.

The withdrawal strings were not merely missing here. bridge.withdrawAddress
existed in all four and still described the field as a bank account or crypto
address the user types in, and withdrawSuccess still said USDT would be sent
after a delay. Neither is true: the destination is derived from the connected
account, and burned wUSDT is only released once the multisig signers approve.
Because the keys existed, fallbackLng never applied and speakers of these four
languages were reading incorrect instructions about their own funds.

Terminology follows each file's existing vocabulary rather than inventing new
terms — kmr Îmze/Berîk, ckb واژوو/جزدان, ar توقيع/المحفظة, fa امضا/کیف پول.
The two Kurdish variants still want a native speaker's read before we treat
them as final.
2026-07-29 20:34:46 -07:00
Satoshi Qazi Muhammed cff5ae8032 fix(social): point KurdMedia links at the canonical accounts
t.me/pezkuwichain and twitter.com/pezkuwichain are both retired handles. The
canonical set is t.me/kurdishmedya for the channel and x.com/bizinikiwi for X.
2026-07-29 20:26:00 -07:00
Satoshi Qazi Muhammed c117b023b1 feat(wallet): surface multisig operations shortcut to bridge signatories
Adds the entry point for the operations screen the multisigOps strings were
written for. The button only renders when the connected account is one of the
five USDT bridge signatories; that check is for discoverability only, the route
stays gated by ProtectedRoute.
2026-07-29 20:26:00 -07:00
Satoshi Qazi Muhammed ee384064c1 i18n(en,tr): add multisig operations strings, correct bridge withdrawal copy
MultisigOperations.tsx and MultisigOperationsPage.tsx are already in the tree and
read multisigOps.* plus wallet.multisigOperations, but no locale defined them, so
the screen rendered raw key names.

The bridge withdrawal copy was also wrong about what happens: the destination is
derived from the connected account rather than typed in, and burned wUSDT is not
released until the multisig signers approve. Both strings now say that.

ar, ckb, fa and kmr still carry the old withdrawal wording. They are not left to
fall back to English because the key exists there — they need translations from a
native speaker, tracked separately.
2026-07-29 20:24:51 -07:00
Satoshi Qazi Muhammed 165cb47c64 security(p2p): close escrow-RPC anon-drain (release/lock behind signed edge functions)
Round 2 — fixes the CRITICAL discovered during round 1 (as severe as the withdrawal
BOLA): lock_/release_/refund_escrow_internal had default PUBLIC EXECUTE and the client
called release_escrow_internal directly with the anon key, so anyone could drain any
victim's LOCKED balance.

- release_escrow_internal now reachable only via new signed confirm-payment edge
  function: verifies seller wallet signature (raw + <Bytes>), asserts the wallet owns
  the seller identity, derived user_id == trade.seller_id, trade == payment_sent,
  single-use nonce, then release runs with the service role. confirmPaymentReceived
  now invokes confirm-payment (no more anon rpc).
- lock_escrow_internal now behind new signed lock-escrow edge function (a caller can
  only lock its own balance; stops griefing a victim's balance). Removed a zero-amount
  no-op lock call.
- Migration 20260725030000: REVOKE EXECUTE on lock_/release_/refund_escrow_internal
  from PUBLIC/anon/authenticated; GRANT to service_role only. refund has no direct
  client caller (service-role + admin_resolve_dispute only).
- DEPLOY_RUNBOOK.md consolidates the ordered migrations, edge functions and secrets
  for both security rounds. Migration 20260725030000 must ship WITH the two new edge
  functions + frontend or offer-create/payment-release break.

Remaining (non-fund, Round 2+ follow-up): read RPCs still key on a non-secret user_id
(binding to a signed session would force a sign-prompt on every passive balance read —
deferred); p2p_fiat_offers/trades/messages retain USING(true) (status labels move no
funds now that all escrow movement is service-role-gated).
2026-07-25 00:21:20 -07:00
Satoshi Qazi Muhammed a8f41cd47f security(p2p): fix withdrawal BOLA, financial RLS exposure, admin dispute escrow
Critical/high audit remediation on the custodial P2P ledger. Auth model is
wallet-based; identity (citizen/visa) is cryptographically bound to a wallet
(People Chain tiki.citizenNft / active p2p_visa), which enables a correct fix.

- Withdrawal BOLA (CRITICAL): process-withdraw now requires a wallet-SIGNED
  challenge; server verifies signature (raw + <Bytes> forms), asserts the signer
  OWNS the identity, consumes a single-use nonce (replay), and derives user_id
  server-side — the client-supplied user_id is ignored. process-withdrawal (batch)
  now requires the service-role key (was: any bearer, incl. public anon key).
  request_withdraw is REVOKEd from anon/authenticated + service-role guarded.
- Financial RLS (HIGH): drop the blanket USING(true) SELECT on user_internal_balances,
  p2p_balance_transactions, p2p_deposit_withdraw_requests; lock p2p_user_payment_methods
  (IBAN PII) + p2p_fiat_disputes UPDATE to service_role; legitimate reads move behind
  scoped SECURITY DEFINER RPCs.
- Deposit integrity: verify-deposit now binds the on-chain sender to identity ownership
  before crediting.
- Admin dispute (CRITICAL fund-logic): DisputeResolutionPanel relabeled trades without
  moving escrow. New admin-signed resolve-dispute function + admin_resolve_dispute RPC
  moves escrow (release/refund/split) atomically with correct accounting (avoids the
  double-count in the legacy resolve_p2p_dispute). Client isAdmin documented as cosmetic.

DEPLOY RUNBOOK (gated; owner runs): 1) apply migrations 20260225/20260725* in order;
2) deploy edge functions process-withdraw, process-withdrawal, verify-deposit, resolve-dispute;
3) set edge secrets PEOPLE_RPC_ENDPOINT (+ optional ADMIN_WALLETS); 4) ship frontend.
Migrations + functions + frontend must go together or the app breaks.

KNOWN RESIDUAL (Round 2 — as severe as the withdrawal BOLA): release_/lock_/refund_
escrow_internal still have PUBLIC EXECUTE and the client calls release_escrow_internal
directly with the anon key from confirmPaymentReceived -> an anon caller can drain any
victim's LOCKED balance. Fix = a wallet-signed confirm-payment edge function (same
pattern as withdrawals) before revoking PUBLIC execute. Not yet fixed.
2026-07-25 00:09:03 -07:00
Satoshi Qazi Muhammed 27b4057bd4 chore(ci/build): typecheck gate, npm ci, backend CI, dep+vuln fixes, hygiene
Audit remediation (build/supply-chain/quality):
- Add `@pezkuwi/api-augment` import in main.tsx -> tsc errors 416->328 (Codec-typed
  on-chain reads now augmented); add `typecheck` script + a non-blocking Typecheck CI
  step (flip to blocking once errors reach 0).
- CI: `npm install` -> `npm ci` (lockfile enforced) in web + security-audit jobs; add a
  Backend Indexer job (npm ci + node --check + non-blocking high/critical audit). No
  live tests run in CI (they need a live chain) — a mockable CI test subset is a
  follow-up.
- Vulns: bump dompurify ^3.4.12 + postcss ^8.5.23 (web prod highs cleared; only the
  react-router v7 open-redirect remains, deferred as a breaking major); backend tar
  ^7.5.22 + ws ^8.21.1 (clears the critical tar advisory). Lockfiles regenerated.
- Prod build now drops console/debugger (vite esbuild.drop); NotificationBell realtime
  subscription now returns cleanup + uses a per-user channel (fixes leak); ProfileSettings
  language codes aligned to i18n config (ku-kurmanji/ku-sorani).
- Root package.json sdk-ui path env-driven (${SDK_UI_DIR}); generate-docs rustup path
  resolved dynamically; Docker image license label MIT (matches LICENSE). Removed scratch
  files (mimari.txt, _scratch_credit_serok.mjs).
2026-07-24 23:43:37 -07:00
Satoshi Qazi Muhammed 1c6ff3d578 fix(web): correct asset IDs/decimals + chain routing, string-BigInt amounts, presale signer
Fund-logic hardening (audit remediation):
- TransferModal: source asset id/decimals from the canonical MINTABLE_ASSETS
  (wUSDT=1000, wDOT=1001, wETH=1002, wBTC=1003) instead of the wrong hardcoded
  ids (BTC=3 was the deprecated "Old USDT", ETH=4, DOT=5). Route every non-native
  asset through the Asset Hub api; only native HEZ uses the relay balances pallet.
  Removes the latent wrong-asset / wrong-chain transfer.
- Replace BigInt(parseFloat(x)*10**dec) with string-based parseTokenInput in
  TransferModal + 3 DEX init modals; BigInt(float) threw RangeError on common
  fractional amounts (e.g. 100.3 HEZ), breaking sends and pool/bridge init.
- Presale.contribute now resolves a signer via getSigner and passes { signer }
  to signAndSend (was signer-less -> broken for all users).
- presale.ts contribute/refund/claimVested now reject on dropped/invalid/usurped/
  retracted tx states instead of hanging the UI forever.
2026-07-24 22:55:54 -07:00
Satoshi Qazi Muhammed 686eecc09e fix(web): repair 6 dead internal links + harden forum markdown against stored XSS
Routing: footer "Explorer"/"Validators" pointed to non-existent /network (4 spots
in AppLayout + LandingPageDesktop) and Explorer/Forum quick-links pointed to bare
/governance (no route) -> all hit the 404 catch-all. Repointed to /explorer and
/governance/assembly; footer "Vote" /-> /elections.

Security (stored XSS): DiscussionThread.parseMarkdown did regex->HTML then
dangerouslySetInnerHTML with no escaping/sanitizer, so <img onerror>/javascript:
links in user comments executed. Now escape HTML first, then DOMPurify-sanitize the
output to a safe tag/attr allow-list with http(s)-only hrefs.
2026-07-24 22:43:30 -07:00
Satoshi Qazi Muhammed dd58fe9164 Add multisig pending-operations UI, fix broken multisig address calc, new signing portal
- calculateMultisigAddress was completely broken (hex-decoded an SS58 string
  and never hashed the preimage) - fixed via @pezkuwi/util-crypto's real
  encodeMultiAddress/createKeyMulti, verified against the actual known
  multisig address on-chain.
- ReservesDashboardPage had stale Noter/Berdevk addresses that don't match
  the real signers - centralized as BRIDGE_MULTISIG_SPECIFIC_ADDRESSES.
- USDTBridge withdrawal called assets.burn directly as a single-signer
  extrinsic (always fails - only the multisig is Admin) while only
  checking status.isFinalized (a failed dispatch is still finalized, so it
  silently did nothing) - replaced with the correct transfer-to-custody
  flow the relayer actually watches for.
- New MultisigOperationsPage (/multisig/pending) lists pending calls from
  real Multisig.Multisigs storage and lets any of the 5 signers
  approve/reject with their own wallet extension.
- New standalone sign/ app (deployed separately at
  pezbridge-sign.pex.mom) - a dedicated, gated signing portal for the same
  operations, so signing isn't dependent on this app alone.
2026-07-14 07:19:34 -07:00
Satoshi Qazi Muhammed fe3c65a706 governance: redesign Assembly into on-chain parliament landing (6 languages)
Replace the placeholder Assembly page with a professional on-chain governance
landing (animated 201-seat hemicycle, live chain ticker, blocs, sessions, active
votes, features). Content is fully localized into the 6 standard app languages
(en/tr/kmr/ckb/fa/ar) with RTL for ckb/fa/ar; each language variant is lazy-loaded.
2026-06-30 23:27:19 -07:00
SatoshiQaziMuhammed ed07878646 fix(auth): make password reset work via Supabase native recovery flow (#19)
PasswordReset called a non-existent 'password-reset' edge function, so
users could never reset a forgotten password. Switch to Supabase Auth's
built-in recovery: resetPasswordForEmail() to request the email, and
updateUser({password}) within the PASSWORD_RECOVERY session to set the
new password. Generic success message (no account enumeration); sign out
after reset to force clean re-login.
2026-06-24 16:58:12 -07:00
Satoshi Qazi Muhammed 7dddae633a identity: remove unused issueDate const (lint fix) 2026-06-20 18:28:38 -07:00
Satoshi Qazi Muhammed a92d61db8a identity: redesign e-ID + passport to match the Digital Kurdistan brand
Rebuild the /identity ID card and passport as flip cards rendered fully in
code (HTML/CSS/SVG) with each citizen's real data:

- e-ID: holographic light card, front (BÊ KURDISTAN JÎYANE NÎNE, photo, gold
  chip, bilingual fields NAV/NAME · PASNAV/SURNAME · DATE OF BIRTH ·
  NATIONALITY KURDISTANÎ/KURDISH · ID NUMBER, faux-QR, e-ID: KOMARA KURDISTAN
  footer) + back (DIGITAL KURDISTAN STATE: ID number, digital wallet ID,
  biometric status, valid-until, gov services, support; authority seal).
- Passport: navy cover with the gold ram (mouflon) emblem + Sorani/English
  titles, flips to a holographic data page (flag stripe, bilingual fields,
  photo, ICAO-style MRZ).
- Adds a Surname field and pulls the connected wallet address for the e-ID
  back. Document labels are the official bilingual set (not UI-translated);
  data stays device-local. Tap ↻ to flip.
2026-06-20 17:53:56 -07:00
Satoshi Qazi Muhammed 35d7ab38fa i18n: translate the P2P/Buy-Sell nav label per language
The Finance 'P2P/Buy-Sell' tile label was the hardcoded Latin string
'P2P/Buy-Sell' in all six locales, so it stayed untranslated (English) when
switching to Sorani/Farsi/Arabic/Turkish/Kurmancî. Keep the universal 'P2P'
token and translate the buy-sell part (TR Al-Sat, KMR Kirîn-Firotin,
CKB کڕین-فرۆشتن, FA خرید-فروش, AR بيع-شراء).
2026-06-20 05:57:19 -07:00
Satoshi Qazi Muhammed 070d682759 feat(p2p): rename Finance tile to P2P/Buy-Sell + add 'Buy with Visa' nav action
The Finance section tile label becomes 'P2P/Buy-Sell'. In the P2P dashboard's
top nav, next to the Messages icon, a Visa card (CreditCard) action 'Buy with
Visa' opens buy-sell.pezkiwi.app in a new tab. Adds p2pNav.buyVisa in all 6
locales.
2026-06-19 17:37:14 -07:00
Satoshi Qazi Muhammed cd56ab8fb6 feat(social): replace Help tile with Loto (Newroz) → loto.pex.mom
In the Social section of both the desktop app grid and the mobile home, the Help
tile becomes a Loto tile with the Newroz flame logo (loto-icon.svg) that opens
loto.pex.mom in a new tab. Adds the mobile.app.loto i18n key in all 6 locales.
Help remains reachable from the landing page.
2026-06-19 17:32:57 -07:00
Satoshi Qazi Muhammed 7a1d3e7917 feat(social): wire DKS Rojname → news.pex.mom and Events → kurdishtts.pezkiwi.app
- KurdMedia: DKS Rojname channel now links to the Dijital Kurdistan News site
- Social Events item opens the Kurdish TTS app (was coming-soon/locked)
- applies across mobile drawer, mobile home and desktop landing pallets
2026-06-15 17:54:19 -07:00
SatoshiQaziMuhammed 78e93e9766 feat(web): PEZ-20 badge on PEZ & USDT balance cards (#17)
* fix(ci): unblock deploy pipeline (audit gate + orphan submodule)

The Quality Gate & Deploy pipeline was failing at security-audit
(npm audit --audit-level=high), which blocks telegram-gate and the
whole deploy chain — that is why production was serving a stale bundle.

- npm audit fix (no --force, lockfile only): clears the critical vitest
  advisory (GHSA-5xrq-8626-4rwp) and the high elliptic one; only low-
  severity items remain, so 'npm audit --audit-level=high' now exits 0.
- Remove the orphaned 'exchange' gitlink: it is an empty submodule
  pointer with no .gitmodules mapping, which made git print
  'fatal: no submodule mapping found' during checkout.

Verified: lint, test (32 passed), and vite build all pass; audit gate
is green. No package.json changes.

* feat(web): PEZ-20 badge on PEZ and USDT balance cards

Add a small reusable Pez20Badge pill next to the PEZ and USDT tokens in
the wallet balance view, linking to the Token Standards docs. These are
fungible assets on Asset Hub, i.e. the PEZ-20 standard — this gives users
the familiar ERC-20-style mental model at a glance.

Additive only: no labels removed, native HEZ is intentionally not badged
(it is the native/gas token, not a PEZ-20 asset).
2026-06-12 23:28:05 -07:00
SatoshiQaziMuhammed d6ace14e70 fix(web): live collator/nominator counts after AHM + reliable B2B redirect (#15)
Staking migrated to Asset Hub (AHM), but the landing page still read
nominators from the relay (api.query.staking.counterForNominators),
which is now empty there — so the count showed '—'. Collators were read
from collatorSelection.candidates (empty; collators are invulnerables)
and only on Asset Hub, missing the People chain set.

- Nominators: query Asset Hub staking.counterForNominators (verified 30).
- Collators: count collatorSelection.invulnerables on both Asset Hub and
  People chain (2 + 2), tracked per-chain and summed.
- NetworkStats.tsx already used the correct sources; this aligns the
  landing page with it.

B2B button (/bereketli SSO interstitial): if there is no Supabase session
or the token exchange fails, redirect to https://bereketli.pezkiwi.app
instead of stranding the user on app.pezkuwichain.io/bereketli. (The
backend CORS allowlist was also missing app.pezkuwichain.io; fixed
server-side so the SSO exchange itself now succeeds.)
2026-06-11 16:41:14 -07:00
Kurdistan Tech Ministry 56f276af1b fix(wallet): add 20s timeout to web3Enable to prevent indefinite hang
- Wrap web3Enable() with Promise.race against a 20-second timeout
- On timeout: show descriptive error explaining the popup may be blocked
- Surface actual error messages (incl. timeout) instead of generic 'Failed to connect wallet'
- Both auto-restore and manual connect button now fail fast instead of hanging
2026-05-05 13:12:36 +03:00
Kurdistan Tech Ministry f024d21cf5 fix(wallet-modal): add loading state for extension connect, fix Play Store link
- Extension button now shows 'Approve in extension...' spinner while web3Enable waits
- Add generic error fallback for errors not matching 'authorize'/'not found' patterns
- Replace 'Coming soon on Play Store' with real Play Store download link (io.pezkuwichain.wallet)
- WalletConnectModal mobile hint now links directly to Play Store
- Updated in all 6 locales: en, tr, ar, fa, kmr, ckb
2026-05-05 08:28:52 +03:00
Kurdistan Tech Ministry 198f53b96f fix(config): point production WS endpoint to rpc.pezkuwichain.io
- App.tsx fallback: localhost:9944 → wss://rpc.pezkuwichain.io
- All locales: remove hardcoded ws://127.0.0.1:9944 from error message
2026-05-03 02:00:40 +03:00
Kurdistan Tech Ministry 9babb94e07 fix(auth): add pexsecBot for Telegram login on app.pezkuwichain.io
- pex.mom uses @PexMomBOT (8690398980)
- app.pezkuwichain.io uses @pexsecBot (8754021997)
- Edge function selects token based on bot_id from request
2026-05-01 23:32:25 +03:00
Kurdistan Tech Ministry ef6a7b2583 feat(i18n): add landing page translations for Sorani, Arabic, and Farsi
All 187 landing.* keys were missing from ckb/ar/fa locales, causing fallback to English.
2026-05-01 19:32:29 +03:00
Kurdistan Tech MinistryandClaude Sonnet 4.6 d446d711ba fix(web): replace AppLayout footer with identical LandingPageDesktop footer
Footer now uses lp-footer CSS classes and identical markup to pre-login landing page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:49:12 +03:00
Kurdistan Tech MinistryandClaude Sonnet 4.6 d1af76f444 fix(web): remove ArrowRightLeft icon from trading button + match bottom tab bar to pre-login design
- Remove ArrowRightLeft icon from desktop nav Trading dropdown button
- Bottom tab bar: add max-w-md mx-auto (centered) and bump z-index to z-50 to match MobileHomeLayout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:16:09 +03:00
Kurdistan Tech MinistryandClaude Sonnet 4.6 914d914b74 fix(lint): remove unused bodyOnly prop from LandingPageDesktop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:38:04 +03:00
Kurdistan Tech MinistryandClaude Sonnet 4.6 8f57224700 feat(web): restore authenticated desktop home layout with modern section cards
- Add body content sections (HeroSection, NetworkStats, TrustScoreCalculator, ChainSpecs, RewardDistribution) after section grid
- Update section cards with distinct gradient header colors per category (Finance/green, Governance/purple, Social/blue, Education/orange)
- Fix bottom tab bar to be full-width (removed max-w-md mx-auto)
- Adjust role/score cards background to bg-gray-800/70 for contrast against main bg
- Add bodyOnly prop to LandingPageDesktop (non-breaking, unused)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:36:31 +03:00
Kurdistan Tech Ministry 346a30fcbb fix(lint): remove unused keyMaterial variable + stale eslint-disable in LandingPageDesktop 2026-05-01 10:16:37 +03:00
Kurdistan Tech Ministry bac4148020 feat(web): authenticated home redesign + Telegram OAuth + SMTP
- AppLayout: logged-in desktop home redesigned — score cards, 4 section
  cards (Finance/Governance/Social/Education), governance extras, fixed
  bottom tab bar (Home/Citizen/Referral) matching mobile layout
- AppLayout: Trading dropdown in header (Presale/Staking/MultiSig),
  Logout button; removed 8-button grid
- Removed PalletsGrid and TokenomicsSection components
- Login: Telegram OAuth via oauth.telegram.org popup + postMessage +
  custom Edge Function (hash verification, find-or-create user,
  magic link token exchange)
- Login: X (Twitter) OAuth 2.0 wired to Supabase
- supabase/functions/telegram-auth: new Edge Function — verifies
  Telegram Login Widget hash, issues Supabase magic link token
- vite.config.ts: process-shim alias to fix TDZ with node polyfills
- i18n: updated locales (en/tr/kmr) for new UI sections
- SDK docs search index regenerated
2026-05-01 10:12:03 +03:00
Kurdistan Tech Ministry 709d408983 feat(landing): desktop landing page redesign
- New LandingPageDesktop component with full redesign
- Kurdish flag design tokens, animated sun, ticker band
- Three hero variants (V1 editorial split, V2 terminal, V3 mosaic)
- Live chain data: blocks, validators, nominators, citizens, proposals
- Features grid, architecture section, tokenomics tabs, referral steps
- Pallet grid with SVG sprite icons (16 pallets, 4 pillars)
- Dev-only hero variant switcher (auto-removed in prod)
- Mobile unchanged (MobileHomeLayout), logged-in unchanged
2026-04-30 23:30:09 +03:00
Kurdistan Tech Ministry 69789548e7 fix: prevent 'API not ready' on mobile by blocking wallet connect until blockchain initializes
- Add isApiInitializing state (true during WS connect, false on ready/fail)
- Add isApiReadyRef for closure-safe polling in connectWalletConnect
- connectWalletConnect now waits up to 30s for API instead of throwing immediately
- WalletModal connect buttons disabled + show spinner while blockchain is initializing
2026-04-27 15:00:58 +03:00
Kurdistan Tech Ministry 86ff43e206 feat: write p2p_user_id to tg_users on Telegram wallet link
TelegramConnect: query tg_users instead of users, resolve visa UUID
from p2p_visa table and store as p2p_user_id for cross-platform P2P.

P2PIdentityContext: when citizen resolves their UUID, backfill
tg_users.p2p_user_id if their wallet is linked to a Telegram account.
2026-04-27 13:31:22 +03:00
Kurdistan Tech Ministry da83aad2df feat: update exchange button URL to pex.network 2026-04-26 22:34:05 +03:00
Kurdistan Tech Ministry 645e703600 fix: auto-read citizen number from NFT role card instead of wallet address
Replaces the buggy useEffect that derived citizen number from wallet
address (and re-triggered whenever user cleared the field) with a clean
sync from DashboardContext's NFT-derived citizenNumber. Field is read-only
when NFT data is present, preventing the refill loop entirely.
2026-04-22 01:23:09 +03:00
Kurdistan Tech Ministry 95bf48f240 feat: show citizen ID in role card, show Welati in KYC status card 2026-04-21 05:22:19 +03:00
Kurdistan Tech Ministry e895a6611c fix: restore Pez-DEX button alongside new KRD Exchange button
Finance section now has both:
- Exchange: PezkuwiExchange.png logo → opens exchange.pezkuwichain.io (new tab)
- Pez-DEX: internal /dex route (auth required)
2026-04-14 18:08:36 +03:00
Kurdistan Tech Ministry ddc251fda3 feat: add Exchange button in finance card linking to exchange.pezkuwichain.io
- Use PezkuwiExchange.png logo instead of emoji for exchange app icon
- External link opens exchange.pezkuwichain.io in new tab (noopener)
- No auth required (exchange is publicly accessible)
- Added imgIcon and href fields to AppItem interface
2026-04-14 17:50:57 +03:00
Kurdistan Tech MinistryandKurdistan Tech Ministry 3f5ccdc5e0 feat: replace sun emoji with Kurdistan sun SVG on identity card and passport
Co-Authored-By: Kurdistan Tech Ministry <noreply@pezkuwichain.io>
2026-04-09 16:09:19 +03:00
Kurdistan Tech Ministry 892c78324f feat: add University, Certificates (Perwerde), Research education pages
- UniversityPage (/education/university): static course catalog, 6 courses, level badges, enroll redirects to Perwerde
- CertificatesPage (/education/certificates): blockchain-connected Perwerde platform, 3 tabs (Kurs/Tev li/Qediya), real api.query.perwerde + api.tx.perwerde.enroll, score tracker
- ResearchPage (/education/research): 5 academic papers, expandable abstracts, status badges, submit CTA
- Wire routes in App.tsx, remove comingSoon from all 3 in MobileHomeLayout
- Add university.*, certificates.*, research.* i18n keys to en.ts
2026-04-09 08:51:21 +03:00
Kurdistan Tech Ministry c56e021a6b feat: add KurdMedia, Help pages + fix Docs + wire routes + i18n (6 langs)
- Add KurdMediaPage (/social/kurdmedia): DKS media channels + social platform links
- Add HelpPage (/help): coming soon with planned features + WhatsKURD shortcut
- Rewrite Docs.tsx (/docs): clean documentation overview with section cards + docs.pezkuwichain.io button
- Wire new routes in App.tsx: /social/kurdmedia, /help
- Update MobileHomeLayout: kurdMedia → /social/kurdmedia, help → /help (fix crash)
- Add i18n keys to all 6 locales: kurdMedia.*, help.*, docs.section.*, messaging.palletNotReady
2026-04-09 08:19:45 +03:00
Kurdistan Tech Ministry eaccf65beb feat: activate whatsKURD button — mobile-styled encrypted messaging page
- Add WhatsKURDPage with mobile header, reusing useMessaging hook,
  KeySetup, InboxMessage, ComposeDialog components
- Route /social/whatskurd wired in App.tsx
- Remove comingSoon from whatsKURD in MobileHomeLayout
2026-04-09 08:03:12 +03:00