Skip to content

feat(wallet): implement DashDEX buy flow with transaction submission#784

Open
romchornyi wants to merge 20 commits into
feat/dash-dexfrom
feat/dash-dex-submit-transaction
Open

feat(wallet): implement DashDEX buy flow with transaction submission#784
romchornyi wants to merge 20 commits into
feat/dash-dexfrom
feat/dash-dex-submit-transaction

Conversation

@romchornyi

@romchornyi romchornyi commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

Implements the complete buy flow for DashDEX, enabling users to deposit crypto (via NEAR or cross-chain providers) and receive Dash at a specified address. This feature enables the "crypto-to-DASH" swap direction and provides a seamless user experience from amount selection through transaction confirmation.

What was done?

Buy Flow Screens

  • BuyEnterAmountView & ViewModel: Amount input with provider selection, deposit address generation, and validation
  • RefundAddressView & ViewModel: NEAR refund address collection for backup/fallback handling
  • BuyReceiveView & ViewModel: Transaction tracking, QR code display, and clipboard support for deposit address

Provider Architecture Consolidation

  • SwapProvider Protocol: Unified interface for buy/sell operations across providers
  • SwapAddressValidator: Consolidated address validation logic
  • ExchangeAddressProvider: Provider-agnostic address resolution
  • SwapDepositURIBuilder: Generates deposit URIs for provider-specific routing
  • SwapCoinIconLoader: Dynamic icon loading with fallbacks

Model Cleanup & Refactoring

  • Removed deprecated Maya standalone models:
    - MayaAddressValidator → merged into MayaSwapProvider
    - MayaCoinIconLoader → dynamic icon loading
    - MayaCryptoCurrency → consolidated in provider models
    - MayaExchangeAddressProvider → in SwapProvider protocol
    - MayaPool → address management in provider
    - MayaSwapPendingGate → transaction tracking in service
  • Created provider-agnostic abstractions for reuse across SwapKit and Maya

How Has This Been Tested?

  • Tested buy flow from amount input → refund address → receive confirmation
  • Verified provider selection (NEAR, cross-chain) displays correct deposit addresses
  • Tested refund address validation for NEAR swaps
  • Confirmed QR code generation and clipboard copy functionality
  • Verified error handling for invalid amounts and missing routes
  • Tested state persistence during navigation
  • Verified address validation with various formats

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Added a new swap flow for buying, refunding, and receiving crypto, including amount entry, refund address entry, and receive QR code screens.
    • Improved coin selection, icon display, and address validation across supported swap assets and networks.
  • Bug Fixes

    • Updated network handling and transaction gating to better prevent invalid or duplicate swap actions.
    • Refined payment URI and address formatting so QR codes and copied details display more reliably.

- Update SwapFlowCoordinator with enhanced provider routing
- Update project.pbxproj for latest DashDEX integration
- Add BuyEnterAmountView and ViewModel for crypto to DASH deposit input
- Add BuyEnterAmountHostingController for UIKit integration
- Add RefundAddressView and ViewModel for NEAR refund address handling
- Add RefundAddressHostingController for address confirmation
- Update SwapFlowCoordinator to route to buy flow
- Update project.pbxproj with new buy flow components
- Update SwapProvider protocol with buy operation support
- Enhance MayaSwapProvider for NEAR-based buy deposits
- Improve SwapKitSwapProvider for cross-chain routing
- Add provider-specific handling for deposit address generation
- Add BuyOrder model for tracking buy transaction state and amounts
- Add SwapDepositURIBuilder for generating deposit URIs with provider-specific routing
- Support NEAR and cross-chain deposit address generation
- Handle refund address and deposit validation
- Enhance BuyEnterAmountView with better validation and formatting
- Improve BuyEnterAmountViewModel for amount calculation and provider selection
- Update RefundAddressView with clearer address input and validation
- Refine RefundAddressViewModel for NEAR address handling and confirmation
- Add Receive screen for displaying DASH receive address and transaction tracking
- Add ReceiveView and ReceiveViewModel for user feedback and QR code display
- Add ReceiveHostingController for UIKit integration
- Improve SelectCoinViewModel with buy mode support and provider routing
- Enhance SwapFlowCoordinator for complete buy flow navigation
- Update project.pbxproj with receive screen components
… flow

