@www.hyperlinks.space/program-kit 1.2.91881 → 81.81.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Telegram Mini App storage availability probe (paste into DevTools console).
3
+ *
4
+ * What this script does:
5
+ * 1) Checks whether Telegram WebApp bridge exists.
6
+ * 2) Probes SecureStorage by real write+read+remove roundtrip.
7
+ * 3) Probes DeviceStorage by real write+read+remove roundtrip.
8
+ * 4) Probes CloudStorage by real write+read+remove roundtrip.
9
+ * 5) Prints a single QA verdict:
10
+ * - "secure" => SecureStorage works.
11
+ * - "device" => SecureStorage fails, but DeviceStorage works.
12
+ * - "none" => neither SecureStorage nor DeviceStorage works.
13
+ *
14
+ * Notes:
15
+ * - "API object exists" does NOT mean storage is supported. Telegram Desktop may expose methods
16
+ * but return UNSUPPORTED at runtime. This script verifies runtime behavior.
17
+ * - Probe keys are deleted at the end.
18
+ */
19
+ (async () => {
20
+ const wa = window.Telegram?.WebApp;
21
+ if (!wa) {
22
+ const result = {
23
+ verdict: "none",
24
+ webApp: null,
25
+ error: "Telegram.WebApp not found (not running in TMA context).",
26
+ };
27
+ console.log("[storage-probe]", result);
28
+ return result;
29
+ }
30
+
31
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32
+
33
+ const probeSecureOrDevice = async (storage, eventPrefix, includeCanRestore) => {
34
+ const key = `probe_${eventPrefix}_${Date.now()}`;
35
+ const value = "ok";
36
+ let eventFailed = null;
37
+
38
+ const failedEventName = `${eventPrefix}_failed`;
39
+ const savedEventName = `${eventPrefix}_key_saved`;
40
+
41
+ const onFailed = (payload) => { eventFailed = payload; };
42
+ const onSaved = () => {};
43
+
44
+ try {
45
+ wa.onEvent?.(failedEventName, onFailed);
46
+ wa.onEvent?.(savedEventName, onSaved);
47
+ } catch {}
48
+
49
+ const cleanup = () => {
50
+ try { wa.offEvent?.(failedEventName, onFailed); } catch {}
51
+ try { wa.offEvent?.(savedEventName, onSaved); } catch {}
52
+ };
53
+
54
+ if (!storage || typeof storage.setItem !== "function" || typeof storage.getItem !== "function") {
55
+ cleanup();
56
+ return {
57
+ present: false,
58
+ supported: false,
59
+ set: null,
60
+ get: null,
61
+ remove: null,
62
+ eventFailed: null,
63
+ };
64
+ }
65
+
66
+ return new Promise((resolve) => {
67
+ storage.setItem(key, value, (setErr, stored) => {
68
+ storage.getItem(key, async (getErr, gotValue, canRestore) => {
69
+ await wait(50);
70
+
71
+ const base = {
72
+ present: true,
73
+ supported: setErr == null && getErr == null && gotValue === value,
74
+ set: { err: setErr ?? null, stored: stored ?? null },
75
+ get: includeCanRestore
76
+ ? { err: getErr ?? null, value: gotValue ?? null, canRestore: canRestore ?? null }
77
+ : { err: getErr ?? null, value: gotValue ?? null },
78
+ remove: null,
79
+ eventFailed,
80
+ };
81
+
82
+ if (typeof storage.removeItem === "function") {
83
+ storage.removeItem(key, (rmErr, removed) => {
84
+ base.remove = { err: rmErr ?? null, removed: removed ?? null };
85
+ cleanup();
86
+ resolve(base);
87
+ });
88
+ } else {
89
+ cleanup();
90
+ resolve(base);
91
+ }
92
+ });
93
+ });
94
+ });
95
+ };
96
+
97
+ const probeCloudStorage = async () => {
98
+ const cs = wa.CloudStorage;
99
+ const key = `probe_cloud_${Date.now()}`;
100
+ const value = "ok";
101
+
102
+ if (!cs || typeof cs.setItem !== "function" || typeof cs.getItem !== "function") {
103
+ return {
104
+ present: false,
105
+ supported: false,
106
+ set: null,
107
+ get: null,
108
+ remove: null,
109
+ };
110
+ }
111
+
112
+ return new Promise((resolve) => {
113
+ cs.setItem(key, value, (setErr, stored) => {
114
+ cs.getItem(key, (getErr, gotValue) => {
115
+ const out = {
116
+ present: true,
117
+ supported: setErr == null && getErr == null && gotValue === value,
118
+ set: { err: setErr ?? null, stored: stored ?? null },
119
+ get: { err: getErr ?? null, value: gotValue ?? null },
120
+ remove: null,
121
+ };
122
+
123
+ if (typeof cs.removeItem === "function") {
124
+ cs.removeItem(key, (rmErr, removed) => {
125
+ out.remove = { err: rmErr ?? null, removed: removed ?? null };
126
+ resolve(out);
127
+ });
128
+ } else {
129
+ resolve(out);
130
+ }
131
+ });
132
+ });
133
+ });
134
+ };
135
+
136
+ const secureStorage = await probeSecureOrDevice(wa.SecureStorage, "secure_storage", true);
137
+ const deviceStorage = await probeSecureOrDevice(wa.DeviceStorage, "device_storage", false);
138
+ const cloudStorage = await probeCloudStorage();
139
+
140
+ const verdict = secureStorage.supported ? "secure" : deviceStorage.supported ? "device" : "none";
141
+
142
+ const result = {
143
+ verdict,
144
+ webApp: { platform: wa.platform ?? null, version: wa.version ?? null },
145
+ secureStorage,
146
+ deviceStorage,
147
+ cloudStorage,
148
+ };
149
+
150
+ console.log(`[storage-probe] verdict=${verdict}`, result);
151
+ return result;
152
+ })();
@@ -0,0 +1,33 @@
1
+ # Storage Lifetime for Wallet Keys
2
+
3
+ This document summarizes practical storage lifetime expectations for wallet-related secrets.
4
+
5
+ Important: "lifetime" is never absolute. Every layer can be lost due to account loss, uninstall, reset, policy changes, corruption, or compromise.
6
+
7
+ ## Quick Matrix
8
+
9
+ | Storage type | Typical lifetime | Main limiting factor | Cross-device | Notes for wallet keys |
10
+ |---|---|---|---|---|
11
+ | Telegram CloudStorage | Potentially long-lived | Telegram account/app availability and platform behavior | Yes (inside Telegram ecosystem) | Use for ciphertext/public metadata, not raw mnemonic |
12
+ | Telegram SecureStorage | Device-scoped durable | Device/app reinstall/reset and client support | No | Best TMA local tier when supported |
13
+ | Telegram DeviceStorage | Device-scoped durable | Device/app reinstall/reset | No | Weaker than SecureStorage; fallback tier |
14
+ | Session Storage (`sessionStorage`) | Current tab/session only | Tab/window/browser session end | No | Not suitable for persistent keys |
15
+ | Browser Local Storage (`localStorage`) | Potentially long-lived on same browser profile | User clearing data, browser profile loss | No | Use only encrypted blobs if needed |
16
+ | IndexedDB (web) | Potentially long-lived on same browser profile | User/browser data clearing, profile loss | No | Better than localStorage for larger encrypted blobs |
17
+ | Expo Secure Store (iOS/Android) | Durable on device | Uninstall/reset/device change | No | Good native local secret store; still not "forever" |
18
+ | Electron OS secure storage (Windows/macOS) | Durable on device profile | OS profile/device lifetime, reinstall/migration | No | Suitable desktop local secret store |
19
+ | In-memory runtime only | Process/session only | App restart/crash/background termination | No | Best for temporary decrypted material |
20
+
21
+ ## Lifetime Statements (practical wording)
22
+
23
+ - **Telegram CloudStorage:** theoretically long-lived, practically bounded by **Telegram account lifetime and platform behavior**.
24
+ - **Device-local storage (SecureStorage/DeviceStorage/Secure Store/OS keychain):** bounded by **device/app/profile lifetime**.
25
+ - **Session storage:** bounded by **session lifetime** (tab/window/app session).
26
+
27
+ ## Recommended usage pattern
28
+
29
+ 1. Keep **mnemonic as offline backup** (user-controlled, not in backend/cloud plaintext).
30
+ 2. Store day-to-day local unlock material in the strongest available **device-local secure store**.
31
+ 3. Store only **ciphertext** in cloud/sync layers.
32
+ 4. Treat all lifetimes as "best effort durability", not guaranteed permanence.
33
+
@@ -0,0 +1,31 @@
1
+ # Telegram CloudStorage Raw Keys Risks
2
+
3
+ This note clarifies a common misunderstanding:
4
+
5
+ - It is true that Telegram Mini App `CloudStorage` is scoped per bot/per user.
6
+ - It is true that each Telegram user session is strongly protected by Telegram account security controls (session management, device authorization, transport security, etc.).
7
+ - It is also true that endpoint compromise exists in both models (Bitcoin Core local storage and TMA/web clients).
8
+
9
+ The key security difference is not "cross-user direct reads". The key difference is how compromise can scale through the app runtime.
10
+
11
+ In other words: Telegram session protection is a strong baseline and should be acknowledged. The residual risk discussed here is about compromised Mini App code/runtime on an already authenticated user session, not about bypassing Telegram account/session controls.
12
+
13
+ ## Corrected comparison
14
+
15
+ Exactly: a PC can be hacked in both cases.
16
+
17
+ - **Bitcoin Core local storage:** an attacker usually needs to compromise that specific machine (or steal wallet backups) to get that user's keys.
18
+ - **Raw mnemonic in Telegram CloudStorage:** per-user storage still applies, but if the Mini App runtime/supply chain is compromised, malicious code can read each currently logged-in user's own CloudStorage during their session and exfiltrate it. This can impact many users over time without a "read all users" API.
19
+
20
+ So both models are vulnerable to endpoint compromise, but web/TMA delivery can add centralized distribution/supply-chain risk that increases aggregate exposure.
21
+
22
+ ## Practical differences vs local desktop wallet model
23
+
24
+ - **Runtime surface:**
25
+ - Local desktop wallet (Bitcoin Core style): narrower app distribution/runtime model.
26
+ - Web/TMA app: JavaScript/runtime/supply-chain surface is broader.
27
+
28
+ - **Secret impact on read:**
29
+ - **Raw mnemonic in cloud:** one successful read is immediate takeover.
30
+ - **Encrypted cloud blob:** attacker still needs decrypt capability (password/device key), which adds a barrier.
31
+
@@ -1,7 +1,7 @@
1
1
  # HyperlinksSpace — Wallet Architecture Doc
