Skip to content
Draft
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased (develop)

- added: Stealth Send: the send scene offers a "Recipient receives" asset selector and a Stealth Send toggle, turning the send into a live swap-to-address quote (Houdini privacy routing when stealth is on, all providers for a plain cross-asset send), with linked You send/Recipient gets amounts, quote expiry countdown, and a destination tag row on memo-required chains.
- added: Stealth Swap: a toggle on the swap amount-entry scene routes the swap through the Houdini privacy provider as a fixed, non-tappable provider.
- added: Remote enable/disable of gift card providers via the info server's giftCardInfo config, supporting whole-provider disabling for Phaze and Bitrefill and per-brand disabling for Phaze.
- changed: Reorganize the wallet list menu so Asset Settings is reached through Wallet Settings, and rename the Monero "Backend" card to "Server Settings".
- fixed: Prevent imported Monero wallets from using the Edge LWS backend. Choosing to import now prompts the user to continue with a full node or configure a custom LWS server, matching the wallet settings rule.
Expand Down
8 changes: 3 additions & 5 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export default [
'src/components/cards/StakingOptionCard.tsx',
'src/components/cards/StakingReturnsCard.tsx',
'src/components/cards/SupportCard.tsx',
'src/components/cards/SwapDetailsCard.tsx',

'src/components/cards/TappableAccountCard.tsx',
'src/components/cards/TappableCard.tsx',
'src/components/cards/UnderlinedNumInputCard.tsx',
Expand Down Expand Up @@ -219,13 +219,11 @@ export default [
'src/components/modals/PasswordReminderModal.tsx',
'src/components/modals/PermissionsSettingModal.tsx',

'src/components/modals/RadioListModal.tsx',
'src/components/modals/RawTextModal.tsx',
'src/components/modals/ScamWarningModal.tsx',
'src/components/modals/ScanModal.tsx',

'src/components/modals/StateProvinceListModal.tsx',

'src/components/modals/TextInputModal.tsx',
'src/components/modals/TransferModal.tsx',

'src/components/modals/WalletListSortModal.tsx',
Expand Down Expand Up @@ -258,7 +256,7 @@ export default [
'src/components/rows/EdgeRow.tsx',

'src/components/rows/PaymentMethodRow.tsx',
'src/components/rows/SwapProviderRow.tsx',

'src/components/rows/TxCryptoAmountRow.tsx',

'src/components/scenes/ChangeMiningFeeScene.tsx',
Expand Down
98 changes: 98 additions & 0 deletions maestro/14-stealth/stealth-qr-payment-uri.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Scanned payment-URI walk for the send-to-address swap UI.
#
# A scanned QR carries a payment URI (`ethereum:0x...?amount=0.5`), never a
# bare address. This walk covers both halves of that handling:
# - the destination address is extracted from the URI and accepted even
# though the source wallet cannot parse a foreign-chain URI, and
# - the URI's amount lands on the RECIPIENT side as the guaranteed amount
# (a payment request states what the recipient should receive; the send
# side comes from the swap quote).
#
# The sim has no camera, so the URI is delivered through the scan modal's
# keyboard-entry affordance, which resolves the typed string through the
# exact code path a decoded QR takes.
#
# Screenshots captured (under ~/.maestro/tests/<run>/ ):
# stealth-qr-01-cross-chain ETH destination filled from the URI, amount
# on the "Recipient gets" row as Guaranteed
appId: ${APP_ID}
env:
APP_ID: co.edgesecure.app
PIN_DIGIT: '0'
SRC_WALLET: My Bitcoin
PAYMENT_URI: 'ethereum:0x1f36BF25aE6c07Ae5B6cB6BF6b0b13B1B4d1B372?amount=0.5'
tags:
- stealth
---
- launchApp
- runFlow:
when:
visible: 'Exit PIN'
commands:
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 1500 }
- runFlow:
when:
visible: 'Security is Our Priority'
commands:
- tapOn: 'Cancel'
# Dismiss the unrelated Stellar/Horizon plugin error toast if present.
- tapOn:
text: '.*Horizon.*'
optional: true

# Open the source wallet, then Send.
- tapOn: 'Assets'
- waitForAnimationToEnd:
timeout: 2000
- tapOn: 'Search Wallets'
- inputText: '${SRC_WALLET}'
- waitForAnimationToEnd:
timeout: 2000
- tapOn: '${SRC_WALLET}'
- assertVisible: 'Receive'
- tapOn: 'Send'
- waitForAnimationToEnd:
timeout: 3000
# An unfunded source wallet offers to buy or exchange first.
- runFlow:
when:
visible: 'Wallet Empty'
commands:
- tapOn: 'Not at this time'
- waitForAnimationToEnd:
timeout: 2000
- assertVisible: 'Send to Address'

# Cross-asset destination: the source wallet cannot parse this chain's URI.
- tapOn: 'Recipient receives'
- waitForAnimationToEnd:
timeout: 2000
- tapOn:
id: 'radioListItem_Ethereum'
- waitForAnimationToEnd:
timeout: 2000

# Deliver the payment URI the way a scanned QR does.
- tapOn:
id: 'addressTileScan'
- waitForAnimationToEnd:
timeout: 3000
- tapOn:
id: 'scanModalTextInput'
- waitForAnimationToEnd:
timeout: 2000
- tapOn:
id: 'textInputModal.textInput'
- inputText: '${PAYMENT_URI}'
- tapOn: 'Submit'
- waitForAnimationToEnd:
timeout: 4000

# The address came out of the URI, and its amount is the guaranteed side.
- assertVisible: '.*0x1f36.*'
- assertVisible: '.*0.5 ETH.*'
- assertVisible: 'Guaranteed'
- takeScreenshot: stealth-qr-01-cross-chain
69 changes: 69 additions & 0 deletions maestro/14-stealth/stealth-send.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Stealth Send UI walk.
#
# Opens the Bitcoin wallet's Send scene and walks the send-to-address swap UI
# without entering an address, so no live quote is requested: the "Recipient
# receives" selector (visible before address entry), the Stealth Send toggle
# card with its explainer copy and inline "Learn more" link, and the
# Destination Tag row on a memo-required destination chain (XRP).
#
# Screenshots captured (under ~/.maestro/tests/<run>/ ):
# stealth-send-01-initial plain same-asset start: add-recipient
# affordance, "Recipient receives" visible
# stealth-send-02-stealth-on toggle expanded with the explainer copy
# stealth-send-03-xrp-tag XRP destination: Destination Tag row
appId: ${APP_ID}
env:
APP_ID: co.edgesecure.app
PIN_DIGIT: '0'
tags:
- stealth
---
- launchApp
- runFlow:
when:
visible: 'Exit PIN'
commands:
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 1500 }
- runFlow:
when:
visible: 'Security is Our Priority'
commands:
- tapOn: 'Cancel'
# Dismiss the unrelated Stellar/Horizon plugin error toast if present.
- tapOn:
text: '.*Horizon.*'
optional: true

# Open the Bitcoin wallet, then Send. Gate on the wallet tx-list "Receive"
# button so Send cannot misfire on the home Send.
- tapOn: 'Assets'
- waitForAnimationToEnd:
timeout: 2000
- tapOn: 'My Bitcoin'
- assertVisible: 'Receive'
- tapOn: 'Send'
- waitForAnimationToEnd:
timeout: 3000

# Plain same-asset start: add-recipient affordance, no amount/fee rows yet,
# "Recipient receives" visible before address entry.
- assertVisible: 'Stealth Send'
- assertVisible: 'Send to Address'
- assertVisible: 'Recipient receives'
- takeScreenshot: stealth-send-01-initial

# Stealth on: the toggle card expands with the explainer + Learn more link.
- tapOn: 'Stealth Send'
- assertVisible: 'Uses a route that helps obfuscate.*Learn more.*'
- takeScreenshot: stealth-send-02-stealth-on
- tapOn: 'Stealth Send'

# Cross-asset memo chain: an XRP destination shows the Destination Tag row.
- tapOn: 'Recipient receives'
- tapOn:
text: 'XRP.*'
- assertVisible: 'Destination Tag'
- takeScreenshot: stealth-send-03-xrp-tag
52 changes: 52 additions & 0 deletions maestro/14-stealth/stealth-swap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Stealth Swap UI walk.
#
# Opens the swap flow from the Bitcoin wallet and checks the Stealth Swap
# toggle card during amount entry: the toggle, the explainer copy, and the
# inline "Learn more" link. No quote is requested.
#
# Screenshots captured (under ~/.maestro/tests/<run>/ ):
# stealth-swap-01-start amount entry with the Stealth Swap card
# stealth-swap-02-stealth-on toggle expanded with the explainer copy
appId: ${APP_ID}
env:
APP_ID: co.edgesecure.app
PIN_DIGIT: '0'
tags:
- stealth
---
- launchApp
- runFlow:
when:
visible: 'Exit PIN'
commands:
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 900 }
- tapOn: { text: '${PIN_DIGIT}', waitToSettleTimeoutMs: 1500 }
- runFlow:
when:
visible: 'Security is Our Priority'
commands:
- tapOn: 'Cancel'
- tapOn:
text: '.*Horizon.*'
optional: true

