Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,29 @@ enum DashPayContactAddressReadiness {
👥 DP-READY :: identity discovery failed locally after \
\(seconds, privacy: .public)s; starting SPV without DashPay state
""")
case .seedBindingUnverified:
// The signer handed to the call belongs to a different wallet, so
// the drain derived nothing rather than write contact addresses
// from the wrong seed. A rerun with the right signer completes the
// work, which stays queued — hence warning, not error.
logger.warning(
"""
👥 DP-READY :: contact crypto could not verify this wallet's seed \
after \(seconds, privacy: .public)s; starting SPV, contact accounts \
stay queued for a run with the matching signer
""")
case .identityScanIncomplete:
// Every later step ran for the identity that is known, but the
// gap-limit scan left indices unanswered, so the identity set is
// not established. The verdict stays on record and the next launch
// re-scans instead of taking the warm shortcut.
logger.warning(
"""
👥 DP-READY :: identity scan left indices unanswered after \
\(seconds, privacy: .public)s \
scans=\(outcome.discoveryAttempts, privacy: .public); \
starting SPV, the next start re-scans
""")
}
}
}
Expand Down
23 changes: 16 additions & 7 deletions DashWallet/Sources/Models/DashConnect/DashConnectDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ protocol DashConnectDataSource {
func parseQR(_ content: String) async throws -> DashConnectQr
func makeConnectionRequest(from loginRequest: DashKeyRequest) async -> ConnectionRequest
func approveLogin(_ request: DashKeyRequest) async throws -> DAppConnection
func completeKeyRegistration(_ request: DashStRequest) async throws
/// Parses a scanned `dash-st:` payload and either completes key
/// registration immediately or returns a token purchase that awaits
/// explicit user approval via `approveTokenPurchase(_:)`.
func handleStateTransition(_ request: DashStRequest) async throws -> DashConnectStAction
/// Rebuilds, signs and submits a token purchase the user approved.
func approveTokenPurchase(_ request: DashConnectTokenPurchaseRequest) async throws
func disconnect(id: String) async
func remove(id: String) async
}
Expand All @@ -36,7 +41,7 @@ enum DashConnectMockError: LocalizedError, Equatable {
case approveFailed
case notDashConnectQrCode
case unsupportedNetwork(expected: DashConnectNetwork, actual: DashConnectNetwork)
case keyRegistrationNotSupported
case stateTransitionNotSupported

var errorDescription: String? {
switch self {
Expand All @@ -48,8 +53,8 @@ enum DashConnectMockError: LocalizedError, Equatable {
return "This QR code is not a DashConnect QR code."
case let .unsupportedNetwork(expected, actual):
return "This DashConnect QR is for \(Self.displayName(for: actual)), but this wallet currently supports \(Self.displayName(for: expected)) only."
case .keyRegistrationNotSupported:
return "Key registration is not supported by the mock."
case .stateTransitionNotSupported:
return "State transitions are not supported by the mock."
}
}

Expand Down Expand Up @@ -150,7 +155,7 @@ final class MockDashConnectDataSource: DashConnectDataSource {
if DashConnectUri.isStUri(trimmed) {
let request = try DashConnectUri.parseStRequest(trimmed)
try validateNetwork(request.network)
return .keyRegistration(request)
return .stateTransition(request)
}

throw DashConnectMockError.notDashConnectQrCode
Expand Down Expand Up @@ -191,9 +196,13 @@ final class MockDashConnectDataSource: DashConnectDataSource {
)
}

func completeKeyRegistration(_ request: DashStRequest) async throws {
func handleStateTransition(_ request: DashStRequest) async throws -> DashConnectStAction {
try await Task.sleep(nanoseconds: 400_000_000)
throw DashConnectMockError.keyRegistrationNotSupported
throw DashConnectMockError.stateTransitionNotSupported
}

func approveTokenPurchase(_ request: DashConnectTokenPurchaseRequest) async throws {
throw DashConnectMockError.stateTransitionNotSupported
}

func disconnect(id: String) async {
Expand Down
47 changes: 46 additions & 1 deletion DashWallet/Sources/Models/DashConnect/DashConnectModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,52 @@ import SwiftDashSDK

enum DashConnectQr: Equatable {
case login(DashKeyRequest)
case keyRegistration(DashStRequest)
/// A `dash-st:` payload — a serialized state transition whose kind is
/// only known once the wallet parses it (key registration or token
/// purchase).
case stateTransition(DashStRequest)
}

/// What the wallet did — or still needs the user to do — with a scanned
/// `dash-st:` state transition.
enum DashConnectStAction: Equatable {
/// Key registration was validated and published (or the derived keys
/// were already on the identity).
case keyRegistrationCompleted
/// The payload is a token purchase; nothing was signed or sent yet — it
/// awaits explicit user approval via `approveTokenPurchase(_:)`.
case tokenPurchaseApprovalRequired(DashConnectTokenPurchaseRequest)
}

/// A pending token purchase parsed from a `dash-st:` payload, awaiting user
/// approval. Carries the raw values the purchase is rebuilt from and the
/// display fields the approval sheet renders.
struct DashConnectTokenPurchaseRequest: Equatable {
/// Name of an already-connected app whose contract id matches the
/// purchase's data contract, when one is stored locally. Display only.
let appName: String?
/// Identity the purchase debits — validated to be the wallet's own both
/// when the request is built and again on approve.
let ownerId: Data
let dataContractId: Data
let tokenId: Data
let tokenContractPosition: UInt16
let tokenCount: UInt64
/// Total price in Platform credits; passed to `tokenPurchase(...)` as
/// `expectedTotalCost` so the shown and charged amounts cannot diverge.
let totalAgreedPriceCredits: UInt64
let walletUsername: String?
let walletIdentityId: String
}

extension DashConnectTokenPurchaseRequest {
/// Platform credits per DASH (1e11 — 1e8 duffs x 1000 credits per duff).
static let creditsPerDash: Decimal = 100_000_000_000

/// The total price converted to DASH for display.
var totalPriceDash: Decimal {
Decimal(totalAgreedPriceCredits) / Self.creditsPerDash
}
Comment on lines +63 to +70

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Nitpick: Reuse the existing credits-per-DASH definition

This introduces another literal definition of the Platform credits conversion even though PlatformCreditsFormatter.creditsPerDash is already internal and available to this module. The same divisor also has private copies in two payment views. Reusing the existing definition here prevents the money-display conversion from drifting independently.

Suggested change
extension DashConnectTokenPurchaseRequest {
/// Platform credits per DASH (1e11 — 1e8 duffs x 1000 credits per duff).
static let creditsPerDash: Decimal = 100_000_000_000
/// The total price converted to DASH for display.
var totalPriceDash: Decimal {
Decimal(totalAgreedPriceCredits) / Self.creditsPerDash
}
extension DashConnectTokenPurchaseRequest {
/// The total price converted to DASH for display.
var totalPriceDash: Decimal {
Decimal(totalAgreedPriceCredits) / Decimal(PlatformCreditsFormatter.creditsPerDash)
}
}

source: ['codex']

}

/// Lifecycle of an app connection.
Expand Down
Loading
Loading