From 9d8310ed3b88cfa7b4cf9f6d5f9fddfbf4fb9b56 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 12 Nov 2025 16:06:50 -0800 Subject: [PATCH 1/8] Log berforeTransaction errors to console --- src/components/scenes/SendScene2.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 9e1b8dfc2a5..1100cfebc2f 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -1130,6 +1130,10 @@ const SendComponent = (props: Props): React.ReactElement => { try { if (beforeTransaction != null) await beforeTransaction() } catch (e: unknown) { + console.error( + 'Error from before transaction route param hook: ', + String(e) + ) return } From f0b19771b52cdc6acde592db72eb0866f9f02cc7 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 12 Nov 2025 16:08:07 -0800 Subject: [PATCH 2/8] Rename type-casted error --- src/components/scenes/SendScene2.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 1100cfebc2f..bd89fcde0e7 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -1293,28 +1293,28 @@ const SendComponent = (props: Props): React.ReactElement => { } catch (e: unknown) { resetSlider() console.log(e) - const error = e instanceof Error ? e : new Error(String(e)) + const errorCasted = e instanceof Error ? e : new Error(String(e)) let message = sprintf( lstrings.transaction_failure_message, - error.message + errorCasted.message ) - error.message = 'broadcastError' - if (error.name === 'ErrorAlgoRecipientNotActivated') { + errorCasted.message = 'broadcastError' + if (errorCasted.name === 'ErrorAlgoRecipientNotActivated') { message = sprintf( lstrings.send_confirmation_algo_recipient_not_activated_s, currencyCode ) } - if (error.name === 'ErrorEosInsufficientCpu') { + if (errorCasted.name === 'ErrorEosInsufficientCpu') { message = lstrings.send_confirmation_eos_error_cpu - } else if (error.name === 'ErrorEosInsufficientNet') { + } else if (errorCasted.name === 'ErrorEosInsufficientNet') { message = lstrings.send_confirmation_eos_error_net - } else if (error.name === 'ErrorEosInsufficientRam') { + } else if (errorCasted.name === 'ErrorEosInsufficientRam') { message = lstrings.send_confirmation_eos_error_ram } else if ( - error instanceof FioError && - error.code === FIO_NO_BUNDLED_ERR_CODE && + errorCasted instanceof FioError && + errorCasted.code === FIO_NO_BUNDLED_ERR_CODE && currencyCode !== FIO_STR ) { const answer = await Airship.show<'ok' | 'cancel' | undefined>( From 8a3e4170523e97f35480709add11333d76454dbf Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Wed, 12 Nov 2025 16:08:50 -0800 Subject: [PATCH 3/8] Rename caught error --- src/components/scenes/SendScene2.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index bd89fcde0e7..fe5ddba9c78 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -1290,10 +1290,11 @@ const SendComponent = (props: Props): React.ReactElement => { /> )).catch(() => {}) } - } catch (e: unknown) { + } catch (error: unknown) { resetSlider() - console.log(e) - const errorCasted = e instanceof Error ? e : new Error(String(e)) + console.log(error) + const errorCasted = + error instanceof Error ? error : new Error(String(error)) let message = sprintf( lstrings.transaction_failure_message, From 08628e2e562ee8f07259da4790f3d69599cdf4a6 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 13 Nov 2025 13:28:16 -0800 Subject: [PATCH 4/8] Fix setComplete race condition in SafeSlider --- eslint.config.mjs | 2 +- src/components/themed/SafeSlider.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9db0b0ce7c1..84fabf6a182 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -354,7 +354,7 @@ export default [ 'src/components/themed/MenuTabs.tsx', 'src/components/themed/ModalParts.tsx', 'src/components/themed/PinDots.tsx', - 'src/components/themed/SafeSlider.tsx', + 'src/components/themed/SceneFooterWrapper.tsx', 'src/components/themed/SceneHeader.tsx', diff --git a/src/components/themed/SafeSlider.tsx b/src/components/themed/SafeSlider.tsx index 9030fde87cc..99ef2798172 100644 --- a/src/components/themed/SafeSlider.tsx +++ b/src/components/themed/SafeSlider.tsx @@ -8,7 +8,7 @@ import Animated, { withTiming } from 'react-native-reanimated' import Entypo from 'react-native-vector-icons/Entypo' -import { runOnJS } from 'react-native-worklets' +import { scheduleOnRN } from 'react-native-worklets' import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' @@ -60,13 +60,13 @@ export const SafeSlider: React.FC = props => { setCompleted(false) }) const handleComplete = (): void => { + setCompleted(true) triggerHaptic('impactMedium') onSlidingComplete(() => { resetSlider() })?.catch((err: unknown) => { showError(err) }) - setCompleted(true) } const gesture = Gesture.Pan() @@ -81,7 +81,7 @@ export const SafeSlider: React.FC = props => { }) .onEnd(event => { if (translateX.value < COMPLETE_POINT) { - runOnJS(handleComplete)() + scheduleOnRN(handleComplete) } else { translateX.value = withTiming(upperBound, { duration: 500, From b4d6ea94855cfe1a8afe98bf1765183332aa8628 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 13 Nov 2025 13:57:00 -0800 Subject: [PATCH 5/8] Remove hasNotifications from SendScene2 --- .../__snapshots__/SendScene2.ui.test.tsx.snap | 25678 ++++++++-------- src/components/scenes/SendScene2.tsx | 1 - 2 files changed, 12544 insertions(+), 13135 deletions(-) diff --git a/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap index a21e44c0de1..7db6ab144a9 100644 --- a/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/SendScene2.ui.test.tsx.snap @@ -1,81 +1,146 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`SendScene2 1 spendTarget 1`] = ` -[ - + - + - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + - - + + + + - - - - Send from Wallet - - - My Bitcoin (BTC) - - - + + My Bitcoin (BTC) + + + + - -  - - +  + - + + + - - + Send to Address + + - Send to Address + some pub address - + + + - +  + + + + + + + + + Amount: + + - some pub address - - + undefined, + ], + null, + ] + } + > + 0.00001234 BTC + + + € 0.23 + -  +  - + + + + + + + + + - + + Add Another Address + + + - - - - Amount: - - - 0.00001234 BTC - - - € 0.23 - - - - -  - - - +  + - + + + - - - - Add Another Address - - - + - -  - - + 0 (0) + - - - - - - - - - - - Network Fee: - - - 0 (0) - - - - -  - - +  + - - - + + + + + - + + - +  + + + - -  - - - - Enter an Amount - - + ], + null, + ] + } + > + Enter an Amount + - , - + +`; + +exports[`SendScene2 1 spendTarget with info tiles 1`] = ` + + , -] -`; - -exports[`SendScene2 1 spendTarget with info tiles 1`] = ` -[ - - - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + - - + + + + - - - - Send from Wallet - - - My Bitcoin (BTC) - - - + + My Bitcoin (BTC) + + + + - -  - - +  + - + + + - - - + + - Send to Address + some pub address - + + + +  + + + + + + + + - + - some pub address - - + undefined, + ], + null, + ] + } + > + 0.00001234 BTC + + + € 0.23 + -  +  - - - - - - Amount: - - - 0.00001234 BTC - - - € 0.23 - - - - -  - - - - - + + + - - - - Add Another Address - - - + + + - -  - - +  + - + + + - - - - Network Fee: - - - 0 (0) - - - + - -  - - + 0 (0) + - - - - - info tile label 1 - - - info tile value 1 - - +  + - + + "borderBottomColor": "rgba(255, 255, 255, .1)", + "borderBottomWidth": 1, + "height": 1, + }, + { + "margin": 11, + }, + ] + } + /> + - - - info tile label 2 - - - info tile value 2 - - + info tile label 1 + + + info tile value 1 + - - - + { + "margin": 11, + }, + ] + } + /> + + + + info tile label 2 + + + info tile value 2 + + + + + - - + + + + + - + - + - -  - - - + + - Enter an Amount - - + { + "color": "#888888", + }, + ], + null, + ] + } + > + Enter an Amount + - , - + +`; + +exports[`SendScene2 2 spendTargets 1`] = ` + + , -] -`; - -exports[`SendScene2 2 spendTargets 1`] = ` -[ - - - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + - - + + - + + + - + + + Send from Wallet + + + My Bitcoin (BTC) + + + + +  + + + + + + + + + + + - Send from Wallet + Send To some pub address - My Bitcoin (BTC) + Amount: 0.00001234 BTC (€ 0.23) -  +  - - - - - - - + + - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - - -  - - + some pub address 2 + - + > + +  + + + + + - Send to Address 2 + Amount: 2 - - - some pub address 2 - - + undefined, + ], + null, + ] + } + > + 0.00012345 BTC + + + € 2.26 + -  +  - + + + + + + + + + - + + Add Another Address + + + - - - - Amount: 2 - - - 0.00012345 BTC - - - € 2.26 - - - - -  - - - +  + - + + + - - - - Add Another Address - - - + - -  - - + 0 (0) + - - - - - - - - - - - Network Fee: - - - 0 (0) - - - - -  - - +  + - - - - + + + + + + - + - + - -  - - - + + - Enter an Amount - - + { + "color": "#888888", + }, + ], + null, + ] + } + > + Enter an Amount + - , - + +`; + +exports[`SendScene2 2 spendTargets hide tiles 1`] = ` + + , -] -`; - -exports[`SendScene2 2 spendTargets hide tiles 1`] = ` -[ - - - - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + - - + + + + - - - - Send from Wallet - - - My Bitcoin (BTC) - - - + + My Bitcoin (BTC) + + + + - -  - - +  + - + + + - - + - - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - + + Amount: 0.00001234 BTC (€ 0.23) + + + + - -  - - +  + - + + + - - - - Amount: 2 - - + - 0.00012345 BTC - - - € 2.26 - - - - -  - - - - - - - - - - - - - - - - Network Fee: + 0.00012345 BTC - 0 (0) + € 2.26 -  +  - + - - - + nativeID="41" + /> + - - -  - - - + Network Fee: + + + 0 (0) + + + - Enter an Amount - + "busy": undefined, + "checked": undefined, + "disabled": undefined, + "expanded": undefined, + "selected": undefined, + } + } + accessibilityValue={ + { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } + accessible={false} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + { + "alignItems": "flex-end", + "height": "100%", + "justifyContent": "center", + "opacity": 1, + "position": "absolute", + "right": 0, + "width": "100%", + } + } + > + +  + + + + - , + + , -] -`; - -exports[`SendScene2 2 spendTargets hide tiles 2`] = ` -[ - - - - - - - - - - - - - - - - - - + - - - - - - - Send from Wallet - - - My Bitcoin (BTC) - - - - -  - - - - +  + - - + Enter an Amount + + + + + + +`; + +exports[`SendScene2 2 spendTargets hide tiles 2`] = ` + + + + + + + + + - - - + - - - - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - - -  - - - - - + + + + + + + + + + + + "marginBottom": 11, + "marginLeft": 11, + "marginRight": 11, + "marginTop": 11, + }, + ] + } + > - - - Send to Address 2 - - + - - some pub address 2 - - - - + My Bitcoin (BTC) + + + + - -  - - +  + - + + } + nativeID="48" + > + - - Network Fee: + Send To some pub address - 0 (0) + Amount: 0.00001234 BTC (€ 0.23) +  + + + + + + + + + Send to Address 2 + + + -  + some pub address 2 + + +  + + - + - - - + nativeID="51" + /> + - - -  - - - + Network Fee: + + + 0 (0) + + + - Enter an Amount - + "busy": undefined, + "checked": undefined, + "disabled": undefined, + "expanded": undefined, + "selected": undefined, + } + } + accessibilityValue={ + { + "max": undefined, + "min": undefined, + "now": undefined, + "text": undefined, + } + } + accessible={false} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + { + "alignItems": "flex-end", + "height": "100%", + "justifyContent": "center", + "opacity": 1, + "position": "absolute", + "right": 0, + "width": "100%", + } + } + > + +  + + + - - , - , -] -`; - -exports[`SendScene2 2 spendTargets hide tiles 3`] = ` -[ + "randomizeDelay": false, + "reduceMotionV": "system", + } + } + jestAnimatedProps={ + { + "value": {}, + } + } + jestAnimatedStyle={ + { + "value": {}, + } + } + layout={ + LinearTransition { + "build": [Function], + "durationV": 300, + "randomizeDelay": false, + "reduceMotionV": "system", + } + } + nativeID="53" + /> + + - - - - - - - - - - - - - - - - - - - - - - - - Send from Wallet - - - My Bitcoin (BTC) - - - + - -  - - - - + "translateX": 240, + }, + ], + }, + } + } + jestInlineStyle={ + [ + { + "alignItems": "center", + "backgroundColor": "#00f1a2", + "borderRadius": 27.5, + "height": 55, + "justifyContent": "center", + "width": 55, + "zIndex": 5, + }, + { + "backgroundColor": "#888888", + }, + ] + } + nativeID="56" + style={ + [ + { + "alignItems": "center", + "backgroundColor": "#00f1a2", + "borderRadius": 27.5, + "height": 55, + "justifyContent": "center", + "width": 55, + "zIndex": 5, + }, + { + "backgroundColor": "#888888", + }, + { + "transform": [ + { + "translateX": 240, + }, + ], + }, + ] + } + > + +  + - - + Enter an Amount + + + + + + +`; + +exports[`SendScene2 2 spendTargets hide tiles 3`] = ` + + + + + + + + + + + + + + + + + + + + - + + Send from Wallet + + + My Bitcoin (BTC) + + + - - - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - - -  - - - +  + - + + } + nativeID="58" + > + - - Network Fee: + Send To some pub address - 0 (0) + Amount: 0.00001234 BTC (€ 0.23) - -  - - + +  + + + + + + + + + + + + + + + + Network Fee: + + + 0 (0) + + + + +  + - - - + + + + + - + - + - -  - - - + + - Enter an Amount - - + { + "color": "#888888", + }, + ], + null, + ] + } + > + Enter an Amount + - , - + +`; + +exports[`SendScene2 2 spendTargets lock tiles 1`] = ` + + , -] -`; - -exports[`SendScene2 2 spendTargets lock tiles 1`] = ` -[ - - - + + + + - - - - - + - + + + + + + + + + - + + - - - - - + + + Send from Wallet + + + My Bitcoin (BTC) + + + + +  + + + + + + + - + layout={ + LinearTransition { + "build": [Function], + "durationV": 300, + "randomizeDelay": false, + "reduceMotionV": "system", + } + } + nativeID="68" + > + - - Send from Wallet + Send To some pub address - My Bitcoin (BTC) + Amount: 0.00001234 BTC (€ 0.23) -  +  - - - - - - - + - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - + - -  - - + some pub address 2 + - + + + } + nativeID="71" + > - Send to Address 2 + Amount: 2 - - - some pub address 2 - - + undefined, + ], + null, + ] + } + > + 0.00012345 BTC + + + € 2.26 + - - - - - - - Amount: 2 - - - 0.00012345 BTC - - - € 2.26 - - - - -  - - +  + - + - + + + - - - - Network Fee: - - - 0 (0) - - - + + 0 (0) + + + + - -  - - +  + - - - + + + + + + - - - -  - - - + + - Enter an Amount - - + { + "color": "#888888", + }, + ], + null, + ] + } + > + Enter an Amount + - , - + +`; + +exports[`SendScene2 2 spendTargets lock tiles 2`] = ` + + , -] -`; - -exports[`SendScene2 2 spendTargets lock tiles 2`] = ` -[ - - - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + + + - + layout={ + LinearTransition { + "build": [Function], + "durationV": 300, + "randomizeDelay": false, + "reduceMotionV": "system", + } + } + nativeID="78" + > + - - - - Send from Wallet - - - My Bitcoin (BTC) - - - + - -  - - + My Bitcoin (BTC) + + + + +  + - + + + - - + - - - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - - - - + Send To some pub address + + - Send to Address 2 + Amount: 0.00001234 BTC (€ 0.23) - + + + + + + + Send to Address 2 + + - - some pub address 2 - - - - -  + some pub address 2 - - + +  + + + + + + - - - Amount: 2 - - - 0.00012345 BTC - - + - € 2.26 - - + ], + null, + ] + } + > + 0.00012345 BTC + + + € 2.26 + - + - + + + - - - - Network Fee: - - - 0 (0) - - - + + 0 (0) + + + + - -  - - +  + - - - + + + + + + - - - -  - - - + + - Enter an Amount - - + { + "color": "#888888", + }, + ], + null, + ] + } + > + Enter an Amount + - , - + +`; + +exports[`SendScene2 2 spendTargets lock tiles 3`] = ` + + , -] -`; - -exports[`SendScene2 2 spendTargets lock tiles 3`] = ` -[ - - - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + + + - + nativeID="89" + > + - - - - Send from Wallet - - - My Bitcoin (BTC) - - - + + My Bitcoin (BTC) + + + + - -  - - +  + - + + + - - + - - - - Send To some pub address - - - Amount: 0.00001234 BTC (€ 0.23) - - - - - - - Send to Address 2 + Send To some pub address - - - some pub address 2 - - + Amount: 0.00001234 BTC (€ 0.23) + - + + + - - - + + - - Amount: 2 - - - 0.00012345 BTC - - - € 2.26 - - + some pub address 2 + - - - - - - - + - Network Fee: + Amount: 2 + 0.00012345 BTC + + - 0 (0) + € 2.26 - + + + + + + + + + + + + + Network Fee: + + + 0 (0) + + + + - -  - - +  + - - - + + + + + - + - - -  - - + ], + }, + ] + } + > - Enter an Amount +  + + Enter an Amount + - , - + +`; + +exports[`SendScene2 Render SendScene 1`] = ` + + , -] -`; - -exports[`SendScene2 Render SendScene 1`] = ` -[ - - - + + + + - - - - - - - + - + } + propList={ + [ + "fill", + ] + } + r={238.7781690140845} + /> - - - + + + - - + + + + - - + Send from Wallet + + + My Bitcoin (BTC) + + + + - - Send from Wallet - - - My Bitcoin (BTC) - - - - -  - - +  + - + + + - - - + - Send to Address - - + - +  + + - -  - - - Enter - - - + + + +  + + - -  - - - Scan - - - + + + +  + + - -  - - - Paste - - + Paste + - + - + - + + - + + + - +  + + + > + Scam Warning + + • + + + Edge will not give financial advice. + + + + -  + • - Scam Warning + Cryptocurrency transactions are irreversible. - - - • - - - Edge will not give financial advice. - - - - - • - - - Cryptocurrency transactions are irreversible. - - - + - - • - - - Do not send money to people or organizations you do not know. - - + Do not send money to people or organizations you do not know. + - - If you have any questions or concerns regarding this send, please contact support@edge.app - + + If you have any questions or concerns regarding this send, please contact support@edge.app + - - - , + + , -] + /> + `; diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index fe5ddba9c78..ef81147bbcb 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -1578,7 +1578,6 @@ const SendComponent = (props: Props): React.ReactElement => { return ( Date: Thu, 13 Nov 2025 13:59:40 -0800 Subject: [PATCH 6/8] Render error as ErrorCard component on SendScene2 --- CHANGELOG.md | 1 + src/components/scenes/SendScene2.tsx | 68 ++++++++++++++++------------ 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b02e0b033db..02511459b9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - added: BitsOfGold and LibertyX ramp plugins. - added: Added remote ramp plugin filtering from info-server rollup. +- fixed: Allow for user-error reporting via `ErrorCard` in `SendScene2`. - fixed: Fixed broken `logEvent` tracking calls by adding the needed `dispatch`. - fixed: Localized display names for payment types in new buy/sell feature. - removed: Remove change quote tracking. diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index ef81147bbcb..21a2469b4fe 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -68,6 +68,7 @@ import { } from '../../util/utils' import { AlertCardUi4 } from '../cards/AlertCard' import { EdgeCard } from '../cards/EdgeCard' +import { ErrorCard, I18nError } from '../cards/ErrorCard' import type { AccentColors } from '../common/DotsBackground' import { EdgeAnim } from '../common/EdgeAnim' import { SceneWrapper } from '../common/SceneWrapper' @@ -104,7 +105,6 @@ import { } from '../tiles/AddressTile2' import { CountdownTile } from '../tiles/CountdownTile' import { EditableAmountTile } from '../tiles/EditableAmountTile' -import { ErrorTile } from '../tiles/ErrorTile' // TODO: Check contentPadding @@ -213,7 +213,7 @@ const SendComponent = (props: Props): React.ReactElement => { initMinNativeAmount ) const [expireDate, setExpireDate] = useState(initExpireDate) - const [error, setError] = useState(undefined) + const [error, setError] = useState(undefined) const [edgeTransaction, setEdgeTransaction] = useState(null) const [pinValue, setPinValue] = useState(undefined) @@ -257,6 +257,7 @@ const SendComponent = (props: Props): React.ReactElement => { async () => { if ( error != null && + error instanceof Error && error.name === 'PendingFundsError' && flipInputModalRef.current == null ) { @@ -664,7 +665,12 @@ const SendComponent = (props: Props): React.ReactElement => { } const handleTimeoutDone = useHandler((): void => { - setError(new Error(lstrings.send_address_expired_error_message)) + setError( + new I18nError( + lstrings.transaction_failure, + lstrings.send_address_expired_error_message + ) + ) }) const renderTimeout = (): React.ReactElement | null => { @@ -682,7 +688,7 @@ const SendComponent = (props: Props): React.ReactElement => { const renderError = (): React.ReactElement | null => { if (error != null && asMaybeNoAmountSpecifiedError(error) == null) { - return + return } return null } @@ -1291,7 +1297,6 @@ const SendComponent = (props: Props): React.ReactElement => { )).catch(() => {}) } } catch (error: unknown) { - resetSlider() console.log(error) const errorCasted = error instanceof Error ? error : new Error(String(error)) @@ -1341,16 +1346,9 @@ const SendComponent = (props: Props): React.ReactElement => { message = lstrings.transaction_failure_504_message } - Airship.show<'ok' | undefined>(bridge => ( - - )).catch(() => {}) + setError(new I18nError(lstrings.transaction_failure, message)) + } finally { + resetSlider() } } ) @@ -1434,7 +1432,8 @@ const SendComponent = (props: Props): React.ReactElement => { const { name } = cryptoDisplayDenomination setError( - new Error( + new I18nError( + lstrings.transaction_failure, sprintf( lstrings.error_spend_amount_less_then_min_s, `${minDisplayAmount} ${name}` @@ -1467,8 +1466,8 @@ const SendComponent = (props: Props): React.ReactElement => { flipInputModalRef.current?.setFees({ feeTokenId, feeNativeAmount }) flipInputModalRef.current?.setError(null) setError(undefined) - } catch (e: unknown) { - const error = e instanceof Error ? e : new Error(String(e)) + } catch (err: unknown) { + let error = err const insufficientFunds = asMaybeInsufficientFundsError(error) if (insufficientFunds != null) { const errorCurrencyCode = getCurrencyCode( @@ -1482,15 +1481,18 @@ const SendComponent = (props: Props): React.ReactElement => { errorCurrencyCode === 'ETH' && coreWallet.currencyInfo.pluginId !== 'ethereum' ) { - error.message = sprintf( - lstrings.insufficient_funds_2s, - errorCurrencyCode, - coreWallet.currencyInfo.displayName + error = new I18nError( + lstrings.transaction_failure, + sprintf( + lstrings.insufficient_funds_2s, + errorCurrencyCode, + coreWallet.currencyInfo.displayName + ) ) } else { - error.message = sprintf( - lstrings.stake_error_insufficient_s, - errorCurrencyCode + error = new I18nError( + lstrings.transaction_failure, + sprintf(lstrings.stake_error_insufficient_s, errorCurrencyCode) ) } @@ -1508,13 +1510,21 @@ const SendComponent = (props: Props): React.ReactElement => { } } - if (error.message === 'Unexpected pending transactions') { - error.message = lstrings.unexpected_pending_transactions_error + if ( + error instanceof Error && + error.message === 'Unexpected pending transactions' + ) { + error = new I18nError( + lstrings.transaction_failure, + lstrings.unexpected_pending_transactions_error + ) } setError(error) setEdgeTransaction(null) - flipInputModalRef.current?.setError(error.message) + const errorMessage = + error instanceof Error ? error.message : String(error) + flipInputModalRef.current?.setError(errorMessage) flipInputModalRef.current?.setFees({ feeNativeAmount: '', feeTokenId: null @@ -1611,7 +1621,6 @@ const SendComponent = (props: Props): React.ReactElement => { {renderAddressAmountPairs()} {renderTimeout()} - {renderError()} @@ -1629,6 +1638,7 @@ const SendComponent = (props: Props): React.ReactElement => { {renderScamWarning()} + {renderError()} Date: Thu, 13 Nov 2025 14:06:32 -0800 Subject: [PATCH 7/8] Propagate unknown errors from handleSliderComplete --- src/components/scenes/SendScene2.tsx | 41 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 21a2469b4fe..02a5691890d 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -1296,28 +1296,32 @@ const SendComponent = (props: Props): React.ReactElement => { /> )).catch(() => {}) } - } catch (error: unknown) { - console.log(error) - const errorCasted = - error instanceof Error ? error : new Error(String(error)) - - let message = sprintf( - lstrings.transaction_failure_message, - errorCasted.message - ) - errorCasted.message = 'broadcastError' + } catch (err: unknown) { + console.log(err) + const errorCasted = err instanceof Error ? err : new Error(String(err)) + let error = err + if (errorCasted.name === 'ErrorAlgoRecipientNotActivated') { - message = sprintf( + error = new I18nError( lstrings.send_confirmation_algo_recipient_not_activated_s, currencyCode ) } if (errorCasted.name === 'ErrorEosInsufficientCpu') { - message = lstrings.send_confirmation_eos_error_cpu + error = new I18nError( + lstrings.transaction_failure, + lstrings.send_confirmation_eos_error_cpu + ) } else if (errorCasted.name === 'ErrorEosInsufficientNet') { - message = lstrings.send_confirmation_eos_error_net + error = new I18nError( + lstrings.transaction_failure, + lstrings.send_confirmation_eos_error_net + ) } else if (errorCasted.name === 'ErrorEosInsufficientRam') { - message = lstrings.send_confirmation_eos_error_ram + error = new I18nError( + lstrings.transaction_failure, + lstrings.send_confirmation_eos_error_ram + ) } else if ( errorCasted instanceof FioError && errorCasted.code === FIO_NO_BUNDLED_ERR_CODE && @@ -1342,11 +1346,14 @@ const SendComponent = (props: Props): React.ReactElement => { await handleSliderComplete(resetSlider) return } - } else if (message.includes('504')) { - message = lstrings.transaction_failure_504_message + } else if (errorCasted.message.includes('504')) { + error = new I18nError( + lstrings.transaction_failure, + lstrings.transaction_failure_504_message + ) } - setError(new I18nError(lstrings.transaction_failure, message)) + setError(error) } finally { resetSlider() } From 2e64e3ddc3545698421cf64873b829ff586002ba Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 13 Nov 2025 14:14:06 -0800 Subject: [PATCH 8/8] Remove error from disableSlider definition We can do this safely because unknown errors from makeSpend will result in `edgeTransaction == null`. This will allow for the slide to be enabled on errors/unknown-errors which can occur but may be intermittent. --- src/components/scenes/SendScene2.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 02a5691890d..b52d05eeaae 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -1550,7 +1550,6 @@ const SendComponent = (props: Props): React.ReactElement => { if ( edgeTransaction == null || processingAmountChanged || - error != null || (zeroString(spendInfo.spendTargets[0].nativeAmount) && getSpecialCurrencyInfo(pluginId).allowZeroTx !== true) ) {