- Enhance MayaCryptoCurrency with buy operation support
- Update project.pbxproj with buy flow component references
- Add support for deposit address resolution and refund handling
- Remove MayaAddressValidator (merged into MayaSwapProvider)
- Remove MayaCoinIconLoader (now handled by dynamic icon loading)
- Remove MayaCryptoCurrency (consolidated in provider models)
- Remove MayaExchangeAddressProvider (functionality in SwapProvider)
- Remove MayaPool (address management in provider)
- Remove MayaSwapPendingGate (transaction tracking in service)
- Cleanup legacy Maya model separation
…roviders

- Enhance SwapProvider with consolidated address validation and deposit routing
- Improve SwapDepositURIBuilder for provider-agnostic URI generation
- Consolidate MayaSwapProvider with address validator functionality
- Improve SwapKitSwapProvider with cross-chain routing logic
- Reduce model fragmentation by moving logic to protocol implementations
…alidation

- Enhance BuyEnterAmountView with provider selection and amount validation
- Improve BuyEnterAmountViewModel with deposit address generation
- Refine BuyReceiveView for transaction tracking and clipboard support
- Improve BuyReceiveViewModel with polling and completion handling
- Enhance RefundAddressView with better UX and validation feedback
- Refine RefundAddressViewModel for NEAR address handling
- Update all hosting controllers for proper lifecycle management
- Enhance SwapConvertView with better provider routing and amount display
- Improve SwapConvertViewModel for provider-specific formatting
- Refine EnterAddressView with consolidated validation logic
- Improve EnterAddressViewModel using SwapProvider protocol methods
- Update hosting controllers for consistent lifecycle handling
- Remove dependency on deleted Maya standalone models
…provements

- Enhance OrderPreviewView for buy/sell flow display
- Improve OrderPreviewViewModel with buy amount calculation
- Update SelectCoinView for better provider routing
- Improve SelectCoinViewModel using SwapProvider protocol methods
- Enhance SwapCoinIconView with dynamic icon fallbacks
- Update SelectCoinHostingController for lifecycle management
- Remove dependency on deleted Maya models
- Update SendCoinsService to use SwapProvider protocol methods
- Enhance MayaAPIService for provider-driven requests
- Improve Coinbase integration with new provider routing
- Remove MayaAddressValidatorTests (functionality moved to providers)
- Remove MayaExchangeAddressLookupContextTests (consolidated in tests)
- Update all services to use new protocol-based swap provider interface
- Update project.pbxproj to remove deleted Maya model references
- Remove test file references for consolidated tests
- Enhance SwapFlowCoordinator with updated provider routing
- Update build configuration for cleaner module separation
- Add ExchangeAddressProvider protocol for provider-agnostic address resolution
- Add SwapAddressValidator for consolidated address validation
- Add SwapCoinIconLoader for dynamic icon loading
- Add SwapCryptoCurrency for unified crypto currency handling
- Add SwapPendingGate for transaction state tracking
- Add SwapPool for liquidity pool representation
- Add test coverage for address validation and exchange lookup
- Create abstraction layer for multi-provider swap support
- Detect noRoutesFound error and show minimum amount message
- Provide user-friendly error messages for swap failures
- Handle edge cases in amount validation with proper localization
…ting