2
2
 
3
3
  > High-level system design, UI flow principles, and implementation thoughts.
4
- > Target: non-custodial TON wallet built into the HyperlinksSpace app (Flutter/TMA/Web).
4
+ > Target: TON wallet architecture options for HyperlinksSpace app (Flutter/TMA/Web), including non-custodial and custodial modes.
5
5
 
6
6
  ---
7
7
 
@@ -254,4 +254,150 @@ Swap between implementations via a flag (`kUseMockWalletState` for dev, env-driv
254
254
 
255
255
  ---
256
256
 
257
+ ## 13. Custodial Model Extension
258
+
259
+ This section adds a custodial architecture option alongside the existing non-custodial design.
260
+
261
+ ### 13.1 What "custodial" means in this doc
262
+
263
+ - Wallet secrets are stored centrally as encrypted data in backend storage.
264
+ - Backend trust boundary can participate in decrypt/sign authorization.
265
+ - User identity (Google/Telegram/GitHub/email OTP) becomes a stronger operational gate.
266
+
267
+ This is a product/trust choice, not just an implementation detail.
268
+
269
+ ---
270
+
271
+ ### 13.2 Custodial key architecture (envelope encryption)
272
+
273
+ Store per-wallet:
274
+
275
+ - `ciphertext` (wallet secret encrypted by a DEK)
276
+ - `wrapped_dek` (DEK encrypted by KEK)
277
+ - metadata (`key_version`, `algo`, `created_at`, `status`)
278
+
279
+ Keep KEK in KMS/HSM, not in DB/app code.
280
+
281
+ Result:
282
+ - DB theft alone is insufficient.
283
+ - Attacker also needs KMS/HSM access path and permissions.
284
+
285
+ ---
286
+
287
+ ### 13.3 Identity and auth in custodial mode
288
+
289
+ Identity providers:
290
+ - Google OAuth
291
+ - GitHub OAuth
292
+ - Telegram login bridge
293
+ - Email + OTP (protection code)
294
+
295
+ Account linking maps all providers to one internal `user_id`.
296
+
297
+ Auth session is used to authorize protected operations:
298
+ - key unwrap requests
299
+ - signing requests
300
+ - provider linking/unlinking
301
+
302
+ ---
303
+
304
+ ### 13.4 Custodial state machine
305
+
306
+ ```
307
+ [ No Wallet Record ]
308
+
309
+ ├── Create wallet ─► [ Custodial Encrypted ]
310
+ │ │
311
+ │ ├── Unlock request (auth + policy) ─► [ Session Unlocked ]
312
+ │ │ │
313
+ │ │ └── Sign tx ─► [ Ready ]
314
+ │ │
315
+ └── Restore/import ─────► [ Custodial Encrypted ]
316
+ ```
317
+
318
+ `Session Unlocked` should be short-lived and policy-limited (risk checks, cooldowns, limits).
319
+
320
+ ---
321
+
322
+ ### 13.5 Signing variants
323
+
324
+ ## Variant A: Backend signing (fully custodial)
325
+ - Backend unwraps DEK and signs transactions server-side.
326
+ - Client receives signed transaction/hash.
327
+ - Simplest UX, highest custodial responsibility.
328
+
329
+ ## Variant B: Backend-assisted client resolve (hybrid custodial)
330
+ - Backend authorizes access and returns short-lived decrypt context.
331
+ - Client resolves and signs locally.
332
+ - Better client-side control, still centralized key lifecycle.
333
+
334
+ Choose one variant explicitly in product docs and legal language.
335
+
336
+ ---
337
+
338
+ ### 13.6 API extension for custodial mode
339
+
340
+ ```
341
+ POST /wallet/custodial/create
342
+ Body: { user_id, wallet_label, encrypted_payload, wrapped_dek, key_version }
343
+ Response: { ok: true, wallet_id }
344
+
345
+ POST /wallet/custodial/unlock
346
+ Body: { wallet_id, auth_context }
347
+ Response: { unlock_token, expires_at } // or server-side unlock only
348
+
349
+ POST /wallet/custodial/sign
350
+ Body: { wallet_id, unlock_token, tx_payload }
351
+ Response: { signed_tx | tx_hash }
352
+
353
+ POST /wallet/custodial/rotate-kek
354
+ Body: { wallet_id? | batch_selector, new_key_version }
355
+ Response: { rewrapped_count }
356
+ ```
357
+
358
+ All endpoints must be audited and rate-limited.
359
+
360
+ ---
361
+
362
+ ### 13.7 Security controls required in custodial mode
363
+
364
+ - KMS/HSM-backed KEK, non-exportable where possible
365
+ - Strict IAM separation (runtime vs admin)
366
+ - Row-level access control by `user_id`
367
+ - Risk checks before unlock/sign (IP/device anomaly, velocity limits)
368
+ - Immutable security event log
369
+ - Emergency freeze and key-rotation runbook
370
+ - Signed release pipeline + dependency controls
371
+
372
+ ---
373
+
374
+ ### 13.8 UX implications vs non-custodial
375
+
376
+ Benefits:
377
+ - Easier cross-device recovery
378
+ - Less mnemonic friction for mainstream users
379
+
380
+ Tradeoffs:
381
+ - Backend/service compromise has larger blast radius
382
+ - Stronger legal/compliance burden
383
+ - Must clearly disclose custodial trust model
384
+
385
+ Recommended product stance:
386
+ - Keep non-custodial as default for advanced users.
387
+ - Offer custodial mode as explicit opt-in with clear warnings and recovery terms.
388
+
389
+ ---
390
+
391
+ ### 13.9 Updated roadmap including custodial track
392
+
393
+ | Phase | Non-custodial track | Custodial track |
394
+ |---|---|---|
395
+ | **Phase 1** | Create/restore local wallet, deploy flow | Auth foundation (Google/GitHub/Telegram/email OTP), user linking |
396
+ | **Phase 2** | Device/local storage hardening | Envelope storage (`ciphertext` + `wrapped_dek`) + KMS/HSM integration |
397
+ | **Phase 3** | TMA/Desktop fallback tiers | Unlock/sign endpoints + policy/rate limits |
398
+ | **Phase 4** | Send/receive/history | Custodial signing UX + anomaly protections |
399
+ | **Phase 5** | Backup/export UX hardening | Key rotation, incident runbooks, compliance hardening |
400
+
401
+ ---
402
+
257
403
  *Last updated: April 2026. Maintained in repo at `docs/wallets_hosting_architecture.md`.*
package/fullREADME.md CHANGED
@@ -4,23 +4,30 @@
4
4
 
5
5
  <u>**In progress, contribute!**</u>
6
6
 
7
- This program is built upon [React Native](https://reactnative.dev/) by Meta and [Expo](https://expo.dev) multiplatform technologies, Windows build and executable creation achieved with [Electron Builder](https://www.electron.build/) and [Electron Forge](https://www.electronforge.io/), working in Telegram with help of [Telegram Mini Apps React SDK](http://telegram-mini-apps.com/) and [Bot API](https://core.telegram.org/bots). AI is backed by [OpenAI API](https://openai.com/ru-RU/api/), blockchain data is processed from [Swap.Coffee API](https://docs.swap.coffee/eng/user-guides/welcome).
8
-
9
- ## Program Kit
10
-
11
- To give value for other developers we decided to launch an npm package that provides a ready starter for creating a multiplatform program in one command.
12
-
13
- ```bash
14
- npx @www.hyperlinks.space/program-kit ./your-project
15
- ```
16
-
17
- Link to the package: https://www.npmjs.com/package/@www.hyperlinks.space/program-kit
7
+ This program is built upon [React Native](https://reactnative.dev/) by Meta and [Expo](https://expo.dev) multiplatform technologies, Windows build and executable creation achieved with [Electron Builder](https://www.electron.build/) and [Electron Forge](https://www.electronforge.io/), working in Telegram with help of [Telegram Mini Apps React SDK](http://telegram-mini-apps.com/), [Bot API](https://core.telegram.org/bots) and [Grammy](https://grammy.dev/). AI is backed by [OpenAI API](https://openai.com/ru-RU/api/), blockchain info is processed from [Swap.Coffee API](https://docs.swap.coffee/eng/user-guides/welcome). DB for the best user's experience we host on [Neon](https://neon.tech/).
18
8
 
19
9
  ## Program design
20
10
 
21
11
  Access [Figma](https://www.figma.com/design/53lDKAD6pRv3e0uef1DP18/TECHSYMBAL-Inc.?node-id=754-71&t=v3tmAlywNgXkTWMd-1) in real time for contributing. Contact [Seva](t.me/sevaaignatyev) in Telegram to discuss and implement.
22
12
 
23
- Copying fully or partially, usage as an inspiration for other developments are unpleasant, participation in our projects is appreciated. All core materials are available publicly for instant access worldwide and our project availability for newcomers.
13
+ All core materials are available publicly for сгккуте hyperlinks.space team members' instant and easy access worldwide and our project's availability for newcomers' research only.
14
+
15
+ ## Structure
16
+
17
+ - [`app`](./app) - Expo/React Telegram Mini App client (web/mobile screens, navigation, UI logic).
18
+ - [`ui`](./ui) - shared UI layer (components, theme tokens, and font configuration used by the app).
19
+ - [`bot`](./bot) - TypeScript Telegram bot service and runtime entrypoints.
20
+ - [`database`](./database) - database startup/migration/service scripts.
21
+ - [`ai`](./ai) - AI assistant service logic and model integration points.
22
+ - [`api`](./api) - backend API handlers and server-side endpoints.
23
+ - [`blockchain`](./blockchain) - TON/blockchain interaction logic and related helpers.
24
+ - [`telegram`](./telegram) - Telegram-specific integration utilities and adapters.
25
+ - [`windows`](./windows) - Electron desktop shell, NSIS installer config, and auto-update flow.
26
+ - [`scripts`](./scripts) - developer/ops scripts (local run, migration, release helpers).
27
+ - [`docs`](./docs) - project and operational documentation.
28
+ - [`backlogs`](./backlogs) - short-term planning notes and prioritized work items.
29
+ - [`assets`](./assets) - static assets used by app, installer, and branding.
30
+ - [`dist`](./dist) - generated web build output (export artifacts).
24
31
 
25
32
  ## How to fork and contribute?
26
33
 
@@ -74,6 +81,35 @@ git switch -c new-branch-for-next-update # Create and switch to a new feature br
74
81
 
75
82
  ## Local deploy
76
83
 
84
+ `npm` package note: `.env.example` is included in the published package so you can use it as a reference for establishing your testing environment with `.env` file.
85
+
86
+ Before local deploy / cloud deploy, prepare these env-backed services:
87
+
88
+ 1. **Neon PostgreSQL (`DATABASE_URL`)**
89
+ - Create an account/project at [Neon](https://neon.tech/).
90
+ - Create a database and copy the connection string.
91
+ - Put it into `.env` as `DATABASE_URL=...`.
92
+ 2. **OpenAI API (`OPENAI_API_KEY`)**
93
+ - Create an account at [OpenAI Platform](https://platform.openai.com/).
94
+ - Create an API key in the API Keys page.
95
+ - Put it into `.env` as `OPENAI_API_KEY=...`.
96
+ 3. **Telegram bot token (`BOT_TOKEN`)**
97
+ - In Telegram, open [@BotFather](https://t.me/BotFather), create a test bot with `/newbot`.
98
+ - Copy the bot token and put it into `.env` as `BOT_TOKEN=...`.
99
+ 4. **Vercel project envs (for comfortable deploy/testing)**
100
+ - Create a [Vercel](https://vercel.com/) account and import this repository as a project.
101
+ - In Project Settings -> Environment Variables, set at least:
102
+ - `DATABASE_URL`
103
+ - `OPENAI_API_KEY`
104
+ - `BOT_TOKEN` (or `TELEGRAM_BOT_TOKEN`)
105
+ - Pull envs locally when needed with `vercel env pull .env.local`.
106
+
107
+ Copy env template locally:
108
+
109
+ ```bash
110
+ cp .env.example .env
111
+ ```
112
+
77
113
  To start the full local stack, run:
78
114
 
79
115
  ```bash
@@ -82,6 +118,13 @@ npm run start
82
118
 
83
119
  This runs Expo dev server, the Telegram bot (polling mode), and local Vercel API (`vercel dev`).
84
120
 
121
+ After `npm run start`, you can test the app on real phones with Expo Go:
122
+
123
+ - Install **Expo Go** from Google Play (Android) or App Store (iOS).
124
+ - Make sure your phone and development machine are on the same network.
125
+ - Open Expo Go and scan the QR code shown in the terminal/Expo UI.
126
+ - The app will launch on the device and hot-reload on code changes.
127
+
85
128
  Isolated/local run options:
86
129
 
87
130
  - Expo only (no bot, no Vercel): `npm run start:expo`
@@ -113,11 +156,11 @@ The `.env` file is gitignored; do not commit it.
113
156
 
114
157
  Current Actions workflows include:
115
158
 
116
- - `Vercel Deploy Test` (`.github/workflows/vercel-deploy-test-envs.yml`) - manual web deploy to Vercel.
117
- - `NPM Package Release` (`.github/workflows/npm-package-release.yml`) - npm/GitHub Packages release workflow.
118
- - `Electron EXE Release` and `Electron Forge EXE Release` - manual Windows release pipelines.
119
- - `EXPO Publish` (`.github/workflows/expo-publish.yml`) - manual OTA publish with EAS CLI.
120
- - `Lint errors check` (`.github/workflows/lint-errors-check.yml`) - manual lint check.
159
+ - [`Vercel Deploy Test`](./.github/workflows/vercel-deploy-test-envs.yml) - manual web deploy to Vercel.
160
+ - [`NPM Package Release`](./.github/workflows/npm-package-release.yml) - npm/GitHub Packages release workflow.
161
+ - [`Electron EXE Release`](./.github/workflows/electron-exe-release.yml) and [`Electron Forge EXE Release`](./.github/workflows/electron-forge-exe-release.yml) - manual Windows release pipelines.
162
+ - [`EXPO Publish`](./.github/workflows/expo-publish.yml) - manual OTA publish with EAS CLI.
163
+ - [`Lint errors check`](./.github/workflows/lint-errors-check.yml) - manual lint check.
121
164
 
122
165
  ## Expo Workflows
123
166
 
@@ -134,14 +177,6 @@ Run `npm run draft` to [publish a preview update](https://docs.expo.dev/eas/work
134
177
 
135
178
  Run `npm run development-builds` to [create a development build](https://docs.expo.dev/eas/workflows/examples/create-development-builds/). Note - you'll need to follow the [Prerequisites](https://docs.expo.dev/eas/workflows/examples/create-development-builds/#prerequisites) to ensure you have the correct emulator setup on your machine.
136
179
 
137
- ### Production Deployments
138
-
139
- Run `npm run deploy` to [deploy to production](https://docs.expo.dev/eas/workflows/examples/deploy-to-production/). Note - you'll need to follow the [Prerequisites](https://docs.expo.dev/eas/workflows/examples/deploy-to-production/#prerequisites) to ensure you're set up to submit to the Apple and Google stores.
140
-
141
- ## Hosting
142
-
143
- Expo offers hosting for websites and API functions via EAS Hosting. See the [Getting Started](https://docs.expo.dev/eas/hosting/get-started/) guide to learn more.
144
-
145
180
  ### Deploy web build to Vercel
146
181
 
147
182
  From the repository root, deploy the static web build to Vercel production:
@@ -152,7 +187,7 @@ vercel --prod
152
187
 
153
188
  Deploying from repository root makes this folder the project root, so `api/bot` is deployed and no Root Directory setting is needed. The project is configured so Vercel runs `npx expo export -p web` and serves the `dist/` output. Link the project first with `vercel` if needed.
154
189
 
155
- ## Telegram bot (Grammy)
190
+ ## Telegram bot
156
191
 
157
192
  The bot is extended beyond a basic "Hello" and "Start program" responder and now supports AI streaming and threads.
158
193
 
@@ -174,6 +209,16 @@ The bot is extended beyond a basic "Hello" and "Start program" responder and now
174
209
  - Run full local stack (Expo + bot + Vercel): `npm run start`
175
210
  - Keep production and local bot tokens separate when possible to avoid webhook/polling conflicts.
176
211
 
212
+ ## Program Kit
213
+
214
+ To make it easier for developers to create multiplatform programs with us, we decided to launch an npm package that provides a ready starter for creating such a program basis in one command.
215
+
216
+ ```bash
217
+ npx @www.hyperlinks.space/program-kit ./new-program
218
+ ```
219
+
220
+ Link to the package: https://www.npmjs.com/package/@www.hyperlinks.space/program-kit
221
+
177
222
  ## Where to discuss the project?
178
223
 
179
224
  This repository has [GitHub Discussions](https://github.com/HyperlinksSpace/HyperlinksSpaceProgram/discussions) opened, as well you can join our [Telegram Chat](https://t.me/HyperlinksSpaceChat) and [Channel](https://t.me/HyperlinksSpace).
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // Earliest bundle entry: polyfill Node `Buffer` before expo-router loads route modules (@ton/* uses it at load time).
2
+ import "./polyfills/buffer";
3
+ import "expo-router/entry";
package/npmReadMe.md CHANGED
@@ -19,13 +19,13 @@ and deployed across popular platforms.
19
19
  ### npmjs (public)
20
20
 
21
21
  ```bash
22
- npx @www.hyperlinks.space/program-kit ./my-new-program
22
+ npx @www.hyperlinks.space/program-kit ./new-program
23
23
  ```
24
24
 
25
25
  ### GitHub Packages
26
26
 
27
27
  ```bash
28
- npx @hyperlinksspace/program-kit ./my-new-program
28
+ npx @hyperlinksspace/program-kit ./new-program
29
29
  ```
30
30
 
31
31
  If you install from GitHub Packages, configure `.npmrc` with the `@hyperlinksspace`
@@ -34,7 +34,7 @@ registry and token.
34
34
  ## After Scaffold
35
35
 
36
36
  ```bash
37
- cd my-new-program
37
+ cd new-program
38
38
  npm install
39
39
  npm run start
40
40
  ```
package/package.json CHANGED
@@ -9,6 +9,7 @@
9
9
  "program-kit": "scripts/program-kit-init.cjs"
10
10
  },
11
11
  "files": [
12
+ ".env.example",
12
13
  "README.md",
13
14
  "fullREADME.md",
14
15
  "npmReadMe.md",
@@ -39,8 +40,8 @@
39
40
  "type": "git",
40
41
  "url": "https://github.com/HyperlinksSpace/HyperlinksSpaceBot.git"
41
42
  },
42
- "main": "expo-router/entry",
43
- "version": "1.2.91881",
43
+ "main": "index.js",
44
+ "version": "81.81.81",
44
45
  "type": "module",
45
46
  "engines": {
46
47
  "node": ">=18 <=22"
@@ -161,6 +162,10 @@
161
162
  "@react-navigation/native": "^7.1.6",
162
163
  "@swap-coffee/sdk": "^1.5.4",
163
164
  "@tma.js/sdk-react": "^3.0.16",
165
+ "@ton/core": "^0.63.1",
166
+ "@ton/crypto": "^3.3.0",
167
+ "@ton/ton": "^16.2.3",
168
+ "buffer": "^6.0.3",
164
169
  "electron-updater": "^6.8.3",
165
170
  "expo": "~54.0.0",
166
171
  "expo-blur": "~15.0.8",
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Quick check that api/base.ts works. Run: npx tsx scripts/test-api-base.ts
2
+ * Quick check that api/_base.ts works. Run: npx tsx scripts/test-api-base.ts
3
3
  * In Node (no window), getApiBaseUrl() uses Vercel env or falls back to http://localhost:3000.
4
4
  */
5
- import { getApiBaseUrl, buildApiUrl } from "../api/base.js";
5
+ import { getApiBaseUrl, buildApiUrl } from "../api/_base.js";
6
6
 
7
7
  const base = getApiBaseUrl();
8
8
  const full = buildApiUrl("/api/telegram");
package/telegram/post.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  normalizeUsername,
8
8
  upsertUserFromTma,
9
9
  } from '../database/users.js';
10
+ import { getDefaultWalletByUsername } from '../database/wallets.js';
10
11
 
11
12
  const LOG_TAG = '[api/telegram]';
12
13
 
@@ -302,10 +303,53 @@ export async function handlePost(
302
303
  const dbStart = Date.now();
303
304
  try {
304
305
  await upsertUserFromTma({ telegramUsername, locale });
306
+ const wallet = await getDefaultWalletByUsername(telegramUsername);
305
307
  log('db_upsert_done', {
306
308
  dbMs: Date.now() - dbStart,
307
309
  elapsedMs: Date.now() - startMs,
310
+ hasWallet: !!wallet,
308
311
  });
312
+
313
+ if (wallet) {
314
+ log('success', {
315
+ telegramUsername,
316
+ hasWallet: true,
317
+ totalMs: Date.now() - startMs,
318
+ });
319
+ return new Response(
320
+ JSON.stringify({
321
+ ok: true,
322
+ telegram_username: telegramUsername,
323
+ has_wallet: true,
324
+ wallet: {
325
+ id: wallet.id,
326
+ wallet_address: wallet.wallet_address,
327
+ wallet_blockchain: wallet.wallet_blockchain,
328
+ wallet_net: wallet.wallet_net,
329
+ type: wallet.type,
330
+ label: wallet.label,
331
+ is_default: wallet.is_default,
332
+ source: wallet.source,
333
+ },
334
+ }),
335
+ { status: 200, headers: { 'content-type': 'application/json' } },
336
+ );
337
+ }
338
+
339
+ log('success', {
340
+ telegramUsername,
341
+ hasWallet: false,
342
+ totalMs: Date.now() - startMs,
343
+ });
344
+ return new Response(
345
+ JSON.stringify({
346
+ ok: true,
347
+ telegram_username: telegramUsername,
348
+ has_wallet: false,
349
+ wallet_required: true,
350
+ }),
351
+ { status: 200, headers: { 'content-type': 'application/json' } },
352
+ );
309
353
  } catch (e) {
310
354
  logErr('db_upsert_failed', e);
311
355
  return new Response(
@@ -317,12 +361,4 @@ export async function handlePost(
317
361
  );
318
362
  }
319
363
 
320
- log('success', {
321
- telegramUsername,
322
- totalMs: Date.now() - startMs,
323
- });
324
- return new Response(
325
- JSON.stringify({ ok: true, telegram_username: telegramUsername }),
326
- { status: 200, headers: { 'content-type': 'application/json' } },
327
- );
328
364
  }