-
Notifications
You must be signed in to change notification settings - Fork 15
ZIP-321 standard support — open Vizor from zcash: links (all platforms) #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
piatoss3612
wants to merge
28
commits into
main
Choose a base branch
from
rowan/vzr-91
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
42920cf
Add macOS ZIP-321 payment URI handling
piatoss3612 1303600
Add Windows and Linux ZIP-321 URI handling
piatoss3612 c11e2eb
Keep payment URI prefill during send route refresh
piatoss3612 7a24072
Add payment URI prefill integration test
piatoss3612 86b9408
Add Android and iOS ZIP-321 payment URI handling
piatoss3612 2f8c672
Deliver payment-URI prefill to /send after unlock, not /home
piatoss3612 5031e50
Add regtest E2E for zcash: payment-URI send
piatoss3612 8e12485
Add regtest E2E for the locked-path payment-URI send
piatoss3612 ab97ed0
Apply ZIP-321 payment-URI prefill on the mobile /send route
piatoss3612 389bad3
Harden the locked-path payment-URI prefill (review fixes)
piatoss3612 d3bfc62
Gate mobile amount-step jump on a valid address + cold-start test (re…
piatoss3612 55bc6ad
Don't steal the zcash: handler on every Windows launch (review)
piatoss3612 b177b5b
Consolidate ZIP-321 parser to one core copy (review)
piatoss3612 22a89c4
Fix mobile send back nav from a prefilled deep link
piatoss3612 8d56cf5
Fix mobile send back nav: page pop in normal flow, step fallback on d…
piatoss3612 124268e
Fix Windows zcash: handler compile error (SHChangeNotify + shlobj.h)
piatoss3612 8b8d354
core: Bound ZIP-321 memo decode size
piatoss3612 94d7eee
mobile: Gate ZIP-321 amount prefills on recipient validation
piatoss3612 6df3c59
desktop: Respect existing zcash URI handlers
piatoss3612 daf9b2d
core: Guard payment URI handoff during sends
piatoss3612 3b1c596
mobile: Ignore stale Android payment intents
piatoss3612 f0666fd
test: sync mobile send focused preview expectation
piatoss3612 4c4b000
fix(windows): forward payment URIs to running instance
piatoss3612 c36974e
fix(windows): present window for forwarded payment URIs
piatoss3612 0bef0cf
fix(android): disable Flutter deeplink dispatch for payment URIs
piatoss3612 e6d9c10
fix(macos): present window for payment URI links
piatoss3612 5bddd0d
fix(zip321): reject unsupported memo controls
piatoss3612 47e5528
fix(zip321): preserve uri memo whitespace
piatoss3612 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| uri='zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez' | ||
| uri+='?amount=0.12345678' | ||
| uri+='&memo=Q1AtQzZDREI3NzU' | ||
| uri+='&message=Thank%20you%20for%20your%20purchase' | ||
|
|
||
| case "$(uname -s)" in | ||
| Darwin) | ||
| open "$uri" | ||
| ;; | ||
| Linux) | ||
| xdg-open "$uri" | ||
| ;; | ||
| MINGW*|MSYS*|CYGWIN*) | ||
| cmd.exe /c start "" "$uri" | ||
| ;; | ||
| *) | ||
| printf 'Unsupported platform for opening payment URI\n' >&2 | ||
| exit 1 | ||
| ;; | ||
| esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| <title>Vizor Payment URI Demo</title> | ||
| <style> | ||
| :root { | ||
| color-scheme: light dark; | ||
| font-family: | ||
| Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, | ||
| "Segoe UI", sans-serif; | ||
| } | ||
|
|
||
| body { | ||
| margin: 0; | ||
| min-height: 100vh; | ||
| display: grid; | ||
| place-items: center; | ||
| background: Canvas; | ||
| color: CanvasText; | ||
| } | ||
|
|
||
| main { | ||
| width: min(680px, calc(100vw - 48px)); | ||
| } | ||
|
|
||
| a { | ||
| display: inline-flex; | ||
| min-height: 44px; | ||
| align-items: center; | ||
| border: 1px solid currentColor; | ||
| border-radius: 6px; | ||
| padding: 0 18px; | ||
| color: inherit; | ||
| font-weight: 700; | ||
| text-decoration: none; | ||
| } | ||
|
|
||
| code { | ||
| display: block; | ||
| margin-top: 18px; | ||
| overflow-wrap: anywhere; | ||
| line-height: 1.6; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <main> | ||
| <a href="zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=0.12345678&memo=Q1AtQzZDREI3NzU&message=Thank%20you%20for%20your%20purchase"> | ||
| Open in Vizor | ||
| </a> | ||
|
|
||
| <code> | ||
| zcash:ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez?amount=0.12345678&memo=Q1AtQzZDREI3NzU&message=Thank%20you%20for%20your%20purchase | ||
| </code> | ||
| </main> | ||
| </body> | ||
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:go_router/go_router.dart'; | ||
| import 'package:integration_test/integration_test.dart'; | ||
| import 'package:zcash_wallet/app.dart'; | ||
| import 'package:zcash_wallet/src/app_bootstrap.dart'; | ||
| import 'package:zcash_wallet/src/core/config/rpc_endpoint_config.dart'; | ||
| import 'package:zcash_wallet/src/core/theme/app_theme.dart'; | ||
| import 'package:zcash_wallet/src/features/send/models/send_prefill_args.dart'; | ||
| import 'package:zcash_wallet/src/features/send/screens/send_screen.dart'; | ||
| import 'package:zcash_wallet/src/providers/account_models.dart'; | ||
| import 'package:zcash_wallet/src/providers/sync_provider.dart'; | ||
|
|
||
| const _accountUuid = '550e8400-e29b-41d4-a716-446655440000'; | ||
| const _address = | ||
| 'ztestsapling10yy2ex5dcqkclhc7z7yrnjq2z6feyjad56ptwlfgmy77dmaqqrl9gyhprdx59qgmsnyfska2kez'; | ||
| const _prefill = SendPrefillArgs( | ||
| id: 'payment-uri-e2e', | ||
| source: 'zcash-uri', | ||
| address: _address, | ||
| amountText: '0.12345678', | ||
| memoText: 'CP-C6CDB775', | ||
| ); | ||
|
|
||
| void main() { | ||
| IntegrationTestWidgetsFlutterBinding.ensureInitialized(); | ||
|
|
||
| setUpAll(() async { | ||
| await initializeZcashWalletRuntime(); | ||
| }); | ||
|
|
||
| testWidgets('payment URI prefill survives send route refresh', ( | ||
| WidgetTester tester, | ||
| ) async { | ||
| await tester.binding.setSurfaceSize(const Size(1512, 982)); | ||
| addTearDown(() async { | ||
| await tester.binding.setSurfaceSize(null); | ||
| }); | ||
|
|
||
| final router = GoRouter( | ||
| initialLocation: '/send', | ||
| initialExtra: _prefill, | ||
| routes: [ | ||
| GoRoute( | ||
| path: '/send', | ||
| builder: (_, state) { | ||
| final extra = state.extra; | ||
| return SendScreen(prefill: extra is SendPrefillArgs ? extra : null); | ||
| }, | ||
| ), | ||
| GoRoute(path: '/home', builder: (_, _) => const Text('home route')), | ||
| ], | ||
| ); | ||
|
|
||
| await tester.pumpWidget(_harness(router)); | ||
|
|
||
| await _waitForFieldText( | ||
| tester, | ||
| const ValueKey('send_address_field'), | ||
| _address, | ||
| description: 'payment URI address prefill', | ||
| ); | ||
| expect( | ||
| _fieldText(tester, const ValueKey('send_amount_field')), | ||
| '0.12345678', | ||
| ); | ||
| expect( | ||
| _fieldText(tester, const ValueKey('send_memo_field')), | ||
| 'CP-C6CDB775', | ||
| ); | ||
|
|
||
| router.go('/send'); | ||
| await tester.pump(); | ||
| await tester.pump(const Duration(milliseconds: 250)); | ||
|
|
||
| expect(_fieldText(tester, const ValueKey('send_address_field')), _address); | ||
| expect( | ||
| _fieldText(tester, const ValueKey('send_amount_field')), | ||
| '0.12345678', | ||
| ); | ||
| expect( | ||
| _fieldText(tester, const ValueKey('send_memo_field')), | ||
| 'CP-C6CDB775', | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
| Widget _harness(GoRouter router) { | ||
| return ProviderScope( | ||
| overrides: [ | ||
| appBootstrapProvider.overrideWithValue(_bootstrap), | ||
| syncProvider.overrideWith(() => _FakeSyncNotifier(_syncState)), | ||
| ], | ||
| child: MaterialApp.router( | ||
| routerConfig: router, | ||
| builder: (_, child) => AppTheme(data: AppThemeData.light, child: child!), | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| Future<void> _waitForFieldText( | ||
| WidgetTester tester, | ||
| Key key, | ||
| String expected, { | ||
| required String description, | ||
| Duration timeout = const Duration(seconds: 10), | ||
| }) async { | ||
| final deadline = DateTime.now().add(timeout); | ||
| while (DateTime.now().isBefore(deadline)) { | ||
| if (tester.any(find.byKey(key)) && _fieldText(tester, key) == expected) { | ||
| return; | ||
| } | ||
| await tester.pump(const Duration(milliseconds: 100)); | ||
| } | ||
| fail('Timed out waiting for $description.'); | ||
| } | ||
|
|
||
| String _fieldText(WidgetTester tester, Key key) { | ||
| final editable = find.descendant( | ||
| of: find.byKey(key), | ||
| matching: find.byType(EditableText), | ||
| ); | ||
| return tester.widget<EditableText>(editable).controller.text; | ||
| } | ||
|
|
||
| final _bootstrap = AppBootstrapState( | ||
| initialLocation: '/send', | ||
| initialAccountState: const AccountState( | ||
| accounts: [AccountInfo(uuid: _accountUuid, name: 'Account 1', order: 0)], | ||
| activeAccountUuid: _accountUuid, | ||
| activeAddress: 'u1paymenturiprefillwalletaddress', | ||
| ), | ||
| initialSyncSnapshot: AppSyncSnapshot.empty, | ||
| network: 'main', | ||
| rpcEndpointConfig: defaultRpcEndpointConfig('main'), | ||
| themeMode: ThemeMode.system, | ||
| privacyModeEnabled: false, | ||
| isPasswordConfigured: true, | ||
| isUnlocked: true, | ||
| passwordRotationRecoveryFailed: false, | ||
| ); | ||
|
|
||
| final _syncState = SyncState( | ||
| accountUuid: _accountUuid, | ||
| hasAccountScopedData: true, | ||
| scannedHeight: 1, | ||
| chainTipHeight: 1, | ||
| spendableBalance: BigInt.zero, | ||
| totalBalance: BigInt.zero, | ||
| ); | ||
|
|
||
| class _FakeSyncNotifier extends SyncNotifier { | ||
| _FakeSyncNotifier(this.initialState); | ||
|
|
||
| final SyncState initialState; | ||
|
|
||
| @override | ||
| Future<SyncState> build() async => initialState; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.