# Open the swap flow with a from + to pair so the toggle card renders.
- tapOn: 'Assets'
- waitForAnimationToEnd:
timeout: 2000
- tapOn: 'My Bitcoin'
- assertVisible: 'Receive'
- tapOn: 'Trade'
- tapOn:
text: 'Swap BTC to/from another crypto'
- waitForAnimationToEnd:
timeout: 3000
- assertVisible: 'Stealth Swap'
- takeScreenshot: stealth-swap-01-start

# Stealth on: the toggle card expands with the explainer + Learn more link.
- tapOn: 'Stealth Swap'
- assertVisible: 'Routes your swap through multiple.*Learn more.*'
- takeScreenshot: stealth-swap-02-stealth-on
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ exports[`AccelerateTxModalComponent should render with loading props 1`] = `
},
]
}
testID="confirmSliderThumb"
>
<Text
adjustsFontSizeToFit={true}
Expand Down
10 changes: 6 additions & 4 deletions src/__tests__/modals/__snapshots__/TextInputModal.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ exports[`TextInputModal should render with a blank input field 1`] = `
},
]
}
testID="textInputModal"
>
<View
collapsable={false}
Expand Down Expand Up @@ -574,7 +575,7 @@ exports[`TextInputModal should render with a blank input field 1`] = `
},
]
}
testID="undefined.textInput"
testID="textInputModal.textInput"
textAlignVertical="top"
/>
</View>
Expand Down Expand Up @@ -615,7 +616,7 @@ exports[`TextInputModal should render with a blank input field 1`] = `
"paddingVertical": 28,
}
}
testID="undefined.clearIcon"
testID="textInputModal.clearIcon"
>
<View
collapsable={false}
Expand Down Expand Up @@ -1266,6 +1267,7 @@ exports[`TextInputModal should render with a populated input field 1`] = `
},
]
}
testID="textInputModal"
>
<View
collapsable={false}
Expand Down Expand Up @@ -1427,7 +1429,7 @@ exports[`TextInputModal should render with a populated input field 1`] = `
},
]
}
testID="undefined.textInput"
testID="textInputModal.textInput"
textAlignVertical="top"
/>
</View>
Expand Down Expand Up @@ -1468,7 +1470,7 @@ exports[`TextInputModal should render with a populated input field 1`] = `
"paddingVertical": 28,
}
}
testID="undefined.clearIcon"
testID="textInputModal.clearIcon"
>
<View
collapsable={false}
Expand Down
Loading