From a14b81afcb815938dfe5accfa648550b0e508bc5 Mon Sep 17 00:00:00 2001 From: Dan Anderson Date: Tue, 20 Oct 2026 11:25:05 -0400 Subject: [PATCH 1/4] Add Casa Tookan app discovery manifest --- apps/web/next.config.ts | 7 ++++ apps/web/public/.well-known/wallet-app.json | 35 ++++++++++++++++++++ apps/web/public/_headers | 9 +++++ apps/web/public/wallet-app/banner.png | Bin 0 -> 55211 bytes apps/web/public/wallet-app/icon.png | Bin 0 -> 58848 bytes apps/web/public/wallet-app/splash.png | Bin 0 -> 15268 bytes docs/casatookan.md | 18 ++++++++++ 7 files changed, 69 insertions(+) create mode 100644 apps/web/public/.well-known/wallet-app.json create mode 100644 apps/web/public/_headers create mode 100644 apps/web/public/wallet-app/banner.png create mode 100644 apps/web/public/wallet-app/icon.png create mode 100644 apps/web/public/wallet-app/splash.png create mode 100644 docs/casatookan.md diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 245ace01..72ca8987 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -5,6 +5,13 @@ import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; void initOpenNextCloudflareForDev(); const nextConfig: NextConfig = { + async headers() { + return [ + { source: "/.well-known/:path*", headers: [{ key: "Access-Control-Allow-Origin", value: "*" }] }, + { source: "/.well-known/wallet-app.json", headers: [{ key: "Content-Type", value: "application/json; charset=utf-8" }] }, + { source: "/wallet-app/:path*", headers: [{ key: "Access-Control-Allow-Origin", value: "*" }] }, + ]; + }, reactCompiler: true, /** * How long one page may take to prerender, in seconds. diff --git a/apps/web/public/.well-known/wallet-app.json b/apps/web/public/.well-known/wallet-app.json new file mode 100644 index 00000000..fd9a85e5 --- /dev/null +++ b/apps/web/public/.well-known/wallet-app.json @@ -0,0 +1,35 @@ +{ + "name": "xcp.fun", + "short_name": "xcp.fun", + "description": "Discover Counterparty token launches and community projects on Bitcoin.", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#09090b", + "categories": [ + "finance", + "counterparty" + ], + "icons": [ + { + "src": "/wallet-app/icon.png", + "sizes": "1024x1024", + "type": "image/png", + "purpose": "any" + } + ], + "wallet": { + "version": "1", + "subtitle": "Counterparty launches", + "heroImageUrl": "/wallet-app/banner.png", + "splashImageUrl": "/wallet-app/splash.png", + "chains": { + "optional": [ + "bip122:000000000019d6689c085ae165831e93" + ] + }, + "capabilities": { + "optional": [] + } + } +} diff --git a/apps/web/public/_headers b/apps/web/public/_headers new file mode 100644 index 00000000..bd6bc929 --- /dev/null +++ b/apps/web/public/_headers @@ -0,0 +1,9 @@ + +/.well-known/* + Access-Control-Allow-Origin: * + +/.well-known/wallet-app.json + Content-Type: application/json; charset=utf-8 + +/wallet-app/* + Access-Control-Allow-Origin: * diff --git a/apps/web/public/wallet-app/banner.png b/apps/web/public/wallet-app/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..8bfe0072492de8f7910c9bfb3d84a5079742a03a GIT binary patch literal 55211 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�E_8QP6sD@}nh-8w+ITIB1G86~gz8`6EQoTP~ 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 XL3yPg8-Ta6TR0`Kx?F>ddHDYTn5oBj literal 0 HcmV?d00001 diff --git a/docs/casatookan.md b/docs/casatookan.md new file mode 100644 index 00000000..6f35ef4d --- /dev/null +++ b/docs/casatookan.md @@ -0,0 +1,18 @@ +# Casa Tookan app + +The app manifest is served at `/.well-known/wallet-app.json`. The PNGs under `/wallet-app/` provide its icon, banner and splash. Paths are root-relative so they resolve outside `/.well-known/`. + +The manifest makes this site discoverable when a user saves it in Casa Tookan. It does not register the site in a curated directory or enable new signing methods. Bitcoin support is optional because browsing needs no wallet. The splash uses Casa Tookan's automatic readiness timeout. + +This change adds browsing metadata only. Existing connection and signing flows are unchanged. + +## Deployment check + +- Fetch the manifest without cookies and confirm JSON, HTTP 200 and `Access-Control-Allow-Origin: *`. +- Fetch a missing `/.well-known/` path and a missing `/wallet-app/` image: each must return 404 with CORS, not the homepage. +- Fetch all three images: 1024×1024 opaque icon, 1200×630 banner, 200×200 splash. +- Open the site in the native Casa Tookan browser, save it and reopen its app card. Check the title, artwork and splash dismissal. + +Headers are configured in both Next (SSR/404) and Cloudflare Static Assets (public files). Production framing and CSP policies are unchanged; the development iframe bridge is not installed. + +Format: https://docs.casatookan.com/apps.html From 5f9888202aeca8d7a1c9b51ad89758e08d430078 Mon Sep 17 00:00:00 2001 From: Dan Anderson Date: Tue, 20 Oct 2026 13:29:24 -0400 Subject: [PATCH 2/4] Add opt-in Casa Tookan wallet connection pilot --- .gitignore | 4 + apps/web/package.json | 2 +- apps/web/public/.well-known/wallet-app.json | 2 +- apps/web/src/components/header-wallet.tsx | 7 +- apps/web/src/components/site-header.tsx | 4 +- apps/web/src/lib/wallet/wallet-context.tsx | 9 +- browser-tests/casatookan/connect.spec.mjs | 167 ++++++++++++++++++++ docs/casatookan.md | 20 ++- package-lock.json | 54 ++++++- package.json | 5 +- playwright.casatookan.config.mjs | 14 ++ 11 files changed, 276 insertions(+), 12 deletions(-) create mode 100644 browser-tests/casatookan/connect.spec.mjs create mode 100644 playwright.casatookan.config.mjs diff --git a/.gitignore b/.gitignore index 74de1c9f..56e24506 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ next-env.d.ts # The manifest is the batch's evidence, but it is reproducible from the script # and the cutoff, and it is recorded in D1 once the batch is frozen. dist/ + +test-results/ + +playwright-report/ diff --git a/apps/web/package.json b/apps/web/package.json index cb337e5d..3d65ad7c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -20,7 +20,7 @@ "@noble/hashes": "^1.8.0", "@scure/base": "^2.2.0", "@scure/btc-signer": "^2.2.0", - "@xcp/wallet-sdk": "github:XCP/wallet-sdk#5a056ce83dc3a999f9384a7cb625094471d19c91", + "@xcp/wallet-sdk": "github:XCP/wallet-sdk#25425b75142f832705d1c3611ab2cba2adc0f942", "babel-plugin-react-compiler": "^1.0.0", "bignumber.js": "^9.3.1", "next": "16.3.4", diff --git a/apps/web/public/.well-known/wallet-app.json b/apps/web/public/.well-known/wallet-app.json index fd9a85e5..5f792483 100644 --- a/apps/web/public/.well-known/wallet-app.json +++ b/apps/web/public/.well-known/wallet-app.json @@ -29,7 +29,7 @@ ] }, "capabilities": { - "optional": [] + "optional": ["provider.bitcoin"] } } } diff --git a/apps/web/src/components/header-wallet.tsx b/apps/web/src/components/header-wallet.tsx index c6017784..eb84c40f 100644 --- a/apps/web/src/components/header-wallet.tsx +++ b/apps/web/src/components/header-wallet.tsx @@ -19,7 +19,7 @@ import { useT } from "@/lib/i18n/client"; */ export function HeaderWallet() { const t = useT(); - const { status, address, proofStatus } = useWallet(); + const { status, address, proofStatus, connecting, connectError } = useWallet(); const menu = useWalletMenu(); const { accounts, switchAccount, disconnect } = menu; /** @@ -70,17 +70,20 @@ export function HeaderWallet() { // — and that read is synchronous from localStorage, so it lands within a // frame of hydration rather than after a round trip. return ( -
+
{!connected ? ( <> {installPrompt} + {connectError &&

{connectError}

} ) : ( diff --git a/apps/web/src/components/site-header.tsx b/apps/web/src/components/site-header.tsx index 1add7bf9..73a6c5ad 100644 --- a/apps/web/src/components/site-header.tsx +++ b/apps/web/src/components/site-header.tsx @@ -134,7 +134,7 @@ export function SiteHeader() {
{/* Telegram uses its icon on phones and in translated headers. Status chips take turns here to leave room for the menu. */} - + {queued ? : } @@ -148,7 +148,7 @@ export function SiteHeader() {
-
+
diff --git a/apps/web/src/lib/wallet/wallet-context.tsx b/apps/web/src/lib/wallet/wallet-context.tsx index 2006fd4e..796dad8a 100644 --- a/apps/web/src/lib/wallet/wallet-context.tsx +++ b/apps/web/src/lib/wallet/wallet-context.tsx @@ -7,6 +7,7 @@ import { WalletProvider as SdkWalletProvider, useWallet as useSdkWallet, } from "@xcp/wallet-sdk/react"; +import { CASATOOKAN_WALLET, discoverWallets, KNOWN_WALLETS } from "@xcp/wallet-sdk/web"; import "@/lib/wallet/sdk-config"; export type { ProofStatus }; @@ -28,9 +29,15 @@ const STATUS: Record = { reload_required: "disconnected", }; +const wallets = discoverWallets( + process.env.NEXT_PUBLIC_CASATOOKAN_ENABLED === "true" + ? [...KNOWN_WALLETS, CASATOOKAN_WALLET] + : KNOWN_WALLETS, +); + export function WalletProvider({ children }: { children: ReactNode }) { // Connect is login here: a wallet that proves nothing at connect (Horizon) is asked to sign the proof. - return {children}; + return {children}; } export function useWallet() { diff --git a/browser-tests/casatookan/connect.spec.mjs b/browser-tests/casatookan/connect.spec.mjs new file mode 100644 index 00000000..dbef3089 --- /dev/null +++ b/browser-tests/casatookan/connect.spec.mjs @@ -0,0 +1,167 @@ +import { expect, test } from '@playwright/test' +import { secp256k1 } from '@noble/curves/secp256k1' +import { legacyMessageHash } from '@xcp/wallet-sdk' + +const route = '/es/profile' +const enabled = process.env.CASATOOKAN_TEST_ENABLED !== 'false' +const address = 'bc1qvux25709r4uw6rzc8wyl7wwecjdhrx085hm5ty' +const publicKey = '0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967' +const taproot = 'bc1pfqarxerx6v0zh6zcj2f3a3np8jselxn8vgefdx95c0n328qpzg4ssw6kv5' +const header = page => page.getByRole('banner') +const connect = page => header(page).getByRole('button', { name: 'Conectar', exact: true }).first() +const account = page => header(page).getByText(/bc1q.*5ty/).filter({ visible: true }).first() + +async function bridge(page, options = {}) { + await page.exposeFunction('signCasaProof', message => { + const signature = secp256k1.sign(legacyMessageHash(message), new Uint8Array(32).fill(9)) + return Buffer.from([31 + signature.recovery, ...signature.toCompactRawBytes()]).toString('base64') + }) + await page.addInitScript(({ address, publicKey, taproot, options }) => { + const listeners = new Map() + const state = { + requests: 0, signatures: 0, psbts: 0, reject: false, unsupported: false, + granted: sessionStorage.getItem('casa-grant') === 'yes', + revoke() { + state.granted = false + sessionStorage.removeItem('casa-grant') + for (const fn of listeners.get('accountsChanged') ?? []) fn([]) + }, + inject() { + Object.assign(window, { + ...(options.marker === false ? {} : { casaTookan: { isCasaTookan: true } }), + unisat: { + requestAccounts: async () => { + state.requests++ + if (state.reject) throw { code: 4001, message: 'User rejected connection' } + state.granted = true + sessionStorage.setItem('casa-grant', 'yes') + return [state.unsupported ? taproot : address] + }, + getAccounts: async () => state.granted ? [state.unsupported ? taproot : address] : [], + getPublicKey: async () => publicKey, + getNetwork: async () => 'livenet', + signMessage: async message => { state.signatures++; return window.signCasaProof(message) }, + signPsbt: async () => { state.psbts++; throw new Error('Unexpected transaction signing') }, + on: (event, fn) => { + if (!listeners.has(event)) listeners.set(event, new Set()) + listeners.get(event).add(fn) + }, + removeListener: (event, fn) => listeners.get(event)?.delete(fn), + }, + }) + }, + } + window.casaTest = state + if (!options.delay) state.inject() + if (options.competing) window.HorizonWalletProvider = { request: async () => { throw new Error('Wrong wallet selected') } } + }, { address, publicKey, taproot, options }) + for (const host of ['api.xcp.io', 'api.xcp.fun', 'mempool.space', 'api.digirare.com']) { + await page.route('https://' + host + '/**', route => route.fulfill({ status: 503, json: { error: 'API disabled in wallet connection test' } })) + } +} + +async function disconnect(page) { + if (await page.getByRole('button', { name: 'Desconectar', exact: true }).count() === 0) await account(page).click() + await page.getByRole('button', { name: 'Desconectar', exact: true }).click() +} + +test('pilot remains absent when disabled', async ({ page }) => { + test.skip(enabled) + await bridge(page) + await page.goto(route) + await connect(page).click() + await expect(page.getByText('Casa Tookan', { exact: true })).toHaveCount(0) + expect(await page.evaluate(() => window.casaTest.requests)).toBe(0) +}) + +test.describe('enabled pilot', () => { + test.skip(!enabled) + + test('connect verifies the proof, restores silently, and disconnects', async ({ page }) => { + await bridge(page) + await page.goto(route) + await connect(page).click() + await expect(account(page)).toBeVisible() + await expect.poll(() => page.evaluate(() => window.casaTest.signatures)).toBe(1) + + await page.reload() + await expect(account(page)).toBeVisible() + expect(await page.evaluate(() => window.casaTest.requests)).toBe(0) + await disconnect(page) + await expect(connect(page)).toBeVisible() + await page.reload() + await expect(connect(page)).toBeVisible() + expect(await page.evaluate(() => window.casaTest.psbts)).toBe(0) + }) + + test('rejection and unsupported accounts show an error and permit retry', async ({ page }) => { + await bridge(page) + await page.goto(route) + await page.evaluate(() => { window.casaTest.reject = true }) + await connect(page).click() + await expect(header(page).getByRole('alert')).toBeVisible() + await page.evaluate(() => { window.casaTest.reject = false; window.casaTest.unsupported = true }) + await connect(page).click() + await expect(header(page).getByRole('alert')).toContainText('Legacy or Native SegWit') + await page.evaluate(() => { window.casaTest.unsupported = false }) + await connect(page).click() + await expect(account(page)).toBeVisible() + await expect(header(page).getByRole('alert')).toHaveCount(0) + }) + + test('revocation clears both live and restored connections', async ({ page }) => { + await bridge(page) + await page.goto(route) + await connect(page).click() + await expect(account(page)).toBeVisible() + await page.evaluate(() => window.casaTest.revoke()) + await expect(connect(page)).toBeVisible() + await connect(page).click() + await expect(account(page)).toBeVisible() + await page.evaluate(() => sessionStorage.removeItem('casa-grant')) + await page.reload() + await expect(connect(page)).toBeVisible() + expect(await page.evaluate(() => window.casaTest.requests)).toBe(0) + }) + + test('chooser selects Casa when another wallet is installed', async ({ page }) => { + await bridge(page, { competing: true }) + await page.goto(route) + await connect(page).click() + await page.getByRole('listitem').filter({ hasText: 'Casa Tookan' }).getByRole('button', { name: 'Conectar', exact: true }).click() + await expect(account(page)).toBeVisible() + }) + + test('mobile connect discovers a late bridge without an install detour', async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }) + await bridge(page, { delay: true }) + await page.goto(route) + await expect(connect(page)).toBeVisible() + await page.evaluate(() => window.casaTest.inject()) + await connect(page).click() + await expect(account(page)).toBeVisible() + await expect(page.getByText('Solo en computadora, por ahora', { exact: true })).toHaveCount(0) + expect(await header(page).evaluate(el => el.scrollWidth <= window.innerWidth)).toBe(true) + await page.screenshot({ path: test.info().outputPath('mobile-connected.png') }) + await disconnect(page) + await expect(connect(page)).toBeVisible() + }) + + test('Unisat alone is not detected as Casa', async ({ page }) => { + await bridge(page, { marker: false }) + await page.goto(route) + await connect(page).click() + expect(await page.evaluate(() => window.casaTest.requests)).toBe(0) + await expect(page.getByRole('listitem').filter({ hasText: 'Casa Tookan' }).getByRole('button', { name: 'Conectar', exact: true })).toHaveCount(0) + }) +}) + +test('app metadata and missing paths preserve JSON/404 and CORS', async ({ request }) => { + const manifest = await request.get('/.well-known/wallet-app.json') + expect(manifest.status()).toBe(200) + expect(manifest.headers()['access-control-allow-origin']).toBe('*') + expect((await manifest.json()).wallet.version).toBe('1') + const missing = await request.get('/.well-known/missing-casa-test.json') + expect(missing.status()).toBe(404) + expect(missing.headers()['access-control-allow-origin']).toBe('*') +}) diff --git a/docs/casatookan.md b/docs/casatookan.md index 6f35ef4d..7da6b874 100644 --- a/docs/casatookan.md +++ b/docs/casatookan.md @@ -4,7 +4,7 @@ The app manifest is served at `/.well-known/wallet-app.json`. The PNGs under `/w The manifest makes this site discoverable when a user saves it in Casa Tookan. It does not register the site in a curated directory or enable new signing methods. Bitcoin support is optional because browsing needs no wallet. The splash uses Casa Tookan's automatic readiness timeout. -This change adds browsing metadata only. Existing connection and signing flows are unchanged. +Wallet connection support is an opt-in pilot; see below. ## Deployment check @@ -16,3 +16,21 @@ This change adds browsing metadata only. Existing connection and signing flows a Headers are configured in both Next (SSR/404) and Cloudflare Static Assets (public files). Production framing and CSP policies are unchanged; the development iframe bridge is not installed. Format: https://docs.casatookan.com/apps.html + +## Wallet connection pilot + +Build with `NEXT_PUBLIC_CASATOOKAN_ENABLED=true` to include Casa Tookan in the SDK chooser. It is off by default. The SDK is pinned to `25425b75142f832705d1c3611ab2cba2adc0f942` (XCP/wallet-sdk#13), including revoked-grant recovery and session-listener restart fixes. + +Mainnet Legacy and Native SegWit accounts are supported. Ordinary compose flows use the SDK PSBT fallback. Taproot, nested SegWit, selected-input signing, batches and non-ALL signatures are refused by the adapter. + +`npm run test:casatookan` exercises the actual site connection UI with a deterministic test bridge, including signed proofs where the site requests them, rejection/retry, unsupported accounts, chooser selection, mobile late injection, disconnect and revoked grants after reload. `CASATOOKAN_TEST_ENABLED=false npm run test:casatookan` checks the disabled pilot (set the environment variable in the syntax for your shell). Tests use an unfunded fixture and never broadcast. + +The SDK has separate live web-development-signer evidence in its PR. These site regressions are simulated-provider tests, not installed iOS/Android or funded-trade verification. Those checks remain required before enabling production. + +## Local QA — 2026-10-20 + +Seven enabled browser checks and two disabled-pilot checks passed on `/es/profile`. The desktop and mobile header connects, signs the proof, restores, rejects unsupported accounts and disconnects. Web typecheck, ESLint, numeric checks and all locale checks passed; 26 session/signature tests passed. All lockfiles pass `npm ci --dry-run --ignore-scripts`. + +The browser fixture signs deterministic test messages. Public API calls are blocked, so these runs do not prove live backend authentication or transaction composition. No production deployment or broadcast was performed. + +The local Next development server returns a self-redirect for the unprefixed English `/profile` route, on both `localhost` and `127.0.0.1`. `/es/profile` works. The wallet pilot does not change the locale proxy; investigate the English-route behavior before rollout and repeat the check on the deployed build. diff --git a/package-lock.json b/package-lock.json index 788374c5..2622fe8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "packages/*" ], "devDependencies": { + "@noble/curves": "1.9.7", + "@playwright/test": "1.63.0", "@scure/bip32": "^2.3.0", "esbuild": "0.28.2", "happy-dom": "20.14.0", @@ -42,7 +44,7 @@ "@noble/hashes": "^1.8.0", "@scure/base": "^2.2.0", "@scure/btc-signer": "^2.2.0", - "@xcp/wallet-sdk": "github:XCP/wallet-sdk#5a056ce83dc3a999f9384a7cb625094471d19c91", + "@xcp/wallet-sdk": "github:XCP/wallet-sdk#25425b75142f832705d1c3611ab2cba2adc0f942", "babel-plugin-react-compiler": "^1.0.0", "bignumber.js": "^9.3.1", "next": "16.3.4", @@ -3923,6 +3925,23 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@poppinss/colors": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", @@ -7809,8 +7828,8 @@ }, "node_modules/@xcp/wallet-sdk": { "version": "0.5.0", - "resolved": "git+ssh://git@github.com/XCP/wallet-sdk.git#5a056ce83dc3a999f9384a7cb625094471d19c91", - "integrity": "sha512-lDrfqg+AcJYaPt1QDEiYbMYVJRMIKoCvxnI5CB9/UNHGH21EiNzvh8X7vBgsdP+5JWAKVybl4jUOk+enOcuGBQ==", + "resolved": "git+ssh://git@github.com/XCP/wallet-sdk.git#25425b75142f832705d1c3611ab2cba2adc0f942", + "integrity": "sha512-MKs1qiFh3Kcz94B47UXRe6MdG8YukrMSUYTkVAh8RVfGFIHB6SBem8yS4ag2HNEoiByaslRIPHR201nMS76qKQ==", "license": "MIT", "dependencies": { "@noble/curves": "^1.9.7", @@ -13285,6 +13304,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", diff --git a/package.json b/package.json index cdde981c..95da419f 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,12 @@ "deploy:web": "npm run deploy --workspace apps/web", "deploy:api": "npm run deploy --workspace apps/api && npm run smoke", "migrate:api:local": "npm run migrate:local --workspace apps/api", - "migrate:api:remote": "npm run migrate:remote --workspace apps/api" + "migrate:api:remote": "npm run migrate:remote --workspace apps/api", + "test:casatookan": "playwright test -c playwright.casatookan.config.mjs" }, "devDependencies": { + "@noble/curves": "1.9.7", + "@playwright/test": "1.63.0", "@scure/bip32": "^2.3.0", "esbuild": "0.28.2", "happy-dom": "20.14.0", diff --git a/playwright.casatookan.config.mjs b/playwright.casatookan.config.mjs new file mode 100644 index 00000000..d8e164f0 --- /dev/null +++ b/playwright.casatookan.config.mjs @@ -0,0 +1,14 @@ +import { defineConfig } from '@playwright/test' + +export default defineConfig({ + testDir: './browser-tests/casatookan', + timeout: 60_000, + workers: 1, + use: { baseURL: 'http://127.0.0.1:3411', trace: 'retain-on-failure', hasTouch: true }, + webServer: { + command: 'npm run dev --workspace apps/web -- --hostname 127.0.0.1 --port 3411', + url: 'http://127.0.0.1:3411/.well-known/wallet-app.json', + timeout: 180_000, + env: { NEXT_PUBLIC_CASATOOKAN_ENABLED: process.env.CASATOOKAN_TEST_ENABLED ?? 'true' }, + }, +}) From 2500a5688df818382828b3fa234ed525d6a69e2c Mon Sep 17 00:00:00 2001 From: Dan Anderson Date: Tue, 20 Oct 2026 14:45:43 -0400 Subject: [PATCH 3/4] feat: adopt expanded Casa Tookan signing and Taproot proofs --- apps/web/package.json | 2 +- browser-tests/casatookan/connect.spec.mjs | 18 +++++++++++++----- docs/casatookan.md | 12 ++++++++++-- package-lock.json | 4 ++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 3d65ad7c..cfbcdc19 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -20,7 +20,7 @@ "@noble/hashes": "^1.8.0", "@scure/base": "^2.2.0", "@scure/btc-signer": "^2.2.0", - "@xcp/wallet-sdk": "github:XCP/wallet-sdk#25425b75142f832705d1c3611ab2cba2adc0f942", + "@xcp/wallet-sdk": "github:XCP/wallet-sdk#c04b34226bba26e8f9c8a757fa11aef31aab2bf3", "babel-plugin-react-compiler": "^1.0.0", "bignumber.js": "^9.3.1", "next": "16.3.4", diff --git a/browser-tests/casatookan/connect.spec.mjs b/browser-tests/casatookan/connect.spec.mjs index dbef3089..6a44e940 100644 --- a/browser-tests/casatookan/connect.spec.mjs +++ b/browser-tests/casatookan/connect.spec.mjs @@ -7,6 +7,7 @@ const enabled = process.env.CASATOOKAN_TEST_ENABLED !== 'false' const address = 'bc1qvux25709r4uw6rzc8wyl7wwecjdhrx085hm5ty' const publicKey = '0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967' const taproot = 'bc1pfqarxerx6v0zh6zcj2f3a3np8jselxn8vgefdx95c0n328qpzg4ssw6kv5' +const unsupported = '3N722gUQ2YmphkTzm9nsgD8rkxh1xvukYC' const header = page => page.getByRole('banner') const connect = page => header(page).getByRole('button', { name: 'Conectar', exact: true }).first() const account = page => header(page).getByText(/bc1q.*5ty/).filter({ visible: true }).first() @@ -16,7 +17,7 @@ async function bridge(page, options = {}) { const signature = secp256k1.sign(legacyMessageHash(message), new Uint8Array(32).fill(9)) return Buffer.from([31 + signature.recovery, ...signature.toCompactRawBytes()]).toString('base64') }) - await page.addInitScript(({ address, publicKey, taproot, options }) => { + await page.addInitScript(({ address, publicKey, taproot, unsupported, options }) => { const listeners = new Map() const state = { requests: 0, signatures: 0, psbts: 0, reject: false, unsupported: false, @@ -35,9 +36,9 @@ async function bridge(page, options = {}) { if (state.reject) throw { code: 4001, message: 'User rejected connection' } state.granted = true sessionStorage.setItem('casa-grant', 'yes') - return [state.unsupported ? taproot : address] + return [state.unsupported ? unsupported : options.taproot ? taproot : address] }, - getAccounts: async () => state.granted ? [state.unsupported ? taproot : address] : [], + getAccounts: async () => state.granted ? [state.unsupported ? unsupported : options.taproot ? taproot : address] : [], getPublicKey: async () => publicKey, getNetwork: async () => 'livenet', signMessage: async message => { state.signatures++; return window.signCasaProof(message) }, @@ -54,7 +55,7 @@ async function bridge(page, options = {}) { window.casaTest = state if (!options.delay) state.inject() if (options.competing) window.HorizonWalletProvider = { request: async () => { throw new Error('Wrong wallet selected') } } - }, { address, publicKey, taproot, options }) + }, { address, publicKey, taproot, unsupported, options }) for (const host of ['api.xcp.io', 'api.xcp.fun', 'mempool.space', 'api.digirare.com']) { await page.route('https://' + host + '/**', route => route.fulfill({ status: 503, json: { error: 'API disabled in wallet connection test' } })) } @@ -102,13 +103,20 @@ test.describe('enabled pilot', () => { await expect(header(page).getByRole('alert')).toBeVisible() await page.evaluate(() => { window.casaTest.reject = false; window.casaTest.unsupported = true }) await connect(page).click() - await expect(header(page).getByRole('alert')).toContainText('Legacy or Native SegWit') + await expect(header(page).getByRole('alert')).toContainText('Legacy, Native SegWit or BIP-86 Taproot') await page.evaluate(() => { window.casaTest.unsupported = false }) await connect(page).click() await expect(account(page)).toBeVisible() await expect(header(page).getByRole('alert')).toHaveCount(0) }) + test('Taproot connects with its declared proof dialect', async ({ page }) => { + await bridge(page, { taproot: true }) + await page.goto(route) + await connect(page).click() + await expect(page.getByText(/bc1p.*kv5/).filter({ visible: true }).first()).toBeVisible() + }) + test('revocation clears both live and restored connections', async ({ page }) => { await bridge(page) await page.goto(route) diff --git a/docs/casatookan.md b/docs/casatookan.md index 7da6b874..66861103 100644 --- a/docs/casatookan.md +++ b/docs/casatookan.md @@ -19,9 +19,9 @@ Format: https://docs.casatookan.com/apps.html ## Wallet connection pilot -Build with `NEXT_PUBLIC_CASATOOKAN_ENABLED=true` to include Casa Tookan in the SDK chooser. It is off by default. The SDK is pinned to `25425b75142f832705d1c3611ab2cba2adc0f942` (XCP/wallet-sdk#13), including revoked-grant recovery and session-listener restart fixes. +Build with `NEXT_PUBLIC_CASATOOKAN_ENABLED=true` to include Casa Tookan in the SDK chooser. It is off by default. The SDK is pinned to `c04b34226bba26e8f9c8a757fa11aef31aab2bf3` (XCP/wallet-sdk#13), including revoked-grant recovery and session-listener restart fixes. -Mainnet Legacy and Native SegWit accounts are supported. Ordinary compose flows use the SDK PSBT fallback. Taproot, nested SegWit, selected-input signing, batches and non-ALL signatures are refused by the adapter. +Mainnet Legacy, Native SegWit and BIP-86 Taproot accounts are supported. Ordinary compose flows use the SDK PSBT fallback. The adapter supports selected inputs, foreign signatures, non-ALL authorizations and one approval for up to 20 PSBTs. Every PSBT is limited to 64 KiB, with explicit prevouts. Nested SegWit, multisig and script-path commit/reveal remain unsupported. Casa does not validate application intents; existing site validation remains required. `npm run test:casatookan` exercises the actual site connection UI with a deterministic test bridge, including signed proofs where the site requests them, rejection/retry, unsupported accounts, chooser selection, mobile late injection, disconnect and revoked grants after reload. `CASATOOKAN_TEST_ENABLED=false npm run test:casatookan` checks the disabled pilot (set the environment variable in the syntax for your shell). Tests use an unfunded fixture and never broadcast. @@ -34,3 +34,11 @@ Seven enabled browser checks and two disabled-pilot checks passed on `/es/profil The browser fixture signs deterministic test messages. Public API calls are blocked, so these runs do not prove live backend authentication or transaction composition. No production deployment or broadcast was performed. The local Next development server returns a self-redirect for the unprefixed English `/profile` route, on both `localhost` and `127.0.0.1`. `/es/profile` works. The wallet pilot does not change the locale proxy; investigate the English-route behavior before rollout and repeat the check on the deployed build. + +## Expanded SDK support + +Pinned SDK `c04b34226bba26e8f9c8a757fa11aef31aab2bf3` passed 476 unit tests and a real dev-wallet check of Taproot proof signing, a shared PSBT v2 transaction, a reusable authorization and a twenty-transaction batch. Evidence is in XCP/wallet-sdk#13. These are unfunded signing checks; installed-phone lifecycle and funded settlement remain unverified. + +Expanded site browser suite: 8 passing checks, including Taproot connection, restore/revocation and mobile discovery. The browser suite uses a deterministic injected bridge; separate live-wallet evidence is recorded in the SDK PR. + +Web typecheck, ESLint, numeric checks and all locale checks pass; the pinned lockfile passes an npm ci dry run. diff --git a/package-lock.json b/package-lock.json index 2622fe8c..76268d86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "@noble/hashes": "^1.8.0", "@scure/base": "^2.2.0", "@scure/btc-signer": "^2.2.0", - "@xcp/wallet-sdk": "github:XCP/wallet-sdk#25425b75142f832705d1c3611ab2cba2adc0f942", + "@xcp/wallet-sdk": "github:XCP/wallet-sdk#c04b34226bba26e8f9c8a757fa11aef31aab2bf3", "babel-plugin-react-compiler": "^1.0.0", "bignumber.js": "^9.3.1", "next": "16.3.4", @@ -7828,7 +7828,7 @@ }, "node_modules/@xcp/wallet-sdk": { "version": "0.5.0", - "resolved": "git+ssh://git@github.com/XCP/wallet-sdk.git#25425b75142f832705d1c3611ab2cba2adc0f942", + "resolved": "git+ssh://git@github.com/XCP/wallet-sdk.git#c04b34226bba26e8f9c8a757fa11aef31aab2bf3", "integrity": "sha512-MKs1qiFh3Kcz94B47UXRe6MdG8YukrMSUYTkVAh8RVfGFIHB6SBem8yS4ag2HNEoiByaslRIPHR201nMS76qKQ==", "license": "MIT", "dependencies": { From dfe6d2da793289a69d32925f87d546a3efb7bbbe Mon Sep 17 00:00:00 2001 From: Dan Anderson Date: Tue, 20 Oct 2026 16:37:09 -0400 Subject: [PATCH 4/4] Gate inscription launches before upload for unsupported wallets --- apps/web/package.json | 2 +- apps/web/src/app/[lang]/create/page.tsx | 31 ++++++++++----- apps/web/src/locales/_source.json | 6 +++ apps/web/src/locales/es.json | 4 +- apps/web/src/locales/fr.json | 4 +- apps/web/src/locales/ja.json | 4 +- apps/web/src/locales/ko.json | 4 +- apps/web/src/locales/pt.json | 4 +- apps/web/src/locales/ru.json | 4 +- apps/web/src/locales/uk.json | 4 +- apps/web/src/locales/zh-hk.json | 4 +- apps/web/src/locales/zh-tw.json | 4 +- apps/web/src/locales/zh.json | 4 +- browser-tests/casatookan/connect.spec.mjs | 46 +++++++++++++++++++++++ docs/casatookan.md | 23 ++++++++++-- package-lock.json | 5 +-- 16 files changed, 126 insertions(+), 27 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index cfbcdc19..e12616d3 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -20,7 +20,7 @@ "@noble/hashes": "^1.8.0", "@scure/base": "^2.2.0", "@scure/btc-signer": "^2.2.0", - "@xcp/wallet-sdk": "github:XCP/wallet-sdk#c04b34226bba26e8f9c8a757fa11aef31aab2bf3", + "@xcp/wallet-sdk": "github:XCP/wallet-sdk#089ca36869ab72440292c3064143e7dc0ea8e75c", "babel-plugin-react-compiler": "^1.0.0", "bignumber.js": "^9.3.1", "next": "16.3.4", diff --git a/apps/web/src/app/[lang]/create/page.tsx b/apps/web/src/app/[lang]/create/page.tsx index bcfe8c2e..7ac61719 100644 --- a/apps/web/src/app/[lang]/create/page.tsx +++ b/apps/web/src/app/[lang]/create/page.tsx @@ -226,9 +226,13 @@ export default function CreatePage() { const num = useNumbers(); const t = useT(); const usd = useFiat(); - const { address, status: walletStatus, signPsbt, signCommitAndReveal, broadcastTransaction, session } = useWallet(); + const { address, status: walletStatus, signPsbt, signCommitAndReveal, broadcastTransaction, session, wallet: selectedWallet } = useWallet(); const compose = useCompose(); const isTaproot = address?.startsWith("bc1p") ?? false; + const { data: canInscribe = false } = useSWR( + walletStatus === "connected" && isTaproot ? ["inscription-support", selectedWallet, address] : null, + async () => (await session.getWallet()?.features())?.marketplaceBundles?.includes("commit-and-reveal") ?? false, + ); const [inscribe, setInscribe] = useState(false); const [inscribeStep, setInscribeStep] = useState(null); const [inscribeTxid, setInscribeTxid] = useState(null); @@ -347,6 +351,7 @@ export default function CreatePage() { (nameCheck === "available" || nameCheck === "owned") && image !== null && !imageTooBigToInscribe && + (!inscribe || canInscribe) && isValidX(xProfile) && isValidTelegram(telegram) && scheduleValid && @@ -363,6 +368,9 @@ export default function CreatePage() { setSubmitting(true); setUploadError(null); try { + if (inscribe && !(await session.getWallet()?.features())?.marketplaceBundles?.includes("commit-and-reveal")) { + throw new Error(t("Use a wallet that supports image inscriptions, or launch with a hosted image.")); + } // 1. Host image + metadata JSON (write-once) so the on-chain // description URL resolves from the first block. const form = new FormData(); @@ -403,12 +411,6 @@ export default function CreatePage() { : baseFeeRate; if (inscribe && isTaproot && address) { - // 3a. Commit/reveal inscription: the image is inscribed and the - // inscription output burned; the description is the JSON URL, - // as on a hosted launch. A wallet that signs a commit and its - // reveal in one approval gets both before either is broadcast. - const features = await session.getWallet()?.features().catch(() => null); - const bundles = features?.marketplaceBundles ?? []; const { revealTxid } = await inscribeLaunch({ asset: name, lpAsset: generateLpAssetName(), @@ -420,7 +422,7 @@ export default function CreatePage() { description: upload.json_url, address, signPsbt, - signCommitAndReveal: bundles.includes("commit-and-reveal") ? signCommitAndReveal : undefined, + signCommitAndReveal, broadcast: broadcastTransaction, onStep: setInscribeStep, }); @@ -472,7 +474,7 @@ export default function CreatePage() { const finishLaunch = async () => { const record = pendingReveal; - if (!record || submitting || walletStatus !== "connected") return; + if (!record || submitting || walletStatus !== "connected" || (!record.revealRawTx && !canInscribe)) return; setSubmitting(true); setResuming(true); setUploadError(null); @@ -605,7 +607,10 @@ export default function CreatePage() { { asset: pendingReveal.asset }, )}

- + {!pendingReveal.revealRawTx && !canInscribe && ( +

{t("Connect a wallet that supports this inscription reveal to finish the launch.")}

+ )} + {resuming && inscribeStep && inscribeStep !== "done" ? t(INSCRIBE_STEP_LABELS[inscribeStep]) : t("Finish launching {asset}", { asset: pendingReveal.asset })} @@ -738,6 +743,7 @@ export default function CreatePage() { setInscribe(e.target.checked)} className="mt-0.5" /> @@ -748,6 +754,11 @@ export default function CreatePage() { "The image itself becomes the permanent on-chain description (commit + reveal, two signatures, higher fees scale with image size; the inscription is burned so it belongs to the asset forever). Max 385 KB. Taproot wallets only.", )} + {!canInscribe && ( + + {t("Use a wallet that supports image inscriptions, or launch with a hosted image.")} + + )} {imageTooBigToInscribe && ( {t( diff --git a/apps/web/src/locales/_source.json b/apps/web/src/locales/_source.json index 6d8fdb22..8a7020a4 100644 --- a/apps/web/src/locales/_source.json +++ b/apps/web/src/locales/_source.json @@ -1355,6 +1355,9 @@ "components/connect-button.tsx", "components/header-wallet.tsx" ], + "Connect a wallet that supports this inscription reveal to finish the launch.": [ + "app/[lang]/create/page.tsx" + ], "Connect a wallet to chat.": [ "components/chat-panel.tsx" ], @@ -3866,6 +3869,9 @@ "Upload token image": [ "app/[lang]/create/page.tsx" ], + "Use a wallet that supports image inscriptions, or launch with a hosted image.": [ + "app/[lang]/create/page.tsx" + ], "Use at most {max} lines.": [ "components/chat-panel.tsx" ], diff --git a/apps/web/src/locales/es.json b/apps/web/src/locales/es.json index a3d00b4f..034407c0 100644 --- a/apps/web/src/locales/es.json +++ b/apps/web/src/locales/es.json @@ -1298,5 +1298,7 @@ "No expiry": "Sin vencimiento", "Burned": "Quemado", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "Incluye los tokens enviados a la dirección de quema y los tokens destruidos explícitamente.", - "Destroyed": "Destruidos" + "Destroyed": "Destruidos", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "Usa una billetera compatible con inscripciones de imágenes o lanza con una imagen alojada.", + "Connect a wallet that supports this inscription reveal to finish the launch.": "Conecta una billetera compatible con la revelación de esta inscripción para finalizar el lanzamiento." } diff --git a/apps/web/src/locales/fr.json b/apps/web/src/locales/fr.json index 39b6e956..8080d791 100644 --- a/apps/web/src/locales/fr.json +++ b/apps/web/src/locales/fr.json @@ -1298,5 +1298,7 @@ "No expiry": "Sans expiration", "Burned": "Brûlés", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "Comprend les tokens envoyés à l’adresse de destruction et les tokens explicitement détruits.", - "Destroyed": "Détruits" + "Destroyed": "Détruits", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "Utilisez un portefeuille compatible avec les inscriptions d’images, ou lancez avec une image hébergée.", + "Connect a wallet that supports this inscription reveal to finish the launch.": "Connectez un portefeuille compatible avec la révélation de cette inscription pour terminer le lancement." } diff --git a/apps/web/src/locales/ja.json b/apps/web/src/locales/ja.json index 6477d9db..9b378455 100644 --- a/apps/web/src/locales/ja.json +++ b/apps/web/src/locales/ja.json @@ -1298,5 +1298,7 @@ "No expiry": "有効期限なし", "Burned": "バーン済み", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "バーンアドレスに送信されたトークンと、明示的に破棄されたトークンを含みます。", - "Destroyed": "破棄済み" + "Destroyed": "破棄済み", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "画像のインスクリプションに対応したウォレットを使うか、ホストされた画像でローンチしてください。", + "Connect a wallet that supports this inscription reveal to finish the launch.": "このインスクリプションのリビールに対応したウォレットを接続して、ローンチを完了してください。" } diff --git a/apps/web/src/locales/ko.json b/apps/web/src/locales/ko.json index 53740397..d1abcd22 100644 --- a/apps/web/src/locales/ko.json +++ b/apps/web/src/locales/ko.json @@ -1298,5 +1298,7 @@ "No expiry": "만료 없음", "Burned": "소각됨", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "소각 주소로 전송된 토큰과 명시적으로 파기된 토큰을 포함합니다.", - "Destroyed": "파기됨" + "Destroyed": "파기됨", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "이미지 인스크립션을 지원하는 지갑을 사용하거나 호스팅된 이미지로 출시하세요.", + "Connect a wallet that supports this inscription reveal to finish the launch.": "출시를 완료하려면 이 인스크립션의 리빌을 지원하는 지갑을 연결하세요." } diff --git a/apps/web/src/locales/pt.json b/apps/web/src/locales/pt.json index e414123b..abe1bd8e 100644 --- a/apps/web/src/locales/pt.json +++ b/apps/web/src/locales/pt.json @@ -1298,5 +1298,7 @@ "No expiry": "Sem expiração", "Burned": "Queimados", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "Inclui tokens enviados ao endereço de queima e tokens explicitamente destruídos.", - "Destroyed": "Destruídos" + "Destroyed": "Destruídos", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "Use uma carteira compatível com inscrições de imagens ou lance com uma imagem hospedada.", + "Connect a wallet that supports this inscription reveal to finish the launch.": "Conecte uma carteira compatível com a revelação desta inscrição para concluir o lançamento." } diff --git a/apps/web/src/locales/ru.json b/apps/web/src/locales/ru.json index 3f560635..aabe80e1 100644 --- a/apps/web/src/locales/ru.json +++ b/apps/web/src/locales/ru.json @@ -1298,5 +1298,7 @@ "No expiry": "Бессрочно", "Burned": "Сожжено", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "Включает токены, отправленные на адрес сжигания, и токены, уничтоженные явно.", - "Destroyed": "Уничтожено" + "Destroyed": "Уничтожено", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "Используйте кошелёк с поддержкой записи изображений в блокчейн или запустите токен с изображением на сервере.", + "Connect a wallet that supports this inscription reveal to finish the launch.": "Подключите кошелёк с поддержкой раскрытия этой записи, чтобы завершить запуск." } diff --git a/apps/web/src/locales/uk.json b/apps/web/src/locales/uk.json index fb5577ef..2fdfe3df 100644 --- a/apps/web/src/locales/uk.json +++ b/apps/web/src/locales/uk.json @@ -1298,5 +1298,7 @@ "No expiry": "Безстроково", "Burned": "Спалено", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "Включає токени, надіслані на адресу спалювання, і токени, знищені явно.", - "Destroyed": "Знищено" + "Destroyed": "Знищено", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "Використовуйте гаманець із підтримкою запису зображень у блокчейн або запустіть токен із зображенням на сервері.", + "Connect a wallet that supports this inscription reveal to finish the launch.": "Підключіть гаманець із підтримкою розкриття цього запису, щоб завершити запуск." } diff --git a/apps/web/src/locales/zh-hk.json b/apps/web/src/locales/zh-hk.json index 2e6ef827..a378487d 100644 --- a/apps/web/src/locales/zh-hk.json +++ b/apps/web/src/locales/zh-hk.json @@ -1298,5 +1298,7 @@ "No expiry": "永不過期", "Burned": "已銷毀", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "包括傳送到銷毀地址的代幣及透過銷毀交易銷毀的代幣。", - "Destroyed": "已銷毀" + "Destroyed": "已銷毀", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "請使用支援圖片銘文的錢包,或使用託管圖片發行。", + "Connect a wallet that supports this inscription reveal to finish the launch.": "請連接支援此銘文揭示交易的錢包以完成發行。" } diff --git a/apps/web/src/locales/zh-tw.json b/apps/web/src/locales/zh-tw.json index 188c6165..0baded22 100644 --- a/apps/web/src/locales/zh-tw.json +++ b/apps/web/src/locales/zh-tw.json @@ -1298,5 +1298,7 @@ "No expiry": "永不過期", "Burned": "已銷毀", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "包括傳送到銷毀地址的代幣和透過銷毀交易銷毀的代幣。", - "Destroyed": "已銷毀" + "Destroyed": "已銷毀", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "請使用支援圖片銘文的錢包,或使用託管圖片發行。", + "Connect a wallet that supports this inscription reveal to finish the launch.": "請連接支援此銘文揭示交易的錢包以完成發行。" } diff --git a/apps/web/src/locales/zh.json b/apps/web/src/locales/zh.json index 8c16fc9b..8f3ba6d1 100644 --- a/apps/web/src/locales/zh.json +++ b/apps/web/src/locales/zh.json @@ -1298,5 +1298,7 @@ "No expiry": "永不过期", "Burned": "已销毁", "Includes tokens sent to the burn address and explicitly destroyed tokens.": "包括发送到销毁地址的代币和通过销毁交易销毁的代币。", - "Destroyed": "已销毁" + "Destroyed": "已销毁", + "Use a wallet that supports image inscriptions, or launch with a hosted image.": "请使用支持图片铭文的钱包,或使用托管图片发行。", + "Connect a wallet that supports this inscription reveal to finish the launch.": "请连接支持此铭文揭示交易的钱包以完成发行。" } diff --git a/browser-tests/casatookan/connect.spec.mjs b/browser-tests/casatookan/connect.spec.mjs index 6a44e940..69611ca2 100644 --- a/browser-tests/casatookan/connect.spec.mjs +++ b/browser-tests/casatookan/connect.spec.mjs @@ -173,3 +173,49 @@ test('app metadata and missing paths preserve JSON/404 and CORS', async ({ reque expect(missing.status()).toBe(404) expect(missing.headers()['access-control-allow-origin']).toBe('*') }) + +for (const width of [1280, 390]) { + test(`Taproot inscription is unavailable before upload or signing at ${width}px`, async ({ page }) => { + test.skip(!enabled) + await page.setViewportSize({ width, height: 900 }) + await bridge(page, { taproot: true }) + let uploads = 0 + await page.route('**/api/launches', route => { + if (route.request().method() === 'POST') uploads++ + return route.fulfill({ status: 503, json: {} }) + }) + await page.goto('/es/create') + await connect(page).click() + await expect(page.getByText(/bc1p.*kv5/).filter({ visible: true }).first()).toBeVisible() + await expect(page.getByRole('checkbox')).toBeDisabled() + await expect(page.getByText('Usa una billetera compatible con inscripciones de imágenes o lanza con una imagen alojada.')).toBeVisible() + await expect(page.locator('input[type=file]')).toBeAttached() + expect(uploads).toBe(0) + expect(await page.evaluate(() => window.casaTest.psbts)).toBe(0) + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true) + }) +} + +for (const signed of [false, true]) { + test(`keeps pending inscription and allows resume only for signed reveal (${signed})`, async ({ page }) => { + test.skip(!enabled) + await bridge(page, { taproot: true }) + const pending = { + version: 1, address: taproot, asset: 'TESTLAUNCH', startBlock: 999999, + revealScript: '51', tapInternalKey: '11'.repeat(32), commitTxid: '22'.repeat(32), + commitVout: 0, commitAmount: 10000, commitRawTx: '00', recipient: taproot, + feeRate: 1, createdAt: Date.now(), ...(signed ? { revealRawTx: '00' } : {}), + } + await page.addInitScript(record => localStorage.setItem('xcp.fun:pending-reveal:' + record.address, JSON.stringify(record)), pending) + await page.goto('/es/create') + await connect(page).click() + const finish = page.getByRole('button', { name: /TESTLAUNCH/ }) + if (signed) await expect(finish).toBeEnabled() + else { + await expect(finish).toBeDisabled() + await expect(page.getByText('Conecta una billetera compatible con la revelación de esta inscripción para finalizar el lanzamiento.')).toBeVisible() + } + expect(await page.evaluate(address => JSON.parse(localStorage.getItem('xcp.fun:pending-reveal:' + address)), taproot)).toEqual(pending) + expect(await page.evaluate(() => window.casaTest.psbts)).toBe(0) + }) +} diff --git a/docs/casatookan.md b/docs/casatookan.md index 66861103..ecf018eb 100644 --- a/docs/casatookan.md +++ b/docs/casatookan.md @@ -19,7 +19,7 @@ Format: https://docs.casatookan.com/apps.html ## Wallet connection pilot -Build with `NEXT_PUBLIC_CASATOOKAN_ENABLED=true` to include Casa Tookan in the SDK chooser. It is off by default. The SDK is pinned to `c04b34226bba26e8f9c8a757fa11aef31aab2bf3` (XCP/wallet-sdk#13), including revoked-grant recovery and session-listener restart fixes. +Build with `NEXT_PUBLIC_CASATOOKAN_ENABLED=true` to include Casa Tookan in the SDK chooser. It is off by default. The SDK is pinned to `089ca36869ab72440292c3064143e7dc0ea8e75c` (XCP/wallet-sdk#13), including revoked-grant recovery and session-listener restart fixes. Mainnet Legacy, Native SegWit and BIP-86 Taproot accounts are supported. Ordinary compose flows use the SDK PSBT fallback. The adapter supports selected inputs, foreign signatures, non-ALL authorizations and one approval for up to 20 PSBTs. Every PSBT is limited to 64 KiB, with explicit prevouts. Nested SegWit, multisig and script-path commit/reveal remain unsupported. Casa does not validate application intents; existing site validation remains required. @@ -33,12 +33,29 @@ Seven enabled browser checks and two disabled-pilot checks passed on `/es/profil The browser fixture signs deterministic test messages. Public API calls are blocked, so these runs do not prove live backend authentication or transaction composition. No production deployment or broadcast was performed. -The local Next development server returns a self-redirect for the unprefixed English `/profile` route, on both `localhost` and `127.0.0.1`. `/es/profile` works. The wallet pilot does not change the locale proxy; investigate the English-route behavior before rollout and repeat the check on the deployed build. +An earlier local development run self-redirected `/profile`. Production checks subsequently returned `/profile` 200 (English), `/en/profile` 308 to `/profile`, and `/es/profile` 200 (Spanish). No production routing defect is established and this PR does not change the locale proxy. ## Expanded SDK support -Pinned SDK `c04b34226bba26e8f9c8a757fa11aef31aab2bf3` passed 476 unit tests and a real dev-wallet check of Taproot proof signing, a shared PSBT v2 transaction, a reusable authorization and a twenty-transaction batch. Evidence is in XCP/wallet-sdk#13. These are unfunded signing checks; installed-phone lifecycle and funded settlement remain unverified. +Pinned SDK `089ca36869ab72440292c3064143e7dc0ea8e75c` passed 481 unit tests and a real dev-wallet check of Taproot proof signing, a shared PSBT v2 transaction, a reusable authorization and a twenty-transaction batch. Evidence is in XCP/wallet-sdk#13. These are unfunded signing checks; installed-phone lifecycle and funded settlement remain unverified. Expanded site browser suite: 8 passing checks, including Taproot connection, restore/revocation and mobile discovery. The browser suite uses a deterministic injected bridge; separate live-wallet evidence is recorded in the SDK PR. Web typecheck, ESLint, numeric checks and all locale checks pass; the pinned lockfile passes an npm ci dry run. + +## Inscription boundary + +The real Casa signer rejects both script-path variants and tree-bearing key-path +recovery. The create page requires the wallet's `commit-and-reveal` capability +before allowing a new inscription, and rechecks before uploading metadata. +Casa users can launch normally with a hosted image. A selected inscription is +not silently converted into a hosted launch on wallet change. + +Pending launches remain stored. An unsigned reveal requires a capable wallet; +an already-signed reveal can still be resumed without asking Casa to sign it. + +Inscription follow-up validation: 34 safety/recovery unit tests and four new +browser tests pass against the updated SDK. Browser coverage includes desktop +and mobile restrictions before upload/signing, retained unsigned reveals, and +resuming already-signed reveals. Web typecheck, ESLint, numeric checks, all ten +locale checks, and the lockfile dry run pass. diff --git a/package-lock.json b/package-lock.json index 76268d86..3fb55ac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "@noble/hashes": "^1.8.0", "@scure/base": "^2.2.0", "@scure/btc-signer": "^2.2.0", - "@xcp/wallet-sdk": "github:XCP/wallet-sdk#c04b34226bba26e8f9c8a757fa11aef31aab2bf3", + "@xcp/wallet-sdk": "github:XCP/wallet-sdk#089ca36869ab72440292c3064143e7dc0ea8e75c", "babel-plugin-react-compiler": "^1.0.0", "bignumber.js": "^9.3.1", "next": "16.3.4", @@ -7828,8 +7828,7 @@ }, "node_modules/@xcp/wallet-sdk": { "version": "0.5.0", - "resolved": "git+ssh://git@github.com/XCP/wallet-sdk.git#c04b34226bba26e8f9c8a757fa11aef31aab2bf3", - "integrity": "sha512-MKs1qiFh3Kcz94B47UXRe6MdG8YukrMSUYTkVAh8RVfGFIHB6SBem8yS4ag2HNEoiByaslRIPHR201nMS76qKQ==", + "resolved": "git+ssh://git@github.com/XCP/wallet-sdk.git#089ca36869ab72440292c3064143e7dc0ea8e75c", "license": "MIT", "dependencies": { "@noble/curves": "^1.9.7",