Repository navigation
Releases
v0.27.3
This release is about reliability in the places people use every day: chat, getting BCH, P2P Ramp, WalletConnect, and wallet restore. Conversations can now be muted and group messages appear the moment you send them, the SideShift swap flow is part of the Get BCH page, and WalletConnect now handles covenant spends, hex-encoded transactions and broadcast: false correctly. It also closes a stored-XSS path in marketplace rendering and removes a large slice of unused and vulnerable dependencies. iOS build 656, browser extension v0.27.5.
✨ New Features
-
Chat mute, instant MLS sends, and push-token filtering (PR #830, #834, #842) — mute is a per-wallet setting mirrored to the server as
room.muted, so it follows the account across devices.- Muting is available from the conversation menu; muted rooms show an icon in the list.
- Archive and delete imply mute, and both write it in the same server update so the states cannot diverge.
- Web/PWA notifications are held until
pullLatestForPushconfirms an unmuted room actually gained activity; a failed pull still notifies rather than swallowing the message. - Outbound NIP-17 gift-wraps now carry an opaque, server-issued room push token, added before signing, so the relay can drop pushes for a muted DM or legacy group without affecting other members.
- MLS sends commit a
pendingbubble immediately and swap it for the signed event when the send resolves — no more waiting on the pre-send ratchet catch-up (up to 2.5 s) or on every relay (30 s cap). A rejection turns the bubble into a failed message with the standard retry affordance, and pending placeholders are stripped from the persisted tail so a kill mid-send cannot restore a ghost message. - Avatars are cached by URL, the room list paints from cache, and room seeding reads server-mirrored MLS roles so rooms created on another device keep their owner/admin roles.
-
Get BCH now includes crypto swap (PR #833, #842) — the standalone Crypto Swap app is gone; its SideShift flow lives inside Get BCH.
- The page opens an option chooser: convert other cryptocurrencies to BCH via SideShift, or buy BCH from sellers in the P2P Ramp. The P2P leg returns to the same chooser on back.
- The swap form is rebuilt as a send/receive pair with coin name plus chain, a direction-switch arrow, and the received amount with the rate underneath. Search matches coin names as well as tickers.
- Typed amounts, received amounts, rates, min/max errors, confirmation and deposit-screen amounts now use
formatWithLocale, so grouping and the decimal separator follow the user's country. - The receiving-address field is removed because it is always the wallet's current BCH receiving address; the refund address stays. The address is still filled for the shift request.
- Swap history and shift details are available in a History tab. The option is also visible on iOS, where the old Crypto Swap app was hidden.
-
P2P Ramp: order status attribution (PR #829) — status history now shows who performed each change: the counterparty, the arbiter or the system, with the role next to the name and the current user rendered as "You". The arbiter view uses the same member lookup. Events the backend does not attribute render no label instead of a client-side guess.
🔒 Security
-
Stored XSS in marketplace rendering (PR #817) — merchant-controlled storefront and product names were interpolated into HTML and rendered with
v-htmlinside the wallet WebView, which holds keys and signing. A merchant could set a name like<img src=x onerror=…>and execute script in the wallet.MarketplaceSearch.vuenow splits the search into before/match/after segments rendered as text nodes, with the match wrapped in<strong>.- The cashback
merchantNameis escaped before interpolation, andv-bch-amountbuilds its result withcreateTextNodeinstead of round-trippingtextContentthroughinnerHTML. - Backend-supplied ramp/swap SVG icons are rendered through
<img src="data:image/svg+xml;base64,…">, a non-scripted image context, instead ofv-html.javascript:,vbscript:,data:text/htmland non-SVG markup are rejected. - Verified in headless Chromium: hostile SVG does not fire
onerrorthrough the image sink and does fire through the oldv-htmlpath. The commerce-hub backend does not sanitize these fields, so the client fix is load-bearing.
-
Dependency and attack-surface reduction —
- axios
0.21.4→0.34.0closes 23 advisories, including CVE-2023-45857 (XSRF token sent cross-origin), CVE-2025-27152 (absolute URL overridingbaseURL) and CVE-2026-25639 (__proto__DoS inmergeConfig).watchtower-cash-jsis deduped onto the same single copy, and the cache-key regression introduced by axios 0.32's null-prototypeparams/dataobjects is fixed so cached GETs cannot return another storefront's data (PR #824). - The Electron desktop mode is removed, including its
nodeIntegration: truesetting; nothing shipped used it (PR #823). - The WalletConnect v1 client and four other unused dependencies are removed (PR #820).
- Undeclared
pbkdf2andcreate-hmacdependencies in gift encryption and Connecta are replaced with the already-shipped@noble/hashes; not one of 5,000 existing gift shards failed to decrypt.ethersis removed and seed-phrase import validation now uses bip39 (PR #808).
- axios
-
No more Google connectivity pings (PR #822) —
v-offlineloadedhttps://google.com/favicon.icoon launch and on every connectivity event, telling Google when people open the wallet. Connectivity now followsnavigator.onLineand the browser'sonline/offlineevents, which also stops false "No internet" banners and skipped balance refreshes where Google is blocked. -
Restored wallets resume the last used address index (PR #821) — importing a seed phrase previously hardcoded
lastAddressIndex: 0, so the receive screen handed out addresses that already had history. Restore now asks the server for the last used index per wallet (exclude_pos=true), derives and stores the pair at that index, and continues atindex + 1.
🐛 Bug Fixes
-
Wallet restore no longer hangs (PR #821) — a stalled watchtower
subscribe(no timeout inwatchtower-cash-js) could leave the restore overlay stuck forever.- Address derivation is now local; address registration is best-effort and non-fatal. The restore step is bounded by a timeout,
walletRestoreInProgressis always cleared, and a visible error with Retry/Back replaces the blank screen. - The restore settings Continue button is disabled during country/language/currency auto-detection, with a 60 s timeout that re-enables it on failure so the user cannot be trapped.
- Address ranges are re-registered in batches of 50, and the old range subscription is not awaited.
- Address derivation is now local; address registration is best-effort and non-fatal. The restore step is bounded by a timeout,
-
Receive on chipnet (PR #827) — a missing or partial
address-discoverresponse no longer aborts the Receive page.- Failed candidate indices are retried up to twice with 500 ms/1000 ms backoff, network/timeout errors retry the request, and each attempt has a 15 s timeout.
- On chipnet, when verification is still unavailable, the page falls back to
last-address-index + 1;{"address": null}and numeric-string indices are handled. - Toasts now show the actual failure reason. Mainnet remains fail-closed: an unverified address is never shown.
-
Push notifications (PR #825) — the app subscribed only the active wallet and still sent the deprecated
multiWalletIndexargument.- All vault wallets are now subscribed with deduplicated BCH/SLP hashes and
prune_unlisted: true, so wallets deleted from the vault stop receiving pushes. - Create/import remain additive and never prune. An empty hash list returns early, so a broken vault cannot unlink every device.
- Subscribing no longer depends on
loadWallet()resolving the mnemonic.
- All vault wallets are now subscribed with deduplicated BCH/SLP hashes and
-
Chat correctness (PR #830, #834, #837)
- Members can leave MLS groups: the leave is published as an encrypted request and committed by the remaining member with the lowest MLS leaf index. Leaves are addressed by tree index, not compacted position, so the right member is removed and sender/owner checks stay correct.
- Group-data admin public keys are decoded as UTF-8 text, not hex. Joined groups previously stored a double-encoded 128-character owner pubkey that matched no member, silently disabling role enforcement and role broadcasts; malformed owners are backfilled from tree leaf 0.
- Self DMs are refused at every entry point; reacted rooms move to the top; room ordering follows stored messages; message deletion completes.
- Async chat work is scoped to the wallet that started it, so switching wallets cannot leak state between them.
- "Change photo" and "Remove photo" work again; the avatar menu compares against its rendered content element instead of the teleport anchor, so menu clicks no longer count as outside clicks and fall through to the copy handler.
- MLS group pushes no longer notify the sender: group events carry a plaintext
sendertag so the relay can drop the sender from the recipient list.
-
WalletConnect (PR #835, #840, #841)
- CashScript covenant spends whose function takes data arguments but no
signow pass through untouched — the ParyonUSDborrow(bytes2, bytes5)case. The wallet only insists on a signature when the ABI declares asiginput;datasigand argless spends also pass through. The wallet still signs every P2PKH input it owns, and redeem-script/P2SH32 hash validation is unchanged. bch_signTransactionhonorsbroadcast: falseand returns only the signed transaction and hash. Omitted ortruestill broadcasts, matching WizardConnect.- Hex-encoded transactions are accepted in
bch_signTransaction, decoded before input/output enrichment so the approval dialog shows real addresses and amo...
- CashScript covenant spends whose function takes data arguments but no
v0.27.2
This release brings active-order live activities, a rebuilt QR scanner, a large batch of MLS group chat fixes, and a security fix that stops the wallet from ever reusing an address whose public key was already published on chain. Three dependency migrations replace @psf/bch-js and mainnet-js with the libauth and CashScript stacks the app already ships. It also makes storage upgrades non-destructive: the browser extension, which ships as v0.27.4, verifies that every stored secret is still decryptable before any migration is allowed to write, and leaves the mobile app's keystore completely untouched.
✨ New Features
-
Order Live Activities (PR #797, #804) — Active P2P Ramp and Marketplace orders are now tracked on the iOS lock screen (ActivityKit) and in the Android notification shade (ongoing notification), one activity per order.
- Shared
OrderLiveActivitypayload for both platforms with status stages, countdowns and deep links (paytaca://live-activity). - The lock-screen payload is masked: order type, status and countdown only — no amounts or counterparty names.
- iOS widget is fully branded (Paytaca mark, blue→red gradient wordmark, segmented step track, matching Dynamic Island expanded/compact/minimal layouts).
- Marketplace counts down to
auto_complete_atonly, the single deadline the backend actually enforces. PaytacaMessagingServiceintercepts live-activity FCM data messages natively so they never leak into the JS push pipeline; activities reconcile on app resume and wallet change.
- Shared
-
QR Scanner on ML Kit (PR #798) — Native scanning migrated from
@capacitor-community/barcode-scannerto@capacitor-mlkit/barcode-scanning, which decodes across the whole camera preview instead of a fixed center framing rectangle.- Fixes QR codes failing to scan unless physically centered on the device.
- Zoom reworked to a 1.0–4.0
setZoomRatiorange and torch state now followsisTorchAvailable/isTorchEnabled. - Camera permission flow now offers "Open Settings" when permission is persistently denied (new i18n keys across all locales).
- Animated QR: decode errors are surfaced, repeated frames deduped, the scanner fully stops before static decodes, and a processing guard prevents duplicate navigation. Per-request QR density and animation speed selectors added.
PstQrDialognow emits a BCR-2020-006-compliantcrypto-psbtUR (untagged CBOR byte string), and the reader unwrapscrypto-psbtCBOR without a silent decode fallback.- Adds single-sig / read-only PSBT scanning and read-only BSMS descriptor scanning from the home screen.
- Fixed hex-string signatures failing verification, which had broken proposal UR QR import.
-
MLS Group Push Notifications (PR #802) — The MLS group id is now registered with watchtower so group messages can be pushed while the app is backgrounded (paired backend change: watchtower-cash #502).
🔒 Security
- Spent addresses are never reused (PR #805) — Spending from a P2PKH address reveals its public key on chain; funds sent to it afterwards are exposed to key recovery. Receiving and change addresses are now treated as one pair per index, and a spend advances the pair so neither side is ever reused.
- The receive page only displays an address positively confirmed unused (via
address-discover), and its fallback path now resolves through the same verifiedrefreshDynamicAddress()instead of deriving straight from the stored index. - The freshness check now sends the real wallet hash instead of a placeholder, so addresses can never be attributed to a sentinel wallet.
getChangeAddress()insend-page-utils.jsis now the single implementation: it scans the change chain for the first index with no on-chain history, subscribes it to watchtower, advances the cursor before broadcast, and throws if freshness cannot be confirmed. Change selection is serialized per wallet and network to stop concurrent spends from colliding on one index.- Seven paths that previously passed no change address now pass a verified one (
createFundingUtxo,consolidateUtxos,CashInDialog,RampDepositInfo,EscrowTransfer,create-gift, gift resubmit). Escrow funding and subscription top-ups no longer hardcode1/0change. - JPP payments now fail with
Missing change addressinstead of guessing index 0. - The address cursor can no longer move backwards, and the "Auto generate address" caption now states why.
- The receive page only displays an address positively confirmed unused (via
🐛 Bug Fixes
-
Wallet reset / onboarding loop (PR #813) — Some web and browser-extension users on v0.27.x were dropped onto the create/import screen as if they had no wallet. The exact trigger could not be reproduced, so the fix is defensive along every path that could produce the symptom:
SECURE_STORAGE_KEY_MISSINGis now recognised as a decrypt failure instead of being swallowed as "mnemonic missing".- The router guard and its
catchblock no longer callnext('/accounts')while any recoverable vault entry exists; they show the "Wallet data unreadable" dialog instead, and stay on the current route if that dialog cannot be rendered. ensureValidWalletIndex()andcleanupNullAndDeletedWallets()no longer clear the vault when entries exist but none has awalletHash.- A one-time
recoverWalletsFromMnemonicKeys()pass rebuilds vault entries fromcap_sec_mn_<walletHash>secure-storage keys after verifyingsha256(mnemonicHash + derivationPathHash). deleteWallet()tombstones the wallet hash, and tombstones are cleared for wallets restored into the vault, so a deleted wallet is never silently resurrected or re-deleted on next boot.
-
Non-destructive storage upgrades — Users upgrading from v0.26.x could lose access to their wallet if the encrypted secure-storage values were no longer decryptable with the current IndexedDB AES key: the migration would re-key, scrub or drop entries it could not read. A read-only preflight now runs before any migration:
- On web and in the browser extension, every
enc:v1:value is decrypted against the current key store before anything is re-keyed, re-flagged, scrubbed or removed. If any value is unreadable, the upgrade stops with storage exactly as it was and shows the restore-from-seed prompt instead of silently producing an empty wallet. SECURE_STORAGE_DECRYPT_FAILEDandSECURE_STORAGE_KEY_MISSINGare treated as fatal migration errors instead of "no mnemonic at this index", andmnemonic_migration_completedis only written after every attempt verified.- Vault entries are rebuilt from legacy index keys and
mn_<walletHash>keys, and a legacy mnemonic is re-keyed only after the wallet hash is verified. - The preflight reads
localStorageand the IndexedDB key store directly, bypassing the plugin'sget()(which lazily re-encrypts plaintext), so it can never disturb storage itself. - Android and iOS behaviour is unchanged from v0.27.2 — the new logic is gated to web/BEX and adds no secure-storage writes or removals on native.
- On web and in the browser extension, every
-
MLS group chat correctness (PR #802)
- Incoming group messages filed under a dead room no longer disappear.
- Fixed the add-member flow and prevented duplicate groups on repeated Create Group taps.
- Group replies now render on the recipient side, and editing a group message updates it in place instead of sending a duplicate.
- The encrypted event is reused on publish retry, so retries no longer burn a ratchet generation and render as invisible sends.
- Invites are deduped and already-delivered events are not re-processed; invitees stay under Invited until they accept, and non-managers get an explanation for why they cannot add members.
- Fixed the reproducible "Desired gen in the past" epoch desync with per-group processed event ids plus a bounded
resyncMlsGrouprecovery path. room.membersnow mirrors the MLS tree on every applied commit, fixing NIP-17 reaction fan-out to a stale member list, with a one-time heal loop for existing rooms.- Cross-device ratchet collisions: a sibling device's group events are replayed before encrypting.
- Private DMs no longer land in MLS rooms.
- The MLS own-send cache is scoped per wallet (
pubkey:eventid), no longer leaking between wallets. - Our own kind-445 sends and Add/Remove commits are tombstoned once applied, so a late relay re-delivery cannot consume the resync budget.
-
Chat pages no longer freeze (PR #802) —
vuex-persistedstatewas serializing the whole store on every mutation, twice. A ~600-mutation relay replay blocked the main thread for 3.7–6.4 s. Now only the last 30 messages per room are persisted (older history re-fetched and merged by id), writes are debounced (flushed onpagehide/beforeunload/visibilitychange), and the duplicatereduceris gone. AnewestMessageAtwatermark keeps the relaysincefilter correct, with a one-time migration scan. Measured: 600 mutations → 1–2 writes; 6,000 messages / 3.33 MB → 341 KB persisted. -
Chat unread count — Pending group invitations are now included in the total unread badge, so an invite is no longer invisible until the first message arrives.
-
WalletConnect contract spends (PR #803) — Cauldron swaps no longer fail with "no signature placeholder found in the unlocking bytecode". Argless contract spends whose ABI declares no
sig/datasiginput (Cauldron's poolexchangebranch) are now passed through untouched, while templates that expect a signature but supply no placeholder are still rejected. -
Multisig PSBT send page — Fixed the amount-vs-balance comparison that could block sends, and single-sig PSBT classification no longer misreads P2WSH multisig as single-sig. Added a sighash getter and handling for a single BIP32 derivation keypair.
-
i18n — Tightened copy and stopped CSS from altering label casing.
🧩 Browser Extension (v0.27.4)
The browser ext...
v0.27.1
Release Notes
✨ New Features
- Chat Invite Links — Share or copy an invite link from the chat profile page (
chat.paytaca.com/chatme?id=<npub>). Opening the link deep-links into Paytaca and routes to the chat app with that contact preloaded; the link host is registered in the Android autoVerify intent filter and both iOS applinks entitlements. Annpubquery arriving while the chat page is already open is handled without remounting.
🐛 Bug Fixes
-
Rewards — Request Failures (PR #794) — Dropped or failed engagement-hub requests no longer break the rewards page or render silent zeros:
- Guard
getAddress0_0PublicKeyin the rewards pagemounted()so a failed key derivation stops skipping engagement-hub calls, and retry it viarecordUserPromoPubKeyso a failed derivation still recordsaddress_0_public_keyonuserpromo. - Add retry and wallet-hash guards to
getUserPromoDataandcreateUserPromoDataso a missing readableuserpromorecord surfaces aPointsLoadErrorerror card instead of fake point balances. - Make
RequestManager.getDataalways resolve and stop retrying cancelled requests, so request aborts don't leave dead error cards behind. - Rework the abort-manager
attachTopatch to wrap the axios adapter soskipAbortManageractually reaches the XHR-abort path. - Guard
fetchRewardsSwapContractDetailsagainst emptygetRewardsSwapContractDetailsresponses so redeem points stops reading undefined bytecode.
- Guard
-
Rewards — Stuck Loading Skeleton (PR #795) —
loadRewards()now clearsisLoadingin afinally, so closing the referral code dialog no longer leaves the rewards home stuck on skeleton loaders. The error card's retry path had the same latent hang and is fixed by the same change. -
Chat Invite Links on iOS — Fixed iOS opening
chat.paytaca.com/chatmeinvites on the send page ("Unable to identify QR code"): iOS opens the invite aspaytaca://chat.paytaca.com/chatme?..., which matched the payment-URI branch; the deep-link handler now checks the chat host before thebitcoincash:/ethereum:/paytaca:branches.
🛠 Improvements
- Release Tooling — Added
scripts/generate-release.mjs(headless-Chrome renderer for the release-notes HTML sources) and the v0.27.0 release-notes sources/screenshots;public/releasesscreenshots are now excluded from Capacitor and browser-extension builds so they don't ship inside the app bundle.
v0.27.0
Release Notes
✨ New Features
- MLS Private Group Chat — End-to-end encrypted group messaging built on MLS (RFC 9420) over the Nostr NIP-EE transport (#762, #768). Create/invite/join flows with member limits and a DM-or-group picker, admin roles and ownership transfer, group file sharing as a single encrypted message, group tips with a recipient picker, deterministic KeyPackages, KeyPackage publish resilience and fresh-device rebuild, wallet-namespaced state, and relay catch-up on join.
- Payment Hub — Subscriptions & Recurring Payments — CashToken subscriptions with plans payable in BCH and CashTokens in a single send, custom subscription JSON forms (form builder + preview), subscription top-up and cancellation refactors, and a recurring-payments app (experimental) with websocket real-time updates (#773, #779).
- NFC Payments — Improvements to NFC payment setup and QR scanning flow (#761).
- Rewards & Referrals — Deferred referral code applied during wallet creation (rewards deep links redirect to the wallet-creation rewards step with the code prefilled), promo contract v2 with versioned artifacts, and reservation public-key recording improvements (#766, #775).
- Home — Open asset details directly from BCH and token cards, plus View All and Swap links in the tokens section (#785, #787).
- Send Flow — Swipeable recipient cards with header action buttons, full recipient address display with actions tucked into a menu, and POS
price_idused for BIP21 fiat amounts (#786). Replaced the advanced-options sheet with a "Pay with" selector and added a merchant banner ("You are paying to", verified badge with tooltip) plus an inline Cauldron-spend row when swapping tokens for BCH (#793). - Cauldron DEX — Redesigned swap form with From/To panels and dual-amount editing with a universal keyboard, platform fee capped at 1 USD worth of BCH, and a token market carousel in token details whose Swap action opens in sell (token → BCH) mode (#770, #787, #788).
- Get Help — Replaced the Help Center with Telegram support and revamped the Support page with blocking scan dialogs (#764).
- P2P Ramp — Group payment methods by currency with an add button (#783).
- Debug App — Secure Storage Migration Tool — Inspect the legacy→native secure-storage migration side by side (legacy
cap_sec_*entries and native keystore keys, migration-flag status, masked values) with a confirmed, web-safe "clear legacy storage" action (#790).
🐛 Bug Fixes
- Wallet — Guard against null vault entries when loading the wallet list (crash reading
wallet.deleted); boot-time cleanup no longer deletes wallets it cannot decrypt (it surfaces a recovery dialog instead) (#789); iOS build version bumps (through 626). - Asset Info — Show the USD-denominated 24h change to match Cauldron.
- Push Notifications — Switch wallet on notification when the current hash is unknown / cold-start, recover notification from the stash when cleared during app lock, and correct deep-link routing (#765, #767).
- Arbiter — Fixed appeal page auth and chat session creation (#781).
- Rewards — Stop orphaning promos, null-safe
createUserRewardsData(), fixrpContractused-before-initialization, and correct PromoContract initialization/version guards (#774, #766). - LIFT / Reservations — Harden reservation public-key sync (no stuck loading skeleton), stop materializing the private-key WIF, await and persist the public key before confirming, granular error messages with support reference codes, and
ConfirmReservationNotReadyacross all locales (#780, direct). - Chat — Prevent composer caret being killed by
removeAllRangeson root click; don't drop read receipts during an in-flight batch; stop the global.action-labelleak breaking themed action buttons; keep the context menu anchored on mobile. - Home — Stop Pending-section and transaction-skeleton flicker, keep home transactions in sync with fresh data, and dismiss the pull-to-refresh spinner before slow background tasks.
- QR — Restore QR rendering in shrink-to-fit inline-block wrappers and on the wallet-backup/shards page; re-prompt camera permission when opening the scanner.
- P2P Ramp — Stop back-button looping/cycling, fix proof-of-payment upload on Android, and fix payment-methods title overlapping the header nav (#783).
- Cauldron — Remove the lingering slide-to-swap panel and reserved bottom padding on the success screen; prevent the "Select token" prompt flash on load (#787); dismiss the custom keyboard on Android back press (#778).
- Send — Keep the amount field focus highlight inside its bounds and tied to the keyboard target; swap-amount inputs no longer trigger the keyboard; POS
price_idfiat handling (#786); no custom keyboard when paying token sends via a BCH swap; Cauldron swap restricted to a single recipient; polish recipient empty state and balance spacing (#793). - WizardConnect / WalletConnect — Sign P2PKH inputs with SIGHASH_UTXOS, honor
broadcast:false, refuse to return unsigned contract inputs, and keep relayer errors from crashing the app (#784). - Android — Dismiss the custom keyboard on back press; keep the numeric keypad above the system navigation bar.
- UI — Anchor bottom sheets to the screen edge on iOS; fix token history icons and token gain/loss; P2P footer scroll.
🔒 Security
- MLS — Scrub the Nostr exporter secret after sealing NIP-EE group events; stop persisting sensitive boot state; remove dead/legacy flows.
- Wallet — Remove private-key WIF logging and stop materializing the WIF when syncing reservation public keys; guard against null vault entries.
- Secure storage — Migrate secrets from the WebView-encrypted local storage into the native OS keystore, fail loud (
SECURE_STORAGE_KEY_MISSING) instead of silently minting a new key when encrypted data is present, and never delete legacy data during migration (#789). Remove obsolete global OAuth keys once per-wallet tokens are written (#790). Remove the seed escrow (Tier-2) that mirrored recovery material to OS cloud keychains (iCloud Keychain / Android Block Store) — at-rest AES-256-GCM encryption and native keystore migration remain (#792). - Contract signing — Refuse to return unsigned contract inputs; fail fast on a missing WalletConnect project ID.
🛠 Improvements
- Send UX — BCH balance + fiat conversion stays on one line and auto-scrolls when it's too long to fit (re-measured on denomination/currency changes; manual swipe under reduced-motion) (#793).
- Subscriptions / Payment Hub UI — Reusable invoice list item, shared composables, consistent per-tab toolbars, search & status filters, inline editing, and websocket-driven real-time updates.
- Home Performance — Lazy-load token metadata as cards scroll into view, limit token store work to visible cards, and serialize boot dialogs so they never stack.
- Chat — "Order again" for e-load orders, NFT metadata backfill from BCMR, hide NFTs without metadata by default, and contact-based group member management.
- Notifications — Re-ask notification permission on app open and stop after 7 declines; show ongoing-orders count on the P2P footer badge.
- Apps — Promote the Recurring Payments (experimental) app on the apps page.
- Dependencies — Upgrade
watchtower-cash-jsto v0.3.2; upgradecapacitor-secure-storage-pluginto v0.9.0 (native keystore) (#789). - Android — Target API 36 (Android 16).
- Localization — Added/updated translations for subscriptions, MLS group management, P2P hints, NFT filters, carousel strings, merchant verification tooltips, and "You are paying to" / "Recipient gets" (#793); translator workflow and
__textssource updates.
v0.26.1
Release Notes
🐛 Bug Fixes
- Contract signing validation — Added redeem-script hash verification against P2SH/P2SH32 locking bytecode to catch contract data mismatches early during signing, preventing silent failures and confusing on-chain errors
- dApp error reporting — Signing/broadcast failures now properly send JSON-RPC error responses back to the dApp (previously the request would hang indefinitely)
- WalletConnect broadcast — Fixed broadcast success check to read
response.data.successinstead of the incorrectresponse.success - WizardConnect broadcast — Always broadcast signed transactions to Watchtower regardless of dApp's broadcast flag (consistent with WalletConnectV2 behavior), and report failures back to the dApp
- Chat Message Lag — Fixed new messages taking time to appear by optimizing computed properties (
messageReadMap,readByNamesMap) to iterate only visible messages instead of the entire history. Also fixed incremental IntersectionObserver tracking for read receipts - Profile Cancel Trap — Cancel button no longer re-opens edit mode; async profile hydration is now properly handled via a watcher
- Pseudo-element Positioning — Added
position: relativeto check-icon containers to anchor glow animations against the correct element - iOS 16 Build — Transpile
mainnet-jsfor iOS 16.2 compatibility - parseInt falsy fallback —
resolvePrivateKeyFromAddressno longer treats 0 as 100 when iterating address indices
🔒 Security
- Secure storage encryption — AES-256-GCM encrypts secure storage values at rest on web/BEX builds (was plaintext in localStorage)
- Mnemonic lifecycle hardening — Deduplicate mnemonic storage, scrub legacy AES-256 copy after migration, release seed phrase from wallet cache on lock/delete, remove dead shard-upload code, gate debug hooks to dev builds
- Legacy PIN migration — Migrate legacy PIN key that embedded raw mnemonic in key name
- Graceful failure — Handle mnemonic decrypt failure in non-critical callers without crashing
- Import typo fix — Corrected
ut8ToBin→utf8ToBinimport (was a dead import that would crash on first use)
🛠 Improvements
- User-facing error notifications — Show
$q.notify/Notify.createalerts on signing and broadcast failures so the user isn't left wondering what went wrong - Chat Performance — New message rendering no longer triggers full DOM re-observation; only newly appended elements are tracked
- Profile UX — Auto-open blank display name/BCH address fields for editing on first visit, with glow-pulse animation highlighting fields needing attention
- Payment Hub Icon — Replaced generic icon with SVG logo
- Payment Method Selection — Newly created payment method auto-selects for new peer
- Swipe-to-Escrow — Added shimmer animation to enabled swipe-to-escrow button
v0.26.0
v0.26.0 Release Notes
✨ New Features
- New Apps — Payment Hub (store management, invoices, subscriptions, API keys) and Paytaca Card (beta) — create, activate, manage, tap-to-pay, cash-in — now available from the apps page
- Wallet List Page — Wallet sidebar converted from dialog to a full page at /wallet/list with smooth drag-and-drop reordering and glassmorphic styling
- Receive Target Selection — Choose between Personal wallet and POS device as the receive destination (feat/pos-receive-target)
- Chat Context Menu — Telegram-style context menu on messages with Copy, Quote, and text-selection support
- Fiat Conversion Display — Show fiat conversion below BCH balance in card details and below card balances/totals in card list page
- Transaction Asset Filter — Filter transactions by All, BCH only, BCH+favorites, or Favorites only with server-side support
- Favorite Tokens Balance Mode — New "Favorites only" balance mode option for BCH card dropdown
- In-App Video Player — Chat embeds inline video with encrypted thumbnail preview and IndexedDB caching
- Support Icon — New support button in Transactions section header on home page
- Back Navigation — Page back button remembers origin (home vs apps default)
🐛 Bug Fixes
- QR Scanner — Fixed camera blocked by hidden body children; scanner under dialogs on mobile now teleports and respects CSS
- Chat — Mark all displayed messages as seen on load; fix duplicate context menu on mobile; prevent stale text selection; fix typing indicator stale closure and timestamp re-sort; suppress Group Not Found flash; preserve message casing; use token symbol in tip messages
- Drag & Drop — Fixed pinned app click/drag conflicts; unpin-bin drop zone detection via position; fixed iOS grid drag with -webkit-user-drag:none; wallet sidebar reordering uses drag handle; prevent text selection on long-press; wallet reorder no longer refreshes the page
- Send Page — Moved wallet balance/max button above advanced options; keyboard anchoring fixed with KeyboardSlidePanel; JPP fix for chipnet addresses
- Card Management — Fixed card pinning not saved; routing after successful creation; dark mode not applied in merchant page; safe-area padding for card page and activation dialog; QR scanner under dialog
- Receiving — Show Receive For gate from first render; selector appears immediately on target selection; generatePosAddress errors rethrow with reentrancy guard; fixed POS gate click handler not responding; fixed websocket listener crash in setupListener
- Wallet — Fixed wallet import failure (nostr pubkey guard, restore flow navigation); wallet name not displaying on startup; stale setTimeout comments cleaned up
- Misc — Chipnet-to-mainnet switch reloads page; merchant page dark mode fix; header-nav preserves original casing per-page; removed nested q-layout from cards.vue for safe-area
🛠 Improvements
- Send Flow — Combined custom keyboard with slide-to-confirm button; auto-focus amount when recipient set; dismiss keyboard on send success; hide keyboard/slide during broadcast
- Receive Page — Removed
isAddressUsedstale comment; added SLP balance check; uses/isused/API for address freshness - General UI/UX
- Chipnet status banner at page bottom with glassmorphic styling
- Chat: message caching, video thumbnails, file upload limits, avatar cache migrated to IndexedDB
- Wallet list: glassmorphic row backgrounds matching token list aesthetic
v0.25.0
Highlights
- Nostr Chat (NIP-17) — Full end-to-end encrypted private messaging with group chats, reactions, media sharing, in-chat BCH payments, typing indicators, and active status.
- Rewards App Overhaul — Complete UI/UX redesign with gamification, help tours, points redemption, referral links, and a Join Rewards Program prompt.
- P2P Ramp Improvements — Eliminated duplicate API calls, peer info display with trade stats, report user flow, and escrow verification fixes.
- Performance & UX — Home page loading optimizations, token list improvements, safe insets, and a reimagined apps page.
New Features
Nostr Chat (NIP-17 Private Messaging)
- Encrypted DMs & group chats over Nostr with NIP-17 gift-wrapped events (Kind 14) and NIP-78 group metadata
- Emoji reactions (NIP-25) with animated badges, detail popup, and undo
- Reply, edit, delete with a long-press floating context menu; edit/delete within a 1-minute window
- Encrypted media sharing via Blossom servers with image resize prompts (Small/Medium/Large) for files >= 1 MB, lazy-loading, and persistent thumbnail cache
- In-chat BCH payments via
/send <amount> [currency]command with swipe-to-confirm and rich payment cards - Read receipts via NIP-25 Kind 7 "👀" reactions, gift-wrapped for privacy, with unread badge counts
- Active status indicators (green dots) showing contacts active within the last 3 minutes, backed by WebSocket + touch API
- Typing status indicator riding on the existing status WebSocket, throttled to 3s
- Push notifications via Watchtower OAuth with client-side registration and in-app fallback for web/PWA
- User profiles with avatar upload (Blossom storage), display name publishing, and BCH address publishing (NIP-78)
- Contact management — add, rename, block, archive, delete; shareable group invite links with request-to-join flow
- Server-authoritative room list with encrypted group names (NIP-44); relay messages no longer mutate room objects
- Per-wallet isolation of chat state (rooms, messages, caches, profile) via
byWallet[walletHash]structure - Telegram-like UI with tabs (All/Unread/Archived), floating chat icon in footer with animated unread badge, theme-aware glassmorphic styling, multiline auto-grow textarea, and relay connection status chip
- Reliability — exponential backoff for relay resubscription, WebSocket ping/keepalive, auto-resubscribe on background tab return
- Renamed
PrivateGroup→ClosedPrivateGroupandPublicGroup→OpenPrivateGroup - Chat disabled on chipnet
Rewards App
- Complete UI/UX redesign of Rewards home, User Rewards, and RFP (Refer-a-Friend) pages with achievement icons, points badges, and reusable cards
- Collapsible rewards summary on home page showing aggregated points, LIFT conversion ratio, and BCH/fiat prices fetched from Cauldron
- Gamified points redemption — RedeemPointsDialog with celebration effects, real-time max redeemable from API, dynamic token balance from promo contract, and integration with rewards swap contract
- Redemption history dialog tracking point-to-token conversions
- Referral program — QR code sharing via ReferralQrDialog encoding full referral URL (
https://rewards.paytaca.com/referral?code=...), deep link handling on Android & iOS, and auto-open referral dialog with pre-filled code - Help tours — data-driven HelpCard component with step-by-step guides, contextual highlighting, scroll-to-view, and smart card placement
- Transaction points — automatic point awarding for marketplace/merchant, cashin, eload, and P2P ramp transactions
- One-time points tracking for first-time user actions (first 7 transactions)
- Join Rewards Program prompt shown once after initial app load, tracked per-wallet; defers until backup reminder is resolved
- Referral banner for new users who skipped entering a referral code during wallet creation
- Beta flag removed — Rewards is now a production app
P2P Ramp
- Peer info component with trade stats (completed/failed/completion %) and reported badge
- Report user flow with reason selection dialog and greyscale styling for reported users
- Price percentage indicator on TradeInfoCard with skeleton loader
- Rate Your Experience with pre-composed feedback chips and trade breakdown display
Apps Page
- Reimagined apps page with categorized list, sticky category chips, visible descriptions, beta section, and search bar
- Pinned apps with long-press context menu
- List/grid view toggle replacing the previous tour
Wallet & Home
- Token list improvements — drag reorder fixes, hide/unhide assets (replacing delete), eye icon toggles
- Token info card on transaction history with Send/Receive/Swap/Chart actions
- View All card on home page when favorite tokens exceed the subscription limit
- BCH info card on transaction history (previously only cash tokens)
- Home page performance — batched price API calls (parallel chunks of 10), store mutation guards skipping no-op updates, and excluded unused
senders/recipientsfields from list views - Transaction caching in localStorage for instant home page rendering
- Chat avatar caching in localStorage for instant display
Bug Fixes
P2P Exchange
- Escrow verification not triggering after swipe-to-escrow — reordered
EscrowTransfer.vuesoescrowPendingOrder()is awaited before emittingsuccess, preventing a race that left the page stuck inescrow-bchstate until manual navigation. (#731)
Wallet / Transactions
- Token metadata inconsistency between home/asset-list and transaction history resolved via BCMR store lookup and background enrichment.
- Address scan — cached master HD node with TTL, fixed
usedDiscoveryfallback, and added balance refresh. - Imported wallet showing zero balance —
getMnemonic(index)now tries hash-based storage before migration flag, fixing mnemonic retrieval for wallets imported from other wallets. - Multisig UTXO type mismatch — false "Insufficient BCH balance" errors fixed by converting
targetSatoshistobigintbefore UTXO selection. - WalletConnect message signing — fixed "buffer length error" by replacing BCHjs signer with libauth; changed response format from hex to base64 to match
personal_signexpectations.
UI / Display
- Safe inset bottom for
fixed-bottomclass — fixed marketplace cart/chat button spacing at the bottom of the screen. (#717)
Localization
- Added ~300 new Nostr chat translation keys across 17 locales.
- Added missing translation keys for apps page, chat profile prompt, and ResetChatDialog.
- Wrapped hardcoded strings in rewards app with translation keys.
- German (
de):Depositchanged from "Anzahlung" to "Empfangen"; added missingShare→ "Teilen". - Added
--keyand--keysflags to i18n tooling for targeted re-translation.
Technical Changes
- New store module:
src/store/nostr-chat/— complete Vuex store with per-wallet state isolation - New services:
src/services/nostr-chat.js,src/wallet/nostr.js(NIP-06/17/25/59),src/wallet/nostr-media.js(Blossom),src/utils/chat-markup.js,src/utils/watchtower-oauth.js - New chat components:
ChatInput,MessageBubble,RoomList,SendBchDialog,RelayStatusChip - New chat pages:
index,conversation,dm-info,group-info,profile,debug/push-notifications-reset - Rewards components:
AchievementCard,AchievementIcon,PointsBadge,HelpCard,ErrorCard,TransactionItem/TransactionList,PointsReceivedDialog,ReferralQrDialog,RedeemPointsDialog,RedeemHistoryDialog,JoinRewardsDialog - PromoContract — updated artifact, rewards swap contract integration, points withdrawal with fee handling, Watchtower UTXO fetching with
getUtxos()fallback - EngagementHub utilities — reorganized env vars, new API calls for LIFT conversion ratio, redemption history, max redeemable, swap contract details
- Dependencies — updated
nostr-tools,@wizardconnect/wallet,bitcoincash-oauth-client
Pull Requests Merged
- #692 — Improvements/rewards app
- #713 — NIP-17 Nostr private messaging chat
- #714 — Rewards app translations
- #715 — P2P Ramp improvements
- #716 — Miscellaneous UI/UX improvements
- #717 — Safe inset bottom for fixed-bottom class
- #718 — Token list UI improvements
- #719 — Token info card & view all card on home page
- #720 — Wallet UX: seed phrase, preferences, per-wallet chat isolation
- #721 — Various UI fixes and improvements
- #722 — Missing translation keys and user-rewards null crash
- #723 — Active status indicator with WebSocket + touch API
- #724 — Nostr chat activity improvements
- #725 — Server-authoritative room list
- #726 — Home page loading improvements
- #727 — Rewards referral code link & deep linking
- #728 — Rename PrivateGroup/PublicGroup terminology
- #729 — Join Rewards Program button
- #730 — Promo contract address subscription
- #731 — Apps UX improvements (escrow verification fix)
- #732 — Chat typing status indicator
- #733 — Fix chat profile horizontal overflow on Android
- #734 — Fix RFP page hang when navigating from rewards home for new users
v0.24.6
Release notes
Bug Fixes
Multisig
- UTXO type mismatch: Fixed false "Insufficient BCH balance" errors by converting
targetSatoshistobigintbefore passing toselectUtxos, and normalizing types throughout UTXO selection logic. - Wallet file download crash on mobile: Fixed app crash when exporting/downloading wallet files on native mobile platforms.
- Wallet watcher + WC session error handling: Improved robustness with early-return guard for falsy
walletId, try/catch aroundgetWalletWcSessionswith 404-specific handling, and proper fallbacks for missing coordination server data.
WalletConnect
- Message signing: Fixed "buffer length error" by replacing BCHjs signer with libauth; changed response format from hex to base64 to match
personal_signexpectations from dapps. Added address verification between client's expected signer and the session signer address. - Disconnect race condition: Prevented race condition in
@wizardconnect/walletduring disconnect.
Reservations / Lift Token
- Discount display: Extracted
getDiscountedPriceUsd/getOriginalPriceUsdinto shared utilities to fix inconsistent discount display between PurchasesTabPanel and PurchaseInfoDialog. - Reservation confirmation: Immediately populate wallet's public key on new reservations so confirmation succeeds on first try. Added comprehensive error handling around
confirmReservation(),initializeVestingContract(),convertDateToBlockHeight(),getAddressPath(), and libauth wallet init. - Oracle data: Added error handling around
getOracleData()and fixedwalletBalancevalue error with optional chaining. - Missing i18n keys: Added translation keys for
FailedToGetContractDataandFailedToGenerateAddress.
UI / Display
- BCH receipt amount: Grayed out trailing zeros in BCH amounts with all-zero decimals; adaptive font-size for receipt amounts.
- Token fiat prices: Refresh fiat prices for all displayed tokens, not just favorites.
QR Scanning
- Normalized UR QR comparisons: Fixed QR content comparison by normalizing UR strings before comparison.
Wallet Import / Mnemonic Storage
- Imported wallet showing zero balance: Fixed mnemonic retrieval failing for wallets imported from other wallets.
getMnemonic(index)now also tries the new hash-based storage scheme even before migration is flagged, so wallets stored via the restore flow (which use only hash-based keys) are immediately found instead of returningnull. - Old-scheme key overwrite: Fixed
storeMnemonicoverwriting wallet 0's old-scheme mnemonic when called with a wallet hash string during import. Old-scheme backup storage is now skipped when the correct index isn't known.
Localization
- German (de): Changed
Depositfrom "Anzahlung" to "Empfangen" to match normal wallet terminology. Added missingShare→ "Teilen".
v0.24.5
Release notes
- Block native keyboard when custom keyboard is active.
- Improved collectibles pagination with input mode and teleporting controls.
- Removed deprecated Connected Address from BEX; updated popup dimensions.
- Fixed marketplace checkout — save buyer's address, improved error debugging.
- Fixed WizardConnect to only broadcast when dApp sets the flag.
v0.24.4
Release Notes
Improvements
- Full 8-decimal precision for BCH amounts with grayed trailing zeros (40% opacity)
- New
v-bch-amountdirective applied across transaction details, lists, and asset displays - Fixed number formatting for Canada, Ireland, Singapore, India, South Africa, Bahrain, and Saint Helena
- Added language mappings for 40+ countries (Arabic, German, French, Spanish)
- Currency auto-updates when selecting a country
- Advance subscription buffer management: maintains 50-pair buffer to prevent subscription gaps
- Contract hydration support for pool withdrawals and smart contract transactions
Bug Fixes
- Fixed WizardConnect address derivation with correct libauth hash160 usage
- Removed cross-wallet timeout collision in WizardConnect buffer management
- Fixed Arabic translation: fixed a number of obvious mistranslations
- Corrected Taiwan locale to zh-TW (Traditional Chinese)
- Fixed spendable BCH calculation for Cauldron max tradeable amount
- Improved transaction fee accuracy with proper BCH input size calculation