diff --git a/CHANGELOG.md b/CHANGELOG.md index 0709bafd8..e17aca66c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [0.1.36] - 2026-07-14 + +###    🚀 Features + +- NormalizeTokenAddress, dualChainConnect, useQuery migration  -  by @akbarsaputrait [(8dfbf)](https://github.com/RozoAI/intent-pay/commit/8dfbf278) +- **mobile**: Unified wallet chooser, eager hydration, deposit address loading state  -  by @akbarsaputrait [(d9117)](https://github.com/RozoAI/intent-pay/commit/d91171c3) +- **sdk**: Emit payment_no_tokens_available on empty SelectToken  -  by @akbarsaputrait [(dd983)](https://github.com/RozoAI/intent-pay/commit/dd9838b2) + +###    🐞 Bug Fixes + +- **connectkit**: + - Remove memo requirement from Solana payment flows  -  by @akbarsaputrait [(c98b4)](https://github.com/RozoAI/intent-pay/commit/c98b46fe) + - Update memo requirement for Stellar payments  -  by @akbarsaputrait [(86934)](https://github.com/RozoAI/intent-pay/commit/869342e2) +- **dualChainConnect**: + - Skip injector page on mobile dual-chain tap  -  by @akbarsaputrait [(66fbd)](https://github.com/RozoAI/intent-pay/commit/66fbd342) +- **ios**: + - Open deeplink via tag click, skip WAITING_WALLET intermediate page  -  by @akbarsaputrait [(0bee7)](https://github.com/RozoAI/intent-pay/commit/0bee72f3) +- **mobile**: + - Dedup injected wallets, img dimensions, deposit address order  -  by @akbarsaputrait [(8aa6c)](https://github.com/RozoAI/intent-pay/commit/8aa6c0ec) +- **sdk**: + - Guard isDNTEnabled against SSR (window undefined)  -  by @akbarsaputrait [(d9b65)](https://github.com/RozoAI/intent-pay/commit/d9b6566f) + - Allow payment options fetch for every appId, incl. default  -  by @akbarsaputrait [(732a5)](https://github.com/RozoAI/intent-pay/commit/732a5ac4) + +--- + + ## [0.1.34] - 2026-07-07 ###    🚀 Features diff --git a/docs/ANALYTICS.md b/docs/ANALYTICS.md index ff9e3e198..b25c5a0b2 100644 --- a/docs/ANALYTICS.md +++ b/docs/ANALYTICS.md @@ -27,6 +27,7 @@ The SDK sends anonymous payment funnel events to RozoAI's PostHog project. This | `payment_completed` | On-chain confirmed | `destination_chain`, `app_name` | | `payment_failed` | Terminal failure | `error_type`, `destination_chain`, `app_name` | | `payment_cancelled` | Modal closed mid-flow | `last_state`, `reason`, `app_name` | +| `payment_no_tokens_available` | SelectToken empty state renders (wallet connected, no eligible balances) | `token_mode`, `connected_chains`, `preferred_token_symbols`, `preferred_token_count`, `app_name` | | `error_occurred` | Error page shown | `error_type`, `error_title`, `can_retry`, `app_name` | | `payment_validation_error` | Invalid chain/token config | `error_type`, `app_name` | diff --git a/docs/PROVIDER_SETUP.md b/docs/PROVIDER_SETUP.md index 74944d258..c2a6a1df4 100644 --- a/docs/PROVIDER_SETUP.md +++ b/docs/PROVIDER_SETUP.md @@ -241,6 +241,27 @@ export function Providers({ children }) { --- +### ⚠️ SSR `ReferenceError: window is not defined` from telemetry (fixed in 0.1.36+) + +Before 0.1.36, the SDK's `isDNTEnabled()` telemetry guard touched +`window.doNotTrack` unconditionally. Under Next.js SSR, `window` is undefined, +so rendering `` on the server threw and returned HTTP 500 for +the entire page. + +**Workaround (older SDK versions):** pass `telemetry={false}` to disable the +built-in telemetry path. + +```tsx + +``` + +**On 0.1.36+:** the guard is SSR-safe (`typeof window === "undefined"` short- +circuits before any `window.*` access). Host apps can drop the +`telemetry={false}` workaround unless they want to opt out of telemetry for +privacy reasons — see [ANALYTICS.md](./ANALYTICS.md#opting-out). + +--- + ### ❌ Using SDK hooks outside the provider ```tsx diff --git a/docs/e2e/06-wallet-stellar.md b/docs/e2e/06-wallet-stellar.md index d6171c235..21fe53e67 100644 --- a/docs/e2e/06-wallet-stellar.md +++ b/docs/e2e/06-wallet-stellar.md @@ -2,8 +2,10 @@ Stellar uses a different approach: the SDK accepts an injectable `stellarKit` prop, so instead of automating a browser extension, a secret-key signer runs inside the -page. Playwright injects the secret at runtime — it never touches the app bundle or -any env var the app reads — so this signer cannot activate in production. +page. Playwright injects the secret at runtime and the app only wires the kit +when it was built with `NEXT_PUBLIC_E2E=1` — so the headless-signer code path +dead-code-eliminates out of production bundles at build time, and even in an +E2E build the kit does nothing unless Playwright has already injected a secret. --- @@ -73,8 +75,21 @@ export function createHeadlessStellarKit(secret: string): StellarWalletsKit { ## 3. Wire the kit into your provider In the component that renders ``, read the runtime-injected -window variable and pass the kit as the `stellarKit` prop. Use a `useState` -initializer so the kit is only constructed once per page load. +window variable and pass the kit as the `stellarKit` prop. Two safety gates: + +1. `NEXT_PUBLIC_E2E` env flag — the Playwright `webServer` config sets this to + `1` when starting the dev server. In production builds it's `undefined`, + so Next.js's bundler tree-shakes the `require("@/lib/e2e-stellar-kit")` + call and the entire `@creit.tech/stellar-wallets-kit` module graph never + enters the client chunk. +2. `window.__E2E_STELLAR_SECRET__` — the actual secret. Even in an E2E build, + the kit is only constructed when Playwright injected the secret via + `addInitScript`. The provider deletes the property from `window` + immediately after reading it, so later scripts on the same page (analytics, + third-party widgets) can't scrape it. + +Use a `useState` initializer so the kit is only constructed once per page +load — the kit registers a custom element and throws on a second construction. ```tsx import { createHeadlessStellarKit } from "@/lib/e2e-stellar-kit" @@ -83,10 +98,21 @@ import type { StellarWalletsKit } from "@creit.tech/stellar-wallets-kit" // Inside your provider component: const [stellarKit] = useState(() => { if (typeof window === "undefined") return undefined - const secret = (window as Window & { __E2E_STELLAR_SECRET__?: string }) - .__E2E_STELLAR_SECRET__ + // Env-flag gate — lets the bundler drop the entire import graph in prod. + if (!process.env.NEXT_PUBLIC_E2E) return undefined + + const w = window as Window & { __E2E_STELLAR_SECRET__?: string } + const secret = w.__E2E_STELLAR_SECRET__ + // Delete immediately so the key isn't readable later in the session. + delete w.__E2E_STELLAR_SECRET__ if (!secret) return undefined + try { + // require() inside the guarded branch: only resolved at build time when + // NEXT_PUBLIC_E2E=1, so the stellarWalletsKit bundle never enters the + // production chunk graph. + const { createHeadlessStellarKit } = + require("@/lib/e2e-stellar-kit") as typeof import("@/lib/e2e-stellar-kit") return createHeadlessStellarKit(secret) } catch (err) { console.error("[E2E] Failed to create headless Stellar kit:", err) @@ -101,9 +127,9 @@ return ( ) ``` -Because the kit only builds when `__E2E_STELLAR_SECRET__` is on `window` — and -Playwright is the only thing that sets it — the headless signer is impossible to -activate in a real user session. +Both gates must hold to activate the signer: the build must have been started +with `NEXT_PUBLIC_E2E=1` **and** Playwright must inject the secret before the +page loads. Neither is reachable from a real user session. --- @@ -145,5 +171,9 @@ test.describe("Bridge: Stellar → EVM (mainnet, real funds)", () => { ## Notes - `useStellarSigner` uses `page.addInitScript` — it must be called before navigation. +- Playwright starts the dev server with `NEXT_PUBLIC_E2E=1` (see + `examples/nextjs-app/e2e/playwright.config.ts` → `webServer.command: + "NEXT_PUBLIC_E2E=1 pnpm dev"`). Production `pnpm build` runs without the + flag, so the headless-kit import is dead-code-eliminated and never ships. - No browser extension needed; Stellar flows can run headless. - Source wallet needs XLM for fees and USDC to send. Destination wallet needs a USDC trustline. diff --git a/examples/nextjs-app/.env.e2e.example b/examples/nextjs-app/.env.e2e.example index 38c91fc28..a25a21d41 100644 --- a/examples/nextjs-app/.env.e2e.example +++ b/examples/nextjs-app/.env.e2e.example @@ -36,7 +36,10 @@ E2E_EVM_ADDRESS="" # - ADDRESS (G...) receives when Stellar is the DESTINATION → needs a trustline. # # The test injects the SECRET into the app at runtime, so you do NOT need to set -# any NEXT_PUBLIC_ secret by hand. Must be funded and have a USDC trustline. +# any NEXT_PUBLIC_ secret by hand. The Playwright webServer starts the dev +# server with NEXT_PUBLIC_E2E=1, which is what wires the headless Stellar kit; +# production `pnpm build` runs without the flag, so the signer never ships. +# Must be funded and have a USDC trustline. E2E_STELLAR_SECRET="" E2E_STELLAR_ADDRESS="" diff --git a/examples/nextjs-app/.gitignore b/examples/nextjs-app/.gitignore index 870efeb4a..401ee6fc3 100644 --- a/examples/nextjs-app/.gitignore +++ b/examples/nextjs-app/.gitignore @@ -51,4 +51,4 @@ playwright-report/ # chainwright wallet cache .wallet-cache/ - +.wallet-context/ diff --git a/examples/nextjs-app/package.json b/examples/nextjs-app/package.json index d1e2eb806..d2689048e 100644 --- a/examples/nextjs-app/package.json +++ b/examples/nextjs-app/package.json @@ -40,8 +40,8 @@ }, "dependencies": { "@creit.tech/stellar-wallets-kit": "^1.9.5", - "@rozoai/intent-common": "0.1.21", - "@rozoai/intent-pay": "0.1.34", + "@rozoai/intent-common": "0.1.23", + "@rozoai/intent-pay": "0.1.36", "@stellar/stellar-sdk": "^14.6.1", "@tanstack/react-query": "^5.0.0", "class-variance-authority": "^0.7.1", diff --git a/package.json b/package.json index a16550d7f..2cf040bf7 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,15 @@ "test:e2e": "pnpm --filter \"examples/nextjs-app\" test:e2e", "lint": "pnpm --filter @rozoai/intent-common run lint && pnpm --filter @rozoai/intent-pay run lint && pnpm --filter examples/nextjs-app run lint", "format": "pnpm --filter @rozoai/intent-common run format && pnpm --filter @rozoai/intent-pay run format && pnpm --filter examples/nextjs-app run format", - "build": "pnpm run build:common && pnpm run build:pay", + "build": "pnpm run build:common && pnpm run build:pay && pnpm run build:example", "build:common": "cd packages/pay-common && pnpm run build", "build:pay": "cd packages/connectkit && pnpm run build", + "build:example": "cd examples/nextjs-app && pnpm run build", "dev": "pnpm run dev:common & pnpm run dev:pay & pnpm run dev:example:16", "dev:common": "cd packages/pay-common && pnpm run dev", "dev:pay": "cd packages/connectkit && pnpm run dev", "dev:example": "cd examples/nextjs-app && pnpm run dev", - "dev:example:16": "cd examples/nextjs-16 && pnpm run dev", + "start:example": "cd examples/nextjs-app && pnpm run start", "install:local": "pnpm install", "prepare": "husky", "release": "pnpm run build && cd packages/pay-common && pnpm publish && cd ../../packages/connectkit && pnpm run release", diff --git a/packages/connectkit/bundle-analysis.html b/packages/connectkit/bundle-analysis.html index fa202deb3..23cc872b0 100644 --- a/packages/connectkit/bundle-analysis.html +++ b/packages/connectkit/bundle-analysis.html @@ -4929,7 +4929,7 @@