element-x-ada/docs/build-logs/pay-flow-result.md

3.5 KiB

/pay Fallback UX Implementation

Date: 2026-03-29 Commit: 2b93236229 on phase1-dev APK: http://192.168.0.5:8888/

What Was Implemented

Flow B — Fallback When Recipient Has No Linked Wallet

When a user types /pay 10 to a recipient who hasn't linked a Cardano wallet:

  1. Address lookup happens in background - CardanoAddressService.lookupAddress(@recipient) is called
  2. Not found → Show banner card with:
    • Warning icon: "⚠️ @recipient hasn't linked a Cardano wallet"
    • Instructional text: "Enter their Cardano address manually:"
    • Embedded editable text field with placeholder "addr1... or addr_test1..."
  3. Live validation on manual address input:
    • Must start with addr_test1 (preprod) or addr1 (mainnet)
    • Length between 58-108 characters
    • Shows inline error if invalid
    • Shows green checkmark "Valid address" when valid
  4. Continue button enables when:
    • Amount is valid (≥1 ADA)
    • Manual address passes validation
  5. Resolved address is passed to confirmation screen - The PaymentEntryNode now passes state.resolvedAddress (which comes from either the lookup or manual entry) instead of the raw recipientInput

Files Changed

File Changes
PaymentFlowEvents.kt Added ManualAddressChanged(address: String) event
PaymentEntryState.kt Added manualAddressInput, manualAddressError, resolvedAddress fields; added needsManualAddressEntry computed property
PaymentEntryPresenter.kt Handle ManualAddressChanged event; validateManualAddress() function; updated canContinue logic to work with manual entry
PaymentEntryView.kt Added ManualAddressEntryCard composable with embedded OutlinedTextField; updated preview states
PaymentEntryNode.kt Changed to pass state.resolvedAddress instead of state.recipientInput to confirmation callback

Flow A — Happy Path (Already Working)

When recipient HAS a linked wallet:

  1. User types /pay 10
  2. Payment form opens with recipient pre-selected from DM context
  3. CardanoAddressService.lookupAddress(@recipient) returns the address
  4. Green card shows: "✓ Address loaded from @recipient's profile" with truncated address
  5. Recipient field shows Matrix user ID (read-only context)
  6. Amount field pre-filled with "10"
  7. Continue button enabled immediately
  8. Confirmation screen shows full transaction details with fee estimate

Amount Pre-fill

When user types /pay 10, the 10 is parsed by PayCommandParser and passed via ParsedPayCommand.AmountOnly or ParsedPayCommand.WithMatrixRecipient. The PaymentEntryPresenter.extractPrefills() function extracts this and pre-fills the amount field.

Address Validation Rules

private fun validateManualAddress(input: String): String? {
    // Must start with addr_test1 (preprod) or addr1 (mainnet)
    val isTestnet = input.startsWith("addr_test1")
    val isMainnet = input.startsWith("addr1") && !input.startsWith("addr_test1")
    if (!isTestnet && !isMainnet) return "Address must start with addr1 or addr_test1"
    if (input.length < 58) return "Address too short"
    if (input.length > 108) return "Address too long"
    if (!CARDANO_ADDRESS_REGEX.matches(input)) return "Invalid Cardano address format"
    return null
}

Testing Notes

  • Build passes with warnings only (no errors)
  • All existing tests pass
  • Preview states updated to include manual entry scenarios
  • APK served on port 8888 for install testing