Problem
App closed/killed + user acts on dapp (sign request, proposal, auth) -> wallet never wakes up. Relay holds the message but nothing delivers a system notification, so the request times out and the user has to manually reopen the app.
Background
- Fix is Reown Push Server (Echo,
echo.walletconnect.com, fallback .org)
- Same APNs/FCM token goes to two servers: Gem
/v2/devices (existing) + Echo POST /{projectId}/clients (new). Existing projectId in core/gemstone/src/config/wallet_connect.rs is reused.
register() is never called today on either platform. Foreground flow already works; this only adds background/kill wake-up + tap-to-open routing into the existing approve screens.
- iOS fork (
gemwalletcom/reown-swift@gemwallet, pinned d343984c): Push module at parity with upstream (diff vs main is empty for Sources/WalletConnectPush; the 188-behind delta is Pay/AppKit/Yttrium removal). No fork pre-work needed, stay pinned.
iOS
ios/Packages/FeatureServices/WalletConnectorService/WalletConnectorService.swift: pass explicit environment to WalletKit.configure (currently defaults to .production; use .sandbox for Debug/TestFlight).
ios/Gem/App.swift didRegisterForRemoteNotifications: after existing Gem sync, add try await WalletKit.instance.register(deviceToken:enableEncrypted: true). Re-register every launch (APNs rotation).
- New
NotificationServiceExtension target: depend on ReownWalletKit product (local-path package), App Group group.com.gemwallet.ios + Push capability. Decrypt via WalletKitDecryptionService (tags 1100/1108/1116); model on Example/PNDecryptionService/NotificationService.swift minus Mixpanel/Notify parts. Push text = dapp name + method only, no amounts/addresses.
- Tap routes into existing
handleSessionRequests; cold start must drain getPendingRequests. If user disables push, call Push.instance.unregister().
Android
ReownWalletConnectClient.initialize: after init success + FCM token available, call WalletKit.registerDeviceToken(fcmToken, onSuccess, onError). Wire both directions: FCM.onNewToken -> forward to Echo if WC initialized; init success -> read stored token from DevicePushSettings and register.
android/flavors/fcm/.../FCM.kt onMessageReceived: branch Gem payloads (type/data + notification) vs Echo data messages; Echo path builds high-priority notification deep-linking to gem://wc/ / WalletConnectRequest route. Coordinator already drains pending via handlePendingRequests().
- Respect
POST_NOTIFICATIONS (API 33+) and existing push-enabled gate. fdroid (noop WC + stub push) needs nothing. HMS support TBD (Echo only knows FCM).
Dashboard ops (blocking, no code)
Project 3bc07... in WalletConnect dashboard: enable Push Server, upload APNs Auth Key (.p8 + Key/Team ID, Sandbox + Production), register FCM project.
Testing
- iOS: physical device only; kill app -> dapp request (e.g. react-app.walletconnect.com) -> decrypted push -> tap -> request screen with VerifyContext. Cover sandbox/prod, rotation, unregister.
- Android: device/emulator with Play Services; kill app -> request -> FCM -> notification -> tap -> request screen. Cover push-disabled, token refresh, cold start.
- Extend
gem-walletconnect-e2e skill to background/kill once landed.
Open decisions
- One push toggle (Gem + WC) or two?
- Encrypted-only on iOS (needs NSE) vs plain fallback for v1?
- Unregister Echo on wallet deletion/logout?
Problem
App closed/killed + user acts on dapp (sign request, proposal, auth) -> wallet never wakes up. Relay holds the message but nothing delivers a system notification, so the request times out and the user has to manually reopen the app.
Background
echo.walletconnect.com, fallback.org)/v2/devices(existing) + EchoPOST /{projectId}/clients(new). ExistingprojectIdincore/gemstone/src/config/wallet_connect.rsis reused.register()is never called today on either platform. Foreground flow already works; this only adds background/kill wake-up + tap-to-open routing into the existing approve screens.gemwalletcom/reown-swift@gemwallet, pinnedd343984c): Push module at parity with upstream (diff vsmainis empty forSources/WalletConnectPush; the 188-behind delta is Pay/AppKit/Yttrium removal). No fork pre-work needed, stay pinned.iOS
ios/Packages/FeatureServices/WalletConnectorService/WalletConnectorService.swift: pass explicitenvironmenttoWalletKit.configure(currently defaults to.production; use.sandboxfor Debug/TestFlight).ios/Gem/App.swiftdidRegisterForRemoteNotifications: after existing Gem sync, addtry await WalletKit.instance.register(deviceToken:enableEncrypted: true). Re-register every launch (APNs rotation).NotificationServiceExtensiontarget: depend onReownWalletKitproduct (local-path package), App Groupgroup.com.gemwallet.ios+ Push capability. Decrypt viaWalletKitDecryptionService(tags 1100/1108/1116); model onExample/PNDecryptionService/NotificationService.swiftminus Mixpanel/Notify parts. Push text = dapp name + method only, no amounts/addresses.handleSessionRequests; cold start must draingetPendingRequests. If user disables push, callPush.instance.unregister().Android
ReownWalletConnectClient.initialize: after init success + FCM token available, callWalletKit.registerDeviceToken(fcmToken, onSuccess, onError). Wire both directions:FCM.onNewToken-> forward to Echo if WC initialized; init success -> read stored token fromDevicePushSettingsand register.android/flavors/fcm/.../FCM.ktonMessageReceived: branch Gem payloads (type/data+notification) vs Echo data messages; Echo path builds high-priority notification deep-linking togem://wc//WalletConnectRequestroute. Coordinator already drains pending viahandlePendingRequests().POST_NOTIFICATIONS(API 33+) and existing push-enabled gate.fdroid(noop WC + stub push) needs nothing. HMS support TBD (Echo only knows FCM).Dashboard ops (blocking, no code)
Project
3bc07...in WalletConnect dashboard: enable Push Server, upload APNs Auth Key (.p8 + Key/Team ID, Sandbox + Production), register FCM project.Testing
gem-walletconnect-e2eskill to background/kill once landed.Open decisions