- Enhance SwapBackend for better provider dispatching
- Improve SwapDepositURIBuilder with better error handling
- Refine SwapKitSwapProvider for cross-chain deposit routing
- Enhance BuyEnterAmountViewModel with better validation
- Improve BuyReceiveView with enhanced message display
- Refine BuyReceiveViewModel for better state management
- Update RefundAddressViewModel with improved error handling
- Update CURRENT_PROJECT_VERSION to 14 for all targets
- All targets (dashwallet, dashpay, TodayExtension, WatchApp) at 8.6.1 (14)
- Maintain consistency across all build configurations
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@romchornyi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d565e29c-0328-4c5b-a80c-8e09bab37272

📥 Commits

Reviewing files that changed from the base of the PR and between dc29ea4 and 60c8690.

📒 Files selected for processing (6)
  • DashWallet/Sources/Models/Swap/SwapDepositURIBuilder.swift
  • DashWallet/Sources/UI/Swap/Buy/EnterAmount/BuyEnterAmountViewModel.swift
  • DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift
  • DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveViewModel.swift
  • DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift
  • DashWallet/Sources/UI/Swap/SwapFlowCoordinator.swift
📝 Walkthrough

Walkthrough

This PR renames Maya-specific swap types, validators, providers, and caches (e.g., MayaCryptoCurrency, MayaAddressValidator, MayaExchangeAddressProvider) to neutral Swap* equivalents, expands the SwapProvider protocol with buy-order APIs and routability tracking, and adds a complete Buy/Refund/Receive UI flow with URI-building support and Xcode project wiring.

Changes

Maya to Swap Rename and Provider Expansion

