-
Notifications
You must be signed in to change notification settings - Fork 24
feat(dashpay): QR contact exchange on the add-contact screen #947
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
Merged
QuantumExplorer
merged 4 commits into
develop
from
claude/qr-scan-contact-requests-d0824f
Aug 9, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
b301831
feat(dashpay): QR contact exchange on the add-contact screen
QuantumExplorer 0ef39e1
fix(dashpay): address PR #947 review — strict user-link parsing, owne…
QuantumExplorer 548e62d
fix(dashpay): verify scanned users via exact DPNS resolution
QuantumExplorer b53983d
fix(dashpay): cancel prior scan verification before parsing a new scan
QuantumExplorer 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
80 changes: 80 additions & 0 deletions
80
DashWallet/Sources/Infrastructure/SwiftDashSDK/Contacts/DashPayUserLink.swift
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,80 @@ | ||
| // | ||
| // DashPayUserLink.swift | ||
| // DashWallet | ||
| // | ||
| // QR/link payload identifying a DashPay user: the Platform identity | ||
| // id (base58) plus the preferred DPNS username, encoded as | ||
| // | ||
| // dashpay://user?id=<base58-identity-id>&username=<label> | ||
| // | ||
| // Rendered as "my QR code" on the add-contact screen and parsed back | ||
| // when another user scans it. Parsing is pure and offline; it proves | ||
| // nothing — the username claim must be verified against Platform | ||
| // (DPNS lookup, identity id match) before it is shown as that user. | ||
| // | ||
|
|
||
| import Foundation | ||
| import SwiftDashSDK | ||
|
|
||
| struct DashPayUserLink: Equatable { | ||
| /// 32-byte Platform identity id. | ||
| let identityId: Data | ||
| /// Preferred DPNS label, stored without the ".dash" suffix. | ||
| let username: String | ||
|
|
||
| var identityIdBase58: String { identityId.toBase58String() } | ||
|
|
||
| /// Canonical URI form — the QR payload. | ||
| var uriString: String { | ||
| var components = URLComponents() | ||
| components.scheme = "dashpay" | ||
| components.host = "user" | ||
| components.queryItems = [ | ||
| URLQueryItem(name: "id", value: identityIdBase58), | ||
| URLQueryItem(name: "username", value: username), | ||
| ] | ||
| return components.string ?? "dashpay://user" | ||
| } | ||
|
|
||
| /// Parse a scanned string, enforcing the canonical `dashpay://user` | ||
| /// shape strictly so the cross-platform wire contract stays | ||
| /// unambiguous: scheme/host/parameter names are matched | ||
| /// case-insensitively, but userinfo, port, path, fragment, | ||
| /// duplicate parameters, and unsupported parameters are all | ||
| /// rejected. `id` must decode to a 32-byte base58 identifier and | ||
| /// `username` must be non-empty (a trailing ".dash" is tolerated | ||
| /// and stripped). Anything else — payment URIs, invitation links, | ||
| /// bare usernames — returns nil. | ||
| static func parse(_ string: String) -> DashPayUserLink? { | ||
| let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| guard let components = URLComponents(string: trimmed), | ||
| components.scheme?.lowercased() == "dashpay", | ||
| components.host?.lowercased() == "user", | ||
| components.user == nil, | ||
| components.password == nil, | ||
| components.port == nil, | ||
| components.path.isEmpty, | ||
| components.fragment == nil | ||
| else { return nil } | ||
|
|
||
| var identityId: Data? | ||
| var username: String? | ||
| var seenNames: Set<String> = [] | ||
| for item in components.queryItems ?? [] { | ||
| let name = item.name.lowercased() | ||
| guard seenNames.insert(name).inserted else { return nil } | ||
| switch name { | ||
| case "id": identityId = item.value.flatMap { Data.identifier(fromBase58: $0) } | ||
| case "username": username = item.value | ||
| default: return nil | ||
| } | ||
| } | ||
|
|
||
| guard let identityId, identityId.count == 32 else { return nil } | ||
| let label = (username ?? "") | ||
| .trimmingCharacters(in: .whitespacesAndNewlines) | ||
| .withoutDashSuffix | ||
| guard !label.isEmpty else { return nil } | ||
| return DashPayUserLink(identityId: identityId, username: label) | ||
| } | ||
| } | ||
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
Oops, something went wrong.
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.