Layer / File(s) Summary
Core swap models and protocol
.../Swap/SwapCryptoCurrency.swift, .../Swap/SwapPool.swift, .../Swap/BuyOrder.swift, .../Swap/SwapProvider.swift, .../Swap/SwapBackend.swift
Adds SwapCryptoCurrency and SwapPool models, BuyOrder struct, and expands SwapProvider protocol with neutral pool/inbound types, buy-order APIs, and SwapProviderError.
Address validation and exchange provider rename
.../Swap/SwapAddressValidator.swift, .../Swap/ExchangeAddressProvider.swift, .../Coinbase/Coinbase.swift
Renames MayaAddressValidatorSwapAddressValidator and MayaExchangeAddressProviderExchangeAddressProvider, updating Coinbase/Uphold helper signatures to use SwapCryptoCurrency.
Pending gate and icon loader rename
.../Swap/SwapPendingGate.swift, .../Swap/SwapCoinIconLoader.swift, .../Transactions/SendCoinsService.swift
Renames MayaSwapPendingGateSwapPendingGate and MayaCoinIconLoaderSwapCoinIconLoader, updating disk cache key and usage sites.
Provider implementations
.../Maya/MayaAPIService.swift, .../Maya/MayaSwapProvider.swift, .../SwapKit/SwapKitSwapProvider.swift
Migrates MayaAPIService/MayaSwapProvider/SwapKitSwapProvider to return SwapPool/SwapInboundAddress, adds buy-order creation/validation, and implements buy-routability caching/probing.
Existing swap UI screens
.../Swap/Convert/*, .../Swap/EnterAddress/*, .../Swap/OrderPreview/*, .../Swap/SelectCoin/*, .../Swap/SwapFlowCoordinator.swift
Migrates UI/view models to SwapCryptoCurrency and renamed enums, and wires .buy navigation with SwapKitSwapProvider guards.
Renamed test suites
DashWalletTests/SwapAddressValidatorTests.swift, DashWalletTests/ExchangeAddressLookupContextTests.swift
Renames and updates test suites to use SwapCryptoCurrency/SwapAddressValidator/ExchangeAddressProvider.

New Buy/Refund/Receive Flow

Layer / File(s) Summary
Deposit URI builder
.../Swap/SwapDepositURIBuilder.swift
Builds BIP21/EIP-681/bare-address payment URIs from coin/address/amount/memo.
Buy Enter Amount screen
.../Buy/EnterAmount/*
Adds view/view model/hosting controller for entering buy amount with currency conversion and continue validation.
Refund Address screen
.../Buy/RefundAddress/*
Adds view/view model/hosting controller for entering/validating refund address with clipboard and QR-scan support.
Buy Receive screen
.../Buy/Receive/*
Adds view/view model/hosting controller that create a buy order and display deposit URI/QR.
Project wiring
DashWallet.xcodeproj/project.pbxproj
Adds new Buy/Refund/Receive/URI-builder file references and build phases, replaces Maya* entries with Swap* equivalents, and bumps CURRENT_PROJECT_VERSION.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BuyEnterAmountView
  participant RefundAddressView
  participant BuyReceiveView
  participant SwapKitSwapProvider

  User->>BuyEnterAmountView: enter amount, tap Continue
  BuyEnterAmountView->>SwapKitSwapProvider: validateBuyOrder(sellAsset, sellAmount, refundAddress)
  SwapKitSwapProvider-->>BuyEnterAmountView: validation result
  BuyEnterAmountView->>RefundAddressView: navigate with coin, amount
  User->>RefundAddressView: enter/paste/scan refund address
  RefundAddressView->>BuyReceiveView: navigate with refund address confirmed
  BuyReceiveView->>SwapKitSwapProvider: createBuyOrder(sellAsset, sellAmount, destination, refundAddress)
  SwapKitSwapProvider-->>BuyReceiveView: BuyOrder(depositAddress, memo)
  BuyReceiveView-->>User: display QR/deposit address
Loading

Possibly related PRs

  • dashpay/dashwallet-ios#782: Extends the same SwapProvider protocol's direction-aware dispatch pattern that this PR further builds upon with buy-order APIs and neutral Swap* types.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: implementing the DashDEX buy flow with transaction submission.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dash-dex-submit-transaction

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@romchornyi

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (7)
DashWallet/Sources/Models/Swap/SwapBackend.swift (1)

27-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move @MainActor to its own line to satisfy the configured SwiftLint attributes rule.

SwiftLint flags attributes on the same line as function declarations. As per coding guidelines, Swift code must satisfy SwiftLint as configured in .swiftlint.yml.

♻️ Proposed fix
-    `@MainActor` func makeProvider() -> SwapProvider {
+    `@MainActor`
+    func makeProvider() -> SwapProvider {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DashWallet/Sources/Models/Swap/SwapBackend.swift` at line 27, The
makeProvider() declaration currently has `@MainActor` on the same line, which
violates the configured SwiftLint attributes rule. Move the `@MainActor` attribute
onto its own line immediately above makeProvider() in SwapBackend so the
function declaration follows the project’s SwiftLint style requirements.

Sources: Coding guidelines, Linters/SAST tools

DashWallet/Sources/UI/Swap/OrderPreview/OrderPreviewViewModel.swift (1)

57-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale "Maya"-specific wording in newly-generic type docs.

SwapBackendOutcome and backendOutcome are renamed to be provider-neutral (used by both Maya and SwapKit via swapProvider: SwapProvider), but the doc comments still hard-code "Maya" ("as reported by Maya's API", "has not reached a terminal state", "Maya returned DASH to sender", "The true backend outcome from Maya's API"). This is misleading now that this state is populated by any SwapProvider implementation.

✏️ Suggested comment wording
-/// The true backend state of the swap as reported by Maya's API.
+/// The true backend state of the swap as reported by the active swap provider's API.
 /// Never causes the user-facing `swapStatus` to regress — it is updated AFTER
 /// success has already been shown, so the UI is never yanked away.
 /// A post-success refund is recorded here for future surfacing in tx history.
 enum SwapBackendOutcome: Equatable {
-    case pending                          // Maya has not reached a terminal state yet
+    case pending                          // Provider has not reached a terminal state yet
     case done(outHashes: [String])        // funds arrived at destination chain
-    case refunded                         // Maya returned DASH to sender ("refunded"/"aborted")
+    case refunded                         // Provider returned DASH to sender ("refunded"/"aborted")
 }
...
-    /// The true backend outcome from Maya's API, tracked independently of `swapStatus`.
+    /// The true backend outcome from the active swap provider's API, tracked independently of `swapStatus`.

Also applies to: 119-124

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DashWallet/Sources/UI/Swap/OrderPreview/OrderPreviewViewModel.swift` around
lines 57 - 67, The doc comments for SwapBackendOutcome and backendOutcome still
mention Maya-specific behavior, but these types are now provider-neutral and
used by swapProvider/SwapProvider across implementations. Update the comments on
SwapBackendOutcome cases and the backendOutcome property to use generic provider
wording instead of “Maya” or “Maya’s API,” while keeping the intent of
pending/done/refunded accurate. Use the symbols SwapBackendOutcome and
backendOutcome to find all affected comments and revise them consistently.
DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift (1)

129-130: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SwiftLint: attribute placement.

@ViewBuilder should be on its own line per the configured attributes rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift` around lines 129
- 130, The qrSection view declaration uses `@ViewBuilder` inline, which violates
the configured SwiftLint attributes rule. Move `@ViewBuilder` onto its own line
directly above the private var qrSection declaration in BuyReceiveView so the
attribute placement matches the project style.

Source: Linters/SAST tools

DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressView.swift (1)

114-124: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SwiftLint: avoid closure literal to fix multiple_closures_with_trailing_closure.

Pass the method reference directly, matching clipboardContentRow above (Line 103).

🔧 Proposed fix
     private var clipboardPermissionRow: some View {
-        Button(action: { viewModel.pasteFromClipboard() }) {
+        Button(action: viewModel.pasteFromClipboard) {
             DashUIKit.MenuItem(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressView.swift` around
lines 114 - 124, The `clipboardPermissionRow` action uses a closure literal,
triggering `multiple_closures_with_trailing_closure`; update the `Button` in
`RefundAddressView` to pass the `viewModel.pasteFromClipboard` method reference
directly, matching the `clipboardContentRow` pattern, so the action is expressed
without an unnecessary wrapper closure.

Source: Linters/SAST tools

DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift (2)

102-110: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Redundant pasteboard reads on first reveal.

On the first tap, pasteFromClipboard calls refreshClipboardAddress() (which internally reads the pasteboard via validClipboardAddress()), then immediately reads the pasteboard again via currentClipboardContent(). This is redundant and, since raw pasteboard access can trigger the system paste notification, doing it twice in one action is unnecessary.

♻️ Proposed fix
     func pasteFromClipboard() {
         if !hasRevealedClipboard {
             hasRevealedClipboard = true
             refreshClipboardAddress()
+            guard let content = clipboardContent else { return }
+            addressText = Self.extractAddressFromURI(content)
+            clearValidationError()
+            return
         }
         guard let content = currentClipboardContent() else { return }
         addressText = Self.extractAddressFromURI(content)
         clearValidationError()
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift`
around lines 102 - 110, The first-call path in pasteFromClipboard() reads the
pasteboard twice because it invokes refreshClipboardAddress() and then
currentClipboardContent() immediately after. Update the RefundAddressViewModel
logic so the initial reveal only performs one pasteboard read, reusing the
already-fetched clipboard value (or deferring the refreshClipboardAddress()
call) before setting addressText and clearing the validation error.

154-187: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate URI-parsing logic — matches the author's own TODO.

extractAddressFromURI duplicates logic destined to live in a shared helper with EnterAddressViewModel. Worth extracting now to avoid the two implementations silently diverging (e.g., one gets a bugfix the other doesn't).

Want me to extract this into a shared URIAddressExtractor utility usable by both view models?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift`
around lines 154 - 187, The URI parsing in extractAddressFromURI is duplicated
and already called out by the TODO, so move this logic into a shared helper now
instead of keeping a local copy in RefundAddressViewModel. Extract the parsing
behavior into a reusable utility (for example a URIAddressExtractor or shared
helper method) and update both RefundAddressViewModel and EnterAddressViewModel
to call it so future fixes stay in sync.
DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressHostingController.swift (1)

42-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SwiftLint: mark unimplemented initializer as unavailable.

🔧 Proposed fix
+    `@available`(*, unavailable)
     required init?(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressHostingController.swift`
around lines 42 - 44, The required initializer in RefundAddressHostingController
is intentionally unimplemented, so instead of leaving required init?(coder:) as
a fatalError, mark that initializer as unavailable to satisfy SwiftLint and make
the intent explicit. Update the RefundAddressHostingController definition so the
NSCoder initializer is declared unavailable while keeping the same class
behavior for the supported initializer path.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@DashWallet/Sources/Models/Swap/SwapDepositURIBuilder.swift`:
- Around line 155-170: The SwapDepositURIBuilder.baseUnits helper is silently
converting any unparseable amount into "0", which can cause evmURI to generate a
valid-looking zero-value payment URI instead of failing. Update baseUnits to
surface an error or return an optional/result that the caller must handle, and
make the evmURI path in SwapDepositURIBuilder fail explicitly when amount
parsing or conversion is invalid; keep the existing numeric conversion logic in
baseUnits, but remove the silent fallback behavior.

In `@DashWallet/Sources/UI/Swap/Buy/EnterAmount/BuyEnterAmountViewModel.swift`:
- Line 234: The amount parsing path is doing a Double round-trip, which can
introduce binary floating-point noise before the Decimal is created. Update
parseInput and updateAmountModel so the sanitized input string is converted
directly to Decimal instead of going through Double and Decimal(d). Keep the
exact user-entered value flowing through the amount model and preserve the
existing rounding/validation behavior.

In `@DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift`:
- Around line 129-154: The qrSection view in BuyReceiveView never distinguishes
a failed fetch from an in-progress load, so errorMessage from the viewModel is
hidden behind the same placeholder spinner. Update qrSection to check
viewModel.errorMessage explicitly before the fallback loading placeholder, and
render an error state with the message (and a retry/action if available) instead
of ProgressView. Keep the current qrImage and isLoading checks, but make the
final branch only represent the non-error, still-loading case.

In `@DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveViewModel.swift`:
- Around line 77-84: `displaySendAmount` is reading the pre-order `sellAmount`
instead of the backend-confirmed amount used by `uri`/`qrContent`. Update
`BuyReceiveViewModel.displaySendAmount` (and any related bare-URI display paths
in the same view model) to derive the displayed amount from the finalized order
value, such as `order.sellAmount`, so the user sees the exact amount the deposit
URI expects. Keep the existing trimming/empty-string handling, but ensure the
source amount is the confirmed one after `createBuyOrder` completes.

In `@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift`:
- Around line 62-74: The addressValidationErrorMessage in RefundAddressViewModel
hardcodes Ethereum as the invalid example, which can conflict when coin.chain is
already Ethereum-based. Update the NSLocalizedString template and its
String(format:) usage so the wrong-network example is derived from the current
chain context instead of always saying “Ethereum,” using
addressValidationErrorMessage, SwapCryptoCurrency.chainDisplayName, and
coin.chain/coin.code to produce a consistent message for every network.
- Around line 116-124: The attemptContinue flow in RefundAddressViewModel
silently exits for URI-like input that parses to an empty candidate, so tapping
Continue shows no validation feedback. Update attemptContinue() to set
shouldShowAddressValidationError before returning nil for an empty result from
Self.extractAddressFromURI(trimmed), and keep the validation path in place for
SwapAddressValidator.isValid(address:for:). This ensures inputs like “bitcoin:”
surface the expected error instead of doing nothing.

In `@DashWallet/Sources/UI/Swap/SwapFlowCoordinator.swift`:
- Around line 53-59: `navigateToEnterAddress`, `navigateToRefundAddress`, and
`navigateToReceive` are missing the same duplicate-push protection already used
in `navigateToConvert` and `navigateToBuyEnterAmount`. Add an early guard in
each of these navigation methods to check whether the intended view controller
is already the navigationController’s topViewController before pushing. Keep the
fix consistent with the existing push logic in `SwapFlowCoordinator` so repeated
confirmation callbacks do not stack duplicate screens.

---

Nitpick comments:
In `@DashWallet/Sources/Models/Swap/SwapBackend.swift`:
- Line 27: The makeProvider() declaration currently has `@MainActor` on the same
line, which violates the configured SwiftLint attributes rule. Move the
`@MainActor` attribute onto its own line immediately above makeProvider() in
SwapBackend so the function declaration follows the project’s SwiftLint style
requirements.

In `@DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift`:
- Around line 129-130: The qrSection view declaration uses `@ViewBuilder` inline,
which violates the configured SwiftLint attributes rule. Move `@ViewBuilder` onto
its own line directly above the private var qrSection declaration in
BuyReceiveView so the attribute placement matches the project style.

In
`@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressHostingController.swift`:
- Around line 42-44: The required initializer in RefundAddressHostingController
is intentionally unimplemented, so instead of leaving required init?(coder:) as
a fatalError, mark that initializer as unavailable to satisfy SwiftLint and make
the intent explicit. Update the RefundAddressHostingController definition so the
NSCoder initializer is declared unavailable while keeping the same class
behavior for the supported initializer path.

In `@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressView.swift`:
- Around line 114-124: The `clipboardPermissionRow` action uses a closure
literal, triggering `multiple_closures_with_trailing_closure`; update the
`Button` in `RefundAddressView` to pass the `viewModel.pasteFromClipboard`
method reference directly, matching the `clipboardContentRow` pattern, so the
action is expressed without an unnecessary wrapper closure.

In `@DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift`:
- Around line 102-110: The first-call path in pasteFromClipboard() reads the
pasteboard twice because it invokes refreshClipboardAddress() and then
currentClipboardContent() immediately after. Update the RefundAddressViewModel
logic so the initial reveal only performs one pasteboard read, reusing the
already-fetched clipboard value (or deferring the refreshClipboardAddress()
call) before setting addressText and clearing the validation error.
- Around line 154-187: The URI parsing in extractAddressFromURI is duplicated
and already called out by the TODO, so move this logic into a shared helper now
instead of keeping a local copy in RefundAddressViewModel. Extract the parsing
behavior into a reusable utility (for example a URIAddressExtractor or shared
helper method) and update both RefundAddressViewModel and EnterAddressViewModel
to call it so future fixes stay in sync.

In `@DashWallet/Sources/UI/Swap/OrderPreview/OrderPreviewViewModel.swift`:
- Around line 57-67: The doc comments for SwapBackendOutcome and backendOutcome
still mention Maya-specific behavior, but these types are now provider-neutral
and used by swapProvider/SwapProvider across implementations. Update the
comments on SwapBackendOutcome cases and the backendOutcome property to use
generic provider wording instead of “Maya” or “Maya’s API,” while keeping the
intent of pending/done/refunded accurate. Use the symbols SwapBackendOutcome and
backendOutcome to find all affected comments and revise them consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a7271a9-7abd-4e51-b609-cb7c07ec4e6f

📥 Commits

Reviewing files that changed from the base of the PR and between eb1f650 and dc29ea4.

📒 Files selected for processing (41)
  • DashWallet.xcodeproj/project.pbxproj
  • DashWallet/Sources/Models/Coinbase/Coinbase.swift
  • DashWallet/Sources/Models/Maya/MayaAPIService.swift
  • DashWallet/Sources/Models/Maya/MayaCryptoCurrency.swift
  • DashWallet/Sources/Models/Maya/MayaSwapProvider.swift
  • DashWallet/Sources/Models/Swap/BuyOrder.swift
  • DashWallet/Sources/Models/Swap/ExchangeAddressProvider.swift
  • DashWallet/Sources/Models/Swap/SwapAddressValidator.swift
  • DashWallet/Sources/Models/Swap/SwapBackend.swift
  • DashWallet/Sources/Models/Swap/SwapCoinIconLoader.swift
  • DashWallet/Sources/Models/Swap/SwapCryptoCurrency.swift
  • DashWallet/Sources/Models/Swap/SwapDepositURIBuilder.swift
  • DashWallet/Sources/Models/Swap/SwapPendingGate.swift
  • DashWallet/Sources/Models/Swap/SwapPool.swift
  • DashWallet/Sources/Models/Swap/SwapProvider.swift
  • DashWallet/Sources/Models/SwapKit/SwapKitSwapProvider.swift
  • DashWallet/Sources/Models/Transactions/SendCoinsService.swift
  • DashWallet/Sources/UI/Swap/Buy/EnterAmount/BuyEnterAmountHostingController.swift
  • DashWallet/Sources/UI/Swap/Buy/EnterAmount/BuyEnterAmountView.swift
  • DashWallet/Sources/UI/Swap/Buy/EnterAmount/BuyEnterAmountViewModel.swift
  • DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveHostingController.swift
  • DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift
  • DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveViewModel.swift
  • DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressHostingController.swift
  • DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressView.swift
  • DashWallet/Sources/UI/Swap/Buy/RefundAddress/RefundAddressViewModel.swift
  • DashWallet/Sources/UI/Swap/Convert/SwapConvertHostingController.swift
  • DashWallet/Sources/UI/Swap/Convert/SwapConvertView.swift
  • DashWallet/Sources/UI/Swap/Convert/SwapConvertViewModel.swift
  • DashWallet/Sources/UI/Swap/EnterAddress/EnterAddressHostingController.swift
  • DashWallet/Sources/UI/Swap/EnterAddress/EnterAddressView.swift
  • DashWallet/Sources/UI/Swap/EnterAddress/EnterAddressViewModel.swift
  • DashWallet/Sources/UI/Swap/OrderPreview/OrderPreviewView.swift
  • DashWallet/Sources/UI/Swap/OrderPreview/OrderPreviewViewModel.swift
  • DashWallet/Sources/UI/Swap/SelectCoin/Components/SwapCoinIconView.swift
  • DashWallet/Sources/UI/Swap/SelectCoin/SelectCoinHostingController.swift
  • DashWallet/Sources/UI/Swap/SelectCoin/SelectCoinView.swift
  • DashWallet/Sources/UI/Swap/SelectCoin/SelectCoinViewModel.swift
  • DashWallet/Sources/UI/Swap/SwapFlowCoordinator.swift
  • DashWalletTests/ExchangeAddressLookupContextTests.swift
  • DashWalletTests/SwapAddressValidatorTests.swift
💤 Files with no reviewable changes (1)
  • DashWallet/Sources/Models/Maya/MayaCryptoCurrency.swift

Comment thread DashWallet/Sources/Models/Swap/SwapDepositURIBuilder.swift Outdated
Comment thread DashWallet/Sources/UI/Swap/Buy/EnterAmount/BuyEnterAmountViewModel.swift Outdated
Comment thread DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveView.swift
Comment thread DashWallet/Sources/UI/Swap/Buy/Receive/BuyReceiveViewModel.swift
Comment thread DashWallet/Sources/UI/Swap/SwapFlowCoordinator.swift
…tor routing

- Improve SwapDepositURIBuilder with better error handling
- Enhance BuyEnterAmountViewModel validation and error messages
- Add message display improvements to BuyReceiveView
- Refine BuyReceiveViewModel for better state handling
- Simplify RefundAddressViewModel logic
- Update SwapFlowCoordinator for improved routing
@romchornyi

romchornyi commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants