diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5f49df169a..f7c93bd9122 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,16 @@
## Unreleased (develop)
-## 4.43.0 (staging)
+## 4.43.2 (2025-02-15)
+
+- fixed: (Zano) Reject wrapped ETH addresses in address validation.
+
+## 4.43.1 (2025-02-13)
+
+- fixed: Missing 2-factor approve / deny scene on login
+- fixed: Security check notification not reappearing after dismissal
+
+## 4.43.0 (2025-02-09)
- added: `chooseCaip19Asset` EdgeProvider API for precise wallet selection using CAIP-19 identifiers
- added: EdgeSpend feature for gift card purchase via Phaze
@@ -16,7 +25,14 @@
- changed: ramps: Infinite buy support enabled
- fixed: iOS simulator builds for XCode 26
-## 4.42.0 (2025-01-19)
+## 4.42.1 (2026-01-28)
+
+- added: `chooseCaip19Asset` EdgeProvider API for precise wallet selection using CAIP-19 identifiers
+- added: Pass OS and app version details to core context for v2/coreRollup endpoint
+- added: EdgeSpend feature for gift card purchase via Phaze
+- changed: Append chain names to token codes in RampCreateScene
+
+## 4.42.0 (2026-01-19)
- added: Zcash buy/sell support with Banxa
- changed: ramps: Infinite buy support according to new API
diff --git a/eslint.config.mjs b/eslint.config.mjs
index fa2df2718a3..d237ea0a170 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -239,10 +239,6 @@ export default [
'src/components/rows/TxCryptoAmountRow.tsx',
'src/components/scenes/AssetSettingsScene.tsx',
'src/components/scenes/ChangeMiningFeeScene.tsx',
- 'src/components/scenes/ChangePasswordScene.tsx',
- 'src/components/scenes/ChangePinScene.tsx',
- 'src/components/scenes/ChangeUsernameScene.tsx',
- 'src/components/scenes/CoinRankingDetailsScene.tsx',
'src/components/scenes/ConfirmScene.tsx',
'src/components/scenes/CreateWalletAccountSelectScene.tsx',
@@ -285,19 +281,17 @@ export default [
'src/components/scenes/Loans/LoanDetailsScene.tsx',
'src/components/scenes/Loans/LoanManageScene.tsx',
'src/components/scenes/Loans/LoanStatusScene.tsx',
- 'src/components/scenes/LoginScene.tsx',
+
'src/components/scenes/ManageTokensScene.tsx',
'src/components/scenes/MigrateWalletCalculateFeeScene.tsx',
'src/components/scenes/MigrateWalletCompletionScene.tsx',
'src/components/scenes/NotificationCenterScene.tsx',
'src/components/scenes/NotificationScene.tsx',
- 'src/components/scenes/OtpRepairScene.tsx',
+
'src/components/scenes/OtpSettingsScene.tsx',
- 'src/components/scenes/PasswordRecoveryScene.tsx',
- 'src/components/scenes/PromotionSettingsScene.tsx',
- 'src/components/scenes/SecurityAlertsScene.tsx',
+ 'src/components/scenes/PromotionSettingsScene.tsx',
'src/components/scenes/SpendingLimitsScene.tsx',
'src/components/scenes/Staking/EarnScene.tsx',
@@ -312,7 +306,6 @@ export default [
'src/components/scenes/TransactionDetailsScene.tsx',
'src/components/scenes/TransactionsExportScene.tsx',
- 'src/components/scenes/UpgradeUsernameScreen.tsx',
'src/components/scenes/WalletRestoreScene.tsx',
'src/components/scenes/WcConnectionsScene.tsx',
@@ -330,7 +323,7 @@ export default [
'src/components/services/FioService.ts',
'src/components/services/LoanManagerService.ts',
'src/components/services/NetworkActivity.ts',
- 'src/components/services/PasswordReminderService.ts',
+
'src/components/services/PermissionsManager.tsx',
'src/components/services/Providers.tsx',
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 224265ebf73..5d419182a4d 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -15,13 +15,13 @@ PODS:
- disklet (0.5.2):
- React
- DoubleConversion (1.1.6)
- - edge-core-js (2.41.0):
+ - edge-core-js (2.41.3):
- React-Core
- - edge-currency-accountbased (4.71.1):
+ - edge-currency-accountbased (4.71.4):
- React-Core
- - edge-currency-plugins (3.8.10):
+ - edge-currency-plugins (3.8.11):
- React-Core
- - edge-exchange-plugins (2.40.4):
+ - edge-exchange-plugins (2.40.5):
- React-Core
- edge-login-ui-rn (3.35.0):
- React-Core
@@ -1773,7 +1773,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-zano (0.2.5):
+ - react-native-zano (0.2.6):
- OpenSSL-Universal
- React-Core
- react-native-zcash (0.10.3):
@@ -3338,10 +3338,10 @@ SPEC CHECKSUMS:
CNIOWindows: 3047f2d8165848a3936a0a755fee27c6b5ee479b
disklet: 8a20bf8a568635b6e6bb8f93297dac13ee5cef98
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
- edge-core-js: c4d5806062e266cc90054901887784a29b60be31
- edge-currency-accountbased: a547885f96f0b0d96efe1bb7b885c7b0985b83dd
- edge-currency-plugins: 6b3341707a6a5c74f837a012768dd2f6c55a691b
- edge-exchange-plugins: f31912c54a50852bd02077b795aace0e0bee8365
+ edge-core-js: 60ad7f9a59418f6bf90eab9bfc66b18d7877c7f1
+ edge-currency-accountbased: 9ca740b8330e909dfcb0cda3e81870a8a3980895
+ edge-currency-plugins: 002abe3c0d4fb6040046ec779b208c6aa83bee83
+ edge-exchange-plugins: db3cfb3686abf0e31c5f2d0f9c0ce8c40b7cf9c9
edge-login-ui-rn: 74294715a31efa2f79a916a3f89bf47a99cec102
EXConstants: 98bcf0f22b820f9b28f9fee55ff2daededadd2f8
Expo: 43d9e0c3108cc3a1c2739743e9b51086144ee4b0
@@ -3419,7 +3419,7 @@ SPEC CHECKSUMS:
react-native-safari-view: 07dc856a2663fef31eaca6beb79b111b8f6cf1f2
react-native-safe-area-context: 83e0ac3d023997de1c2e035af907cc4dc05f718c
react-native-webview: 69c118d283fccfbc4fca0cd680e036ff3bf188fa
- react-native-zano: 4af16a60e81819be92c425a373db3fa7908b10fb
+ react-native-zano: dd0967aaa0bbfe347c0ea5af15bc94f2d059fbf7
react-native-zcash: 34b665ed972547f6d09ad47aad706c5daa80bf09
React-NativeModulesApple: df8e5bc59e78ca3040ffbf41336889f3bd0fad68
React-oscompat: ef5df1c734f19b8003e149317d041b8ce1f7d29c
diff --git a/package.json b/package.json
index f87a3cd1b98..e4fbce0a31e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "edge-react-gui",
- "version": "4.43.0",
+ "version": "4.43.2",
"private": true,
"description": "Edge Wallet React GUI",
"homepage": "https://edge.app",
@@ -105,11 +105,11 @@
"deprecated-react-native-prop-types": "^5.0.0",
"detect-bundler": "^1.1.0",
"disklet": "^0.5.2",
- "edge-core-js": "^2.41.0",
- "edge-currency-accountbased": "^4.71.1",
+ "edge-core-js": "^2.41.3",
+ "edge-currency-accountbased": "^4.71.4",
"edge-currency-monero": "^2.2.0",
- "edge-currency-plugins": "^3.8.10",
- "edge-exchange-plugins": "^2.40.4",
+ "edge-currency-plugins": "^3.8.11",
+ "edge-exchange-plugins": "^2.40.5",
"edge-info-server": "^3.10.0",
"edge-login-ui-rn": "^3.35.0",
"ethers": "^5.7.2",
@@ -171,7 +171,7 @@
"react-native-webview": "^13.15.0",
"react-native-wheel-picker-android": "^2.0.6",
"react-native-worklets": "^0.6.1",
- "react-native-zano": "^0.2.5",
+ "react-native-zano": "^0.2.6",
"react-native-zcash": "^0.10.3",
"react-redux": "^8.1.1",
"redux": "^4.2.1",
diff --git a/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
index c9f8020455f..77835d74bc4 100644
--- a/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
@@ -5265,206 +5265,6 @@ exports[`SettingsScene should render SettingsScene 1`] = `
-
-
-
- Light Mode
-
-
-
-
-
-
-
-
-
-
-
-
{
- return await writeDeviceSettings({ ...deviceSettings, isLightTheme })
+export const writeThemeMode = async (themeMode: ThemeMode) => {
+ return await writeDeviceSettings({ ...deviceSettings, themeMode })
}
/**
diff --git a/src/actions/LoginActions.tsx b/src/actions/LoginActions.tsx
index a7baad4fac3..613743aeb01 100644
--- a/src/actions/LoginActions.tsx
+++ b/src/actions/LoginActions.tsx
@@ -25,7 +25,11 @@ import { lstrings } from '../locales/strings'
import type { WalletCreateItem } from '../selectors/getCreateWalletList'
import { config } from '../theme/appConfig'
import type { Dispatch, GetState, ThunkAction } from '../types/reduxTypes'
-import type { EdgeAppSceneProps, NavigationBase } from '../types/routerTypes'
+import type {
+ EdgeAppSceneProps,
+ NavigationBase,
+ RootSceneProps
+} from '../types/routerTypes'
import { currencyCodesToEdgeAssets } from '../util/CurrencyInfoHelpers'
import { logActivity } from '../util/logger'
import { logEvent, trackError } from '../util/tracking'
@@ -52,12 +56,11 @@ const PER_WALLET_TIMEOUT = 5000
const MIN_CREATE_WALLET_TIMEOUT = 20000
export function initializeAccount(
- navigation: NavigationBase,
+ navigation: RootSceneProps<'login'>['navigation'],
account: EdgeAccount
): ThunkAction> {
return async (dispatch, getState) => {
const { newAccount } = account
- const rootNavigation = getRootNavigation(navigation)
// Load all settings upfront so we can navigate immediately after LOGIN
const [syncedSettings, localSettings] = await Promise.all([
@@ -80,7 +83,7 @@ export function initializeAccount(
// Navigate immediately - all settings are now in Redux
if (newAccount) {
await navigateToNewAccountFlow(
- rootNavigation,
+ navigation,
account,
syncedSettings,
referralPromise,
@@ -88,7 +91,7 @@ export function initializeAccount(
getState
)
} else {
- navigateToExistingAccountHome(rootNavigation, referralPromise)
+ navigateToExistingAccountHome(navigation, referralPromise)
}
performance.mark('loginEnd', { detail: { isNewAccount: newAccount } })
@@ -143,6 +146,9 @@ export function initializeAccount(
// Check for security alerts:
if (hasSecurityAlerts(account)) {
+ // This is not the normal security alerts scene!
+ // Since we only have access to the root navigator,
+ // this scene exists as a peer of the main app:
navigation.push('securityAlerts')
hideSurvey = true
}
@@ -208,7 +214,7 @@ export function initializeAccount(
* Navigate to wallet creation flow for new accounts.
*/
async function navigateToNewAccountFlow(
- rootNavigation: NavigationBase,
+ navigation: RootSceneProps<'login'>['navigation'],
account: EdgeAccount,
syncedSettings: SyncedAccountSettings,
referralPromise: Promise,
@@ -279,7 +285,7 @@ async function navigateToNewAccountFlow(
)
}
- rootNavigation.replace('edgeApp', {
+ navigation.replace('edgeApp', {
screen: 'edgeAppStack',
params: {
screen: 'createWalletSelectCryptoNewAccount',
@@ -296,11 +302,11 @@ async function navigateToNewAccountFlow(
* Navigate to home screen for existing accounts.
*/
function navigateToExistingAccountHome(
- rootNavigation: NavigationBase,
+ navigation: RootSceneProps<'login'>['navigation'],
referralPromise: Promise
): void {
const { defaultScreen } = getDeviceSettings()
- rootNavigation.replace('edgeApp', {
+ navigation.replace('edgeApp', {
screen: 'edgeAppStack',
params: {
screen: 'edgeTabs',
diff --git a/src/app.ts b/src/app.ts
index b1e3950dde1..005681c2ce0 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -9,7 +9,7 @@ import NetInfo from '@react-native-community/netinfo'
import * as Sentry from '@sentry/react-native'
import { Buffer } from 'buffer'
import { asObject, asString } from 'cleaners'
-import { InteractionManager, LogBox } from 'react-native'
+import { Appearance, InteractionManager, LogBox } from 'react-native'
import { getVersion } from 'react-native-device-info'
import RNFS from 'react-native-fs'
@@ -285,16 +285,37 @@ if (ENV.DEBUG_THEME) {
})
}
+// Theme initialization and system theme listener
initDeviceSettings()
.then(() => {
- const { isLightTheme } = getDeviceSettings()
- // Only change theme if light mode is enabled (dark is already the default)
- if (isLightTheme) {
+ const { themeMode } = getDeviceSettings()
+
+ // Apply theme based on mode setting at startup
+ let shouldUseLightTheme = false
+ if (themeMode === 'light') {
+ shouldUseLightTheme = true
+ } else if (themeMode === 'system') {
+ shouldUseLightTheme = Appearance.getColorScheme() !== 'dark'
+ }
+ // Only change theme if light mode is needed (dark is already the default)
+ if (shouldUseLightTheme) {
// Defer until after React render cycle completes
InteractionManager.runAfterInteractions(() => {
changeTheme(config.lightTheme)
})
}
+
+ // Global listener for OS theme changes (active when themeMode is 'system')
+ Appearance.addChangeListener(({ colorScheme }) => {
+ const { themeMode: currentMode } = getDeviceSettings()
+ if (currentMode === 'system') {
+ InteractionManager.runAfterInteractions(() => {
+ const newTheme =
+ colorScheme === 'dark' ? config.darkTheme : config.lightTheme
+ changeTheme(newTheme)
+ })
+ }
+ })
})
.catch(err => {
console.log(err)
diff --git a/src/components/Main.tsx b/src/components/Main.tsx
index f2424a4d16d..82e1977a731 100644
--- a/src/components/Main.tsx
+++ b/src/components/Main.tsx
@@ -1280,6 +1280,11 @@ export const Main: React.FC = () => {
return
}}
+
+
{navigation == null ? null : (
diff --git a/src/components/cards/ErrorCard.tsx b/src/components/cards/ErrorCard.tsx
index bc716fa2a4c..c288d587450 100644
--- a/src/components/cards/ErrorCard.tsx
+++ b/src/components/cards/ErrorCard.tsx
@@ -1,3 +1,4 @@
+import Clipboard from '@react-native-clipboard/clipboard'
import * as React from 'react'
import { useHandler } from '../../hooks/useHandler'
@@ -6,7 +7,7 @@ import { useSelector } from '../../types/reactRedux'
import { normalizeError } from '../../util/normalizeError'
import { trackError } from '../../util/tracking'
import { RawTextModal } from '../modals/RawTextModal'
-import { Airship } from '../services/AirshipInstance'
+import { Airship, showToast } from '../services/AirshipInstance'
import { AlertCardUi4 } from './AlertCard'
/**
@@ -36,17 +37,41 @@ export const ErrorCard: React.FC = props => {
const { error } = props
const isDevMode = useSelector(state => state.ui.settings.developerModeOn)
- const [reportSent, setReportSent] = React.useState(false)
+ const [errorIdentifier, setErrorIdentifier] = React.useState<
+ { eventId: string } | { aggregateId: string }
+ >()
+
+ // Reset error identifier when error changes
+ React.useEffect(() => {
+ setErrorIdentifier(undefined)
+ }, [error])
const handleReportError = useHandler((): void => {
if (error != null) {
- trackError(error, 'AlertDropdown_Report', {
+ const errorIdentifier = trackError(error, 'AlertDropdown_Report', {
userReportedError: true
})
- setReportSent(true)
+ setErrorIdentifier(errorIdentifier)
+ }
+ })
+
+ const handleCopyEventId = useHandler((): void => {
+ if (errorIdentifier != null) {
+ const id =
+ 'eventId' in errorIdentifier
+ ? errorIdentifier.eventId
+ : errorIdentifier.aggregateId
+ Clipboard.setString(id)
+ showToast(lstrings.fragment_error_report_id_copied)
}
})
+ const handleShowError = useHandler(async (): Promise => {
+ await Airship.show(bridge => (
+
+ ))
+ })
+
// Happy path
if (error instanceof I18nError) {
return (
@@ -54,33 +79,50 @@ export const ErrorCard: React.FC = props => {
)
}
+ const reportSent = errorIdentifier != null
+
+ const isAggregateError =
+ errorIdentifier != null && 'aggregateId' in errorIdentifier
+ const copyLabel = isAggregateError
+ ? lstrings.fragment_copy_aggregate_id
+ : lstrings.fragment_copy_event_id
+
const buttonProps =
isDevMode || __DEV__
? {
- label: 'Show Error',
- onPress: async () => {
- await Airship.show(bridge => (
-
- ))
- }
+ label: lstrings.string_show_error,
+ onPress: handleShowError
+ }
+ : reportSent
+ ? {
+ label: copyLabel,
+ onPress: handleCopyEventId
}
: {
- label: reportSent
- ? lstrings.string_report_sent
- : lstrings.string_report_error,
- disabled: reportSent,
+ label: lstrings.string_report_error,
onPress: handleReportError
}
+ const idLabel =
+ errorIdentifier != null && 'eventId' in errorIdentifier
+ ? lstrings.fragment_event_id
+ : lstrings.fragment_aggregate_id
+ const idValue =
+ errorIdentifier != null
+ ? 'eventId' in errorIdentifier
+ ? errorIdentifier.eventId
+ : errorIdentifier.aggregateId
+ : ''
+ const bodyText = reportSent
+ ? `${lstrings.string_report_sent}\n\n${idLabel}: ${idValue}`
+ : lstrings.error_generic_message
+
// Unhappy path
return (
)
diff --git a/src/components/cards/GiftCardDisplayCard.tsx b/src/components/cards/GiftCardDisplayCard.tsx
index b69e9ae44d9..9ce751064ba 100644
--- a/src/components/cards/GiftCardDisplayCard.tsx
+++ b/src/components/cards/GiftCardDisplayCard.tsx
@@ -249,6 +249,7 @@ const getStyles = cacheStyles((theme: Theme) => ({
pendingText: {
fontSize: theme.rem(0.875),
fontFamily: theme.fontFaceMedium,
- ...theme.embossedTextShadow
+ ...theme.embossedTextShadow,
+ color: theme.giftCardText
}
}))
diff --git a/src/components/icons/IconBadge.tsx b/src/components/icons/IconBadge.tsx
index 905a6a544a7..b95b79c1733 100644
--- a/src/components/icons/IconBadge.tsx
+++ b/src/components/icons/IconBadge.tsx
@@ -83,12 +83,14 @@ const getStyles = cacheStyles((theme: Theme) => {
justifyContent: 'center'
},
textIos: {
+ color: theme.badgeText,
fontSize: theme.rem(0.5) - 1,
fontFamily: theme.fontFaceBold,
marginLeft: 2,
marginRight: 1
},
textAndroid: {
+ color: theme.badgeText,
fontSize: theme.rem(0.5) - 1,
fontFamily: theme.fontFaceBold,
marginTop: 1.5,
@@ -98,7 +100,7 @@ const getStyles = cacheStyles((theme: Theme) => {
width: theme.rem(0.15),
height: theme.rem(0.15),
borderRadius: theme.rem(0.15 / 2),
- backgroundColor: theme.primaryText
+ backgroundColor: theme.badgeText
}
}
})
diff --git a/src/components/scenes/ChangePasswordScene.tsx b/src/components/scenes/ChangePasswordScene.tsx
index f21dc9100ab..208f77c3ebd 100644
--- a/src/components/scenes/ChangePasswordScene.tsx
+++ b/src/components/scenes/ChangePasswordScene.tsx
@@ -10,7 +10,7 @@ import { SceneWrapper } from '../common/SceneWrapper'
interface Props extends EdgeAppSceneProps<'changePassword'> {}
-export const ChangePasswordScene = (props: Props) => {
+export const ChangePasswordScene: React.FC = props => {
const { navigation } = props
const account = useSelector(state => state.core.account)
const context = useSelector(state => state.core.context)
diff --git a/src/components/scenes/ChangePinScene.tsx b/src/components/scenes/ChangePinScene.tsx
index fd342afc225..f89f476b579 100644
--- a/src/components/scenes/ChangePinScene.tsx
+++ b/src/components/scenes/ChangePinScene.tsx
@@ -10,16 +10,16 @@ import { SceneWrapper } from '../common/SceneWrapper'
interface Props extends EdgeAppSceneProps<'changePin'> {}
-export const ChangePinScene = (props: Props) => {
+export const ChangePinScene: React.FC = props => {
const { navigation } = props
const account = useSelector(state => state.core.account)
const context = useSelector(state => state.core.context)
const dispatch = useDispatch()
- const handleComplete = () => {
+ const handleComplete = useHandler(() => {
logActivity(`PIN Changed: ${account.username}`)
navigation.goBack()
- }
+ })
const handleLogEvent = useHandler((event, values) => {
dispatch(logEvent(event, values))
diff --git a/src/components/scenes/ChangeUsernameScene.tsx b/src/components/scenes/ChangeUsernameScene.tsx
index 34f8fe0fdcd..8ace835384c 100644
--- a/src/components/scenes/ChangeUsernameScene.tsx
+++ b/src/components/scenes/ChangeUsernameScene.tsx
@@ -9,7 +9,7 @@ import { SceneWrapper } from '../common/SceneWrapper'
interface Props extends EdgeAppSceneProps<'changeUsername'> {}
-export const ChangeUsernameScene = (props: Props) => {
+export const ChangeUsernameScene: React.FC = props => {
const { navigation, route } = props
const { password } = route.params
const dispatch = useDispatch()
diff --git a/src/components/scenes/GiftCardPurchaseScene.tsx b/src/components/scenes/GiftCardPurchaseScene.tsx
index 1ab15e8b975..640bf3b9967 100644
--- a/src/components/scenes/GiftCardPurchaseScene.tsx
+++ b/src/components/scenes/GiftCardPurchaseScene.tsx
@@ -231,6 +231,9 @@ export const GiftCardPurchaseScene: React.FC = props => {
const [amountText, setAmountText] = React.useState(
hasFixedDenominations ? String(sortedDenominations[0]) : ''
)
+ const [amountInputError, setAmountInputError] = React.useState<
+ string | undefined
+ >()
// Update selection when denominations become available (e.g., after brand fetch)
React.useEffect(() => {
@@ -247,6 +250,7 @@ export const GiftCardPurchaseScene: React.FC = props => {
setMinimumWarning(null)
setProductUnavailable(false)
setError(null)
+ setAmountInputError(undefined)
// Only allow numbers and decimal point
const cleaned = text.replace(/[^0-9.]/g, '')
@@ -262,12 +266,38 @@ export const GiftCardPurchaseScene: React.FC = props => {
}
})
+ // Validate amount on blur for variable range cards
+ const handleAmountBlur = useHandler(() => {
+ if (!hasVariableRange || amountText === '') return
+
+ const parsed = parseFloat(amountText)
+ if (isNaN(parsed)) return
+
+ const fiatSymbol = getFiatSymbol(brand.currency)
+ if (parsed < minVal) {
+ setAmountInputError(
+ sprintf(
+ lstrings.card_amount_min_error_message_1s,
+ `${fiatSymbol}${minVal}`
+ )
+ )
+ } else if (parsed > maxVal) {
+ setAmountInputError(
+ sprintf(
+ lstrings.card_amount_max_error_message_1s,
+ `${fiatSymbol}${maxVal}`
+ )
+ )
+ }
+ })
+
// Handle MAX button press
const handleMaxPress = useHandler(() => {
if (hasVariableRange) {
setMinimumWarning(null)
setProductUnavailable(false)
setError(null)
+ setAmountInputError(undefined)
setAmountText(String(maxVal))
setSelectedAmount(maxVal)
}
@@ -366,22 +396,23 @@ export const GiftCardPurchaseScene: React.FC = props => {
const caip19 = tokenInfo.caip19
+ // Get currency code for display (used in warnings, success, and error messages)
+ const currencyCode =
+ tokenId != null
+ ? account.currencyConfig[wallet.currencyInfo.pluginId]?.allTokens[
+ tokenId
+ ]?.currencyCode ?? wallet.currencyInfo.currencyCode
+ : wallet.currencyInfo.currencyCode
+
// Check minimum amount for selected token
if (selectedAmount < tokenInfo.minimumAmountInUSD) {
- const currencyCode =
- tokenId != null
- ? account.currencyConfig[wallet.currencyInfo.pluginId]?.allTokens[
- tokenId
- ]?.currencyCode ?? wallet.currencyInfo.currencyCode
- : wallet.currencyInfo.currencyCode
-
setMinimumWarning({
header: sprintf(
- lstrings.gift_card_minimum_warning_header,
+ lstrings.gift_card_minimum_warning_header_1s,
currencyCode
),
footer: sprintf(
- lstrings.gift_card_minimum_warning_footer,
+ lstrings.gift_card_minimum_warning_footer_1s,
formatMinimumInBrandCurrency(tokenInfo.minimumAmountInUSD)
)
})
@@ -424,13 +455,6 @@ export const GiftCardPurchaseScene: React.FC = props => {
// Convert quantity to native amount (crypto amount to pay)
// The quantity is in the token's standard units (e.g., BTC, ETH)
- const currencyCode =
- tokenId != null
- ? account.currencyConfig[wallet.currencyInfo.pluginId]?.allTokens[
- tokenId
- ]?.currencyCode ?? wallet.currencyInfo.currencyCode
- : wallet.currencyInfo.currencyCode
-
const multiplier =
tokenId != null
? account.currencyConfig[wallet.currencyInfo.pluginId]?.allTokens[
@@ -568,23 +592,36 @@ export const GiftCardPurchaseScene: React.FC = props => {
return
}
- // Check for minimum amount error from API
+ // Check for minimum amount error from API (with specific minimum)
const minimumMatch = /Minimum cart cost should be above: ([\d.]+)/.exec(
errorMessage
)
+ // Check for generic "order too small" error (no specific minimum)
+ const isGenericMinimumError = errorMessage.includes(
+ 'Order amount is too small'
+ )
+
if (minimumMatch != null) {
const minimumUSD = parseFloat(minimumMatch[1])
setMinimumWarning({
header: sprintf(
- lstrings.gift_card_minimum_warning_header,
- 'this cryptocurrency'
+ lstrings.gift_card_minimum_warning_header_1s,
+ currencyCode
),
footer: sprintf(
- lstrings.gift_card_minimum_warning_footer,
+ lstrings.gift_card_minimum_warning_footer_1s,
formatMinimumInBrandCurrency(minimumUSD)
)
})
+ } else if (isGenericMinimumError) {
+ setMinimumWarning({
+ header: sprintf(
+ lstrings.gift_card_minimum_warning_header_1s,
+ currencyCode
+ ),
+ footer: lstrings.gift_card_minimum_warning_generic
+ })
} else {
// Show ErrorCard for other errors
setError(err)
@@ -725,10 +762,13 @@ export const GiftCardPurchaseScene: React.FC = props => {
{}
let firstRun = true
-export function LoginScene(props: Props) {
+export const LoginScene: React.FC = props => {
const { navigation, route } = props
const {
experimentConfig,
@@ -78,11 +77,9 @@ export function LoginScene(props: Props) {
context
.loginWithPIN(YOLO_USERNAME, YOLO_PIN)
.then(async account => {
- await dispatch(
- initializeAccount(navigation as NavigationBase, account)
- )
+ await dispatch(initializeAccount(navigation, account))
})
- .catch(error => {
+ .catch((error: unknown) => {
showError(error)
})
}
@@ -90,11 +87,9 @@ export function LoginScene(props: Props) {
context
.loginWithPassword(YOLO_USERNAME, YOLO_PASSWORD)
.then(async account => {
- await dispatch(
- initializeAccount(navigation as NavigationBase, account)
- )
+ await dispatch(initializeAccount(navigation, account))
})
- .catch(error => {
+ .catch((error: unknown) => {
showError(error)
})
}
@@ -111,11 +106,9 @@ export function LoginScene(props: Props) {
useLoginId: true
})
.then(async account => {
- await dispatch(
- initializeAccount(navigation as NavigationBase, account)
- )
+ await dispatch(initializeAccount(navigation, account))
})
- .catch(error => {
+ .catch((error: unknown) => {
showError(error)
})
}
@@ -129,8 +122,8 @@ export function LoginScene(props: Props) {
() => ({
callback() {
Keyboard.dismiss()
- showHelpModal(navigation as NavigationBase).catch(err => {
- showDevError(err)
+ showHelpModal(navigation as NavigationBase).catch((error: unknown) => {
+ showDevError(error)
})
},
text: lstrings.string_help
@@ -145,16 +138,14 @@ export function LoginScene(props: Props) {
: undefined
const handleLogin = useHandler((account: EdgeAccount) => {
- dispatch(initializeAccount(navigation as NavigationBase, account)).catch(
- (error: unknown) => {
- showError(error)
- }
- )
+ dispatch(initializeAccount(navigation, account)).catch((error: unknown) => {
+ showError(error)
+ })
})
const handleSendLogs = useHandler(() => {
- dispatch(showSendLogsModal()).catch(err => {
- showError(err)
+ dispatch(showSendLogsModal()).catch((error: unknown) => {
+ showError(error)
})
})
diff --git a/src/components/scenes/OtpRepairScene.tsx b/src/components/scenes/OtpRepairScene.tsx
index 26adc01aa6b..5437b02a281 100644
--- a/src/components/scenes/OtpRepairScene.tsx
+++ b/src/components/scenes/OtpRepairScene.tsx
@@ -15,7 +15,7 @@ export interface OtpRepairParams {
interface Props extends EdgeAppSceneProps<'otpRepair'> {}
-export const OtpRepairScene = (props: Props) => {
+export const OtpRepairScene: React.FC = props => {
const { navigation, route } = props
const { otpError } = route.params
const account = useSelector(state => state.core.account)
diff --git a/src/components/scenes/PasswordRecoveryScene.tsx b/src/components/scenes/PasswordRecoveryScene.tsx
index e74761c939b..b250581249b 100644
--- a/src/components/scenes/PasswordRecoveryScene.tsx
+++ b/src/components/scenes/PasswordRecoveryScene.tsx
@@ -10,7 +10,7 @@ import { SceneWrapper } from '../common/SceneWrapper'
interface Props extends EdgeAppSceneProps<'passwordRecovery'> {}
-export const ChangeRecoveryScene = (props: Props) => {
+export const ChangeRecoveryScene: React.FC = props => {
const { navigation } = props
const account = useSelector(state => state.core.account)
const context = useSelector(state => state.core.context)
diff --git a/src/components/scenes/SecurityAlertsScene.tsx b/src/components/scenes/SecurityAlertsScene.tsx
index 8c28804ff23..c2ebbec6b48 100644
--- a/src/components/scenes/SecurityAlertsScene.tsx
+++ b/src/components/scenes/SecurityAlertsScene.tsx
@@ -3,13 +3,15 @@ import * as React from 'react'
import { useHandler } from '../../hooks/useHandler'
import { useDispatch, useSelector } from '../../types/reactRedux'
-import type { EdgeAppSceneProps } from '../../types/routerTypes'
+import type { RootSceneProps } from '../../types/routerTypes'
import { logEvent } from '../../util/tracking'
import { SceneWrapper } from '../common/SceneWrapper'
-interface Props extends EdgeAppSceneProps<'securityAlerts'> {}
+// Danger: This scene is mounted in two places,
+// so this could also be `EdgeAppSceneProps<'securityAlerts'>`:
+interface Props extends RootSceneProps<'securityAlerts'> {}
-export const SecurityAlertsScene = (props: Props) => {
+export const SecurityAlertsScene: React.FC = props => {
const { navigation } = props
const account = useSelector(state => state.core.account)
const context = useSelector(state => state.core.context)
diff --git a/src/components/scenes/SettingsScene.tsx b/src/components/scenes/SettingsScene.tsx
index 0a5f073f494..b707dbffbe2 100644
--- a/src/components/scenes/SettingsScene.tsx
+++ b/src/components/scenes/SettingsScene.tsx
@@ -7,7 +7,7 @@ import {
isTouchEnabled
} from 'edge-login-ui-rn'
import * as React from 'react'
-import { Platform } from 'react-native'
+import { Appearance, InteractionManager, Platform } from 'react-native'
import { check } from 'react-native-permissions'
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome'
import IonIcon from 'react-native-vector-icons/Ionicons'
@@ -18,7 +18,7 @@ import {
getDeviceSettings,
writeDisableAnimations,
writeForceLightAccountCreate,
- writeIsLightTheme
+ writeThemeMode
} from '../../actions/DeviceSettingsActions'
import {
setDeveloperModeOn,
@@ -42,6 +42,7 @@ import { config } from '../../theme/appConfig'
import { useState } from '../../types/reactHooks'
import { useDispatch, useSelector } from '../../types/reactRedux'
import type { EdgeAppSceneProps, NavigationBase } from '../../types/routerTypes'
+import type { ThemeMode } from '../../types/types'
import { secondsToDisplay } from '../../util/displayTime'
import { getDisplayUsername, removeIsoPrefix } from '../../util/utils'
import { ButtonsView } from '../buttons/ButtonsView'
@@ -51,6 +52,7 @@ import { TextDropdown } from '../common/TextDropdown'
import { SectionView } from '../layout/SectionView'
import { AutoLogoutModal } from '../modals/AutoLogoutModal'
import { ConfirmContinueModal } from '../modals/ConfirmContinueModal'
+import { RadioListModal } from '../modals/RadioListModal'
import { TextInputModal } from '../modals/TextInputModal'
import { Airship, showDevError, showError } from '../services/AirshipInstance'
import { requestContactsPermission } from '../services/PermissionsManager'
@@ -129,8 +131,8 @@ export const SettingsScene: React.FC = props => {
const [localContactPermissionOn, setLocalContactsPermissionOn] =
React.useState(false)
- const [isLightTheme, setIsLightTheme] = useState(
- getDeviceSettings().isLightTheme
+ const [themeMode, setThemeMode] = useState(
+ getDeviceSettings().themeMode
)
const [defaultLogLevel, setDefaultLogLevel] = React.useState<
EdgeLogType | 'silent'
@@ -239,11 +241,55 @@ export const SettingsScene: React.FC = props => {
}
})
- const handleToggleLightTheme = useHandler(async () => {
- const newIsLightTheme = !isLightTheme
- setIsLightTheme(newIsLightTheme)
- changeTheme(newIsLightTheme ? config.lightTheme : config.darkTheme)
- await writeIsLightTheme(newIsLightTheme)
+ // Apply the correct theme based on mode and system preference
+ // Deferred to avoid "Cannot update a component while rendering" errors
+
+ // Note: System theme change listener is registered globally in app.ts
+ const applyTheme = React.useCallback((mode: ThemeMode) => {
+ InteractionManager.runAfterInteractions(() => {
+ if (mode === 'system') {
+ const systemIsDark = Appearance.getColorScheme() === 'dark'
+ changeTheme(systemIsDark ? config.darkTheme : config.lightTheme)
+ } else {
+ changeTheme(mode === 'light' ? config.lightTheme : config.darkTheme)
+ }
+ })
+ }, [])
+
+ const handleSelectTheme = useHandler(async () => {
+ const themeModeLabels: Record = {
+ light: lstrings.settings_theme_light,
+ dark: lstrings.settings_theme_dark,
+ system: lstrings.settings_theme_system
+ }
+
+ const items = (['light', 'dark', 'system'] as ThemeMode[]).map(mode => ({
+ icon: null,
+ name: themeModeLabels[mode],
+ value: mode
+ }))
+
+ const result = await Airship.show(bridge => (
+
+ ))
+
+ if (result != null) {
+ // Find the mode by label
+ const newMode = Object.entries(themeModeLabels).find(
+ ([, label]) => label === result
+ )?.[0] as ThemeMode | undefined
+
+ if (newMode != null && newMode !== themeMode) {
+ setThemeMode(newMode)
+ applyTheme(newMode)
+ await writeThemeMode(newMode)
+ }
+ }
})
const handleSetAutoLogoutTime = useHandler(async () => {
@@ -646,12 +692,6 @@ export const SettingsScene: React.FC = props => {
value={defaultLogLevel === 'info'}
onPress={handleToggleVerboseLogging}
/>
-
>
{ENV.ALLOW_DEVELOPER_MODE && (
@@ -671,6 +711,19 @@ export const SettingsScene: React.FC = props => {
onPress={handleToggleForceLightAccountCreate}
/>
)}
+ {developerModeOn && (
+
+ )}
)}
diff --git a/src/components/scenes/UpgradeUsernameScreen.tsx b/src/components/scenes/UpgradeUsernameScreen.tsx
index d37e321eb6d..0612d18149d 100644
--- a/src/components/scenes/UpgradeUsernameScreen.tsx
+++ b/src/components/scenes/UpgradeUsernameScreen.tsx
@@ -11,7 +11,7 @@ import { SceneWrapper } from '../common/SceneWrapper'
interface Props extends EdgeAppSceneProps<'upgradeUsername'> {}
-export const UpgradeUsernameScene = (props: Props) => {
+export const UpgradeUsernameScene: React.FC = props => {
const { navigation } = props
const account = useSelector(state => state.core.account)
const context = useSelector(state => state.core.context)
diff --git a/src/components/services/PasswordReminderService.ts b/src/components/services/PasswordReminderService.ts
index c6d501fe0de..db913df1b2e 100644
--- a/src/components/services/PasswordReminderService.ts
+++ b/src/components/services/PasswordReminderService.ts
@@ -1,51 +1,36 @@
import * as React from 'react'
import { setPasswordReminder } from '../../actions/LocalSettingsActions'
-import { connect } from '../../types/reactRedux'
-import type { PasswordReminder } from '../../types/types'
+import { useAsyncEffect } from '../../hooks/useAsyncEffect'
+import {
+ initialState,
+ type PasswordReminderState
+} from '../../reducers/PasswordReminderReducer'
+import { useDispatch, useSelector } from '../../types/reactRedux'
import { matchJson } from '../../util/matchJson'
-import { showError } from './AirshipInstance'
-interface StateProps {
- settingsLoaded: boolean | null
- passwordReminder: PasswordReminder
-}
-interface DispatchProps {
- setPasswordReminder: (passwordReminder: PasswordReminder) => Promise
-}
-type Props = StateProps & DispatchProps
+interface Props {}
-class PasswordReminderComponent extends React.PureComponent {
- componentDidUpdate(prevProps: Props) {
- if (
- this.props.settingsLoaded &&
- !matchJson(prevProps.passwordReminder, this.props.passwordReminder)
- ) {
- this.props
- .setPasswordReminder(this.props.passwordReminder)
- .catch((error: unknown) => {
- showError(error)
- })
- }
- }
+export const PasswordReminderService: React.FC = props => {
+ const settingsLoaded =
+ useSelector(state => state.ui.settings.settingsLoaded) ?? false
+ const passwordReminder = useSelector(state => state.ui.passwordReminder)
+ const lastPasswordReminder = React.useRef(initialState)
+ const dispatch = useDispatch()
- render() {
- return null
- }
-}
+ useAsyncEffect(
+ async () => {
+ if (
+ settingsLoaded &&
+ !matchJson(passwordReminder, lastPasswordReminder.current)
+ ) {
+ lastPasswordReminder.current = passwordReminder
+ await dispatch(setPasswordReminder(passwordReminder))
+ }
+ },
+ [settingsLoaded, passwordReminder],
+ 'PasswordReminderService'
+ )
-export const PasswordReminderService = connect<
- StateProps,
- DispatchProps,
- unknown
->(
- state => ({
- settingsLoaded: state.ui.settings.settingsLoaded,
- passwordReminder: state.ui.passwordReminder
- }),
- dispatch => ({
- async setPasswordReminder(passwordReminder: PasswordReminder) {
- await dispatch(setPasswordReminder(passwordReminder))
- }
- })
-)(PasswordReminderComponent)
+ return null
+}
diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts
index b207db9a902..d74d02563a8 100644
--- a/src/locales/en_US.ts
+++ b/src/locales/en_US.ts
@@ -220,6 +220,11 @@ const strings = {
fragment_request_address_uri_copied:
'Request address URI copied to clipboard',
fragment_copied: 'Successfully copied to clipboard',
+ fragment_aggregate_id: 'Aggregate ID',
+ fragment_copy_aggregate_id: 'Copy Aggregate ID',
+ fragment_copy_event_id: 'Copy Event ID',
+ fragment_event_id: 'Event ID',
+ fragment_error_report_id_copied: 'Error report ID copied',
request_minimum_notification_title: 'Minimum Balance Required',
request_xrp_minimum_notification_body_1xrp:
'Ripple (XRP) wallets require a 1 XRP minimum balance. You must deposit at least 1 XRP to this address before this wallet will show a balance or transactions. 1 XRP will be unspendable for the lifetime of this wallet address.',
@@ -507,8 +512,10 @@ const strings = {
settings_button_change_username: 'Change Username',
settings_developer_mode: 'Developer Mode',
settings_verbose_logging: 'Verbose Logging',
- settings_dark_theme: 'Dark Theme',
- settings_light_mode: 'Light Mode',
+ settings_theme: 'Theme',
+ settings_theme_light: 'Light',
+ settings_theme_dark: 'Dark',
+ settings_theme_system: 'System',
button_disable_animations: 'Disable Animations',
settings_button_contacts_access_permission: 'Contacts Access',
settings_button_lock_settings: 'Tap to Lock Account Settings',
@@ -1405,7 +1412,8 @@ const strings = {
string_max_cap: 'MAX',
string_warning: 'Warning', // Generic string. Same with wc_smartcontract_warning_title
string_report_error: 'Report Error',
- string_report_sent: 'Report sent.',
+ string_report_sent: 'The report has been sent successfully.',
+ string_show_error: 'Show Error',
string_best_rate_badge_text: 'Best\nRate',
step_prefix_s: 'Step %s:',
@@ -1918,10 +1926,12 @@ const strings = {
gift_card_network_error:
'Unable to load gift cards. Please check your network connection.',
gift_card_minimum_warning_title: 'Below Minimum',
- gift_card_minimum_warning_header:
- 'The selected amount is below the minimum for %s.',
- gift_card_minimum_warning_footer:
- 'Please select a different payment method or increase your purchase amount to at least %s.',
+ gift_card_minimum_warning_header_1s:
+ 'The selected amount is below the minimum for %1$s.',
+ gift_card_minimum_warning_footer_1s:
+ 'Please select a different payment method or increase your purchase amount to at least %1$s.',
+ gift_card_minimum_warning_generic:
+ 'The selected amount is too small for this cryptocurrency. Please select a different payment method or increase your purchase amount.',
gift_card_redeemed_cards: 'Redeemed Cards',
gift_card_unmark_as_redeemed: 'Unmark as Redeemed',
gift_card_active_cards: 'Active Cards',
@@ -2376,6 +2386,8 @@ const strings = {
buy_new_card_button: `Buy New Card`,
card_amount_max_error_message_s: `Maximum card purchase amount is $%s`,
card_amount_min_error_message_s: `Minimum card purchase amount is $%s`,
+ card_amount_max_error_message_1s: 'Maximum card purchase amount is %1$s',
+ card_amount_min_error_message_1s: 'Minimum card purchase amount is %1$s',
delete_card_confirmation_title: 'Delete Card?',
getting_payment_invoice_message: 'Getting payment invoice',
learn_more_button: `Learn More`,
diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json
index c21266ea0ce..9f1630df5b8 100644
--- a/src/locales/strings/enUS.json
+++ b/src/locales/strings/enUS.json
@@ -134,6 +134,11 @@
"fragment_request_subtitle": "Receive",
"fragment_request_address_uri_copied": "Request address URI copied to clipboard",
"fragment_copied": "Successfully copied to clipboard",
+ "fragment_aggregate_id": "Aggregate ID",
+ "fragment_copy_aggregate_id": "Copy Aggregate ID",
+ "fragment_copy_event_id": "Copy Event ID",
+ "fragment_event_id": "Event ID",
+ "fragment_error_report_id_copied": "Error report ID copied",
"request_minimum_notification_title": "Minimum Balance Required",
"request_xrp_minimum_notification_body_1xrp": "Ripple (XRP) wallets require a 1 XRP minimum balance. You must deposit at least 1 XRP to this address before this wallet will show a balance or transactions. 1 XRP will be unspendable for the lifetime of this wallet address.",
"request_xrp_minimum_notification_alert_body_1xrp": "This wallet will always require a 1 XRP minimum",
@@ -361,8 +366,10 @@
"settings_button_change_username": "Change Username",
"settings_developer_mode": "Developer Mode",
"settings_verbose_logging": "Verbose Logging",
- "settings_dark_theme": "Dark Theme",
- "settings_light_mode": "Light Mode",
+ "settings_theme": "Theme",
+ "settings_theme_light": "Light",
+ "settings_theme_dark": "Dark",
+ "settings_theme_system": "System",
"button_disable_animations": "Disable Animations",
"settings_button_contacts_access_permission": "Contacts Access",
"settings_button_lock_settings": "Tap to Lock Account Settings",
@@ -1109,7 +1116,8 @@
"string_max_cap": "MAX",
"string_warning": "Warning",
"string_report_error": "Report Error",
- "string_report_sent": "Report sent.",
+ "string_report_sent": "The report has been sent successfully.",
+ "string_show_error": "Show Error",
"string_best_rate_badge_text": "Best\nRate",
"step_prefix_s": "Step %s:",
"scan_as_in_scan_barcode": "Scan",
@@ -1491,8 +1499,9 @@
"gift_card_more_options": "Browse more gift cards",
"gift_card_network_error": "Unable to load gift cards. Please check your network connection.",
"gift_card_minimum_warning_title": "Below Minimum",
- "gift_card_minimum_warning_header": "The selected amount is below the minimum for %s.",
- "gift_card_minimum_warning_footer": "Please select a different payment method or increase your purchase amount to at least %s.",
+ "gift_card_minimum_warning_header_1s": "The selected amount is below the minimum for %1$s.",
+ "gift_card_minimum_warning_footer_1s": "Please select a different payment method or increase your purchase amount to at least %1$s.",
+ "gift_card_minimum_warning_generic": "The selected amount is too small for this cryptocurrency. Please select a different payment method or increase your purchase amount.",
"gift_card_redeemed_cards": "Redeemed Cards",
"gift_card_unmark_as_redeemed": "Unmark as Redeemed",
"gift_card_active_cards": "Active Cards",
@@ -1856,6 +1865,8 @@
"buy_new_card_button": "Buy New Card",
"card_amount_max_error_message_s": "Maximum card purchase amount is $%s",
"card_amount_min_error_message_s": "Minimum card purchase amount is $%s",
+ "card_amount_max_error_message_1s": "Maximum card purchase amount is %1$s",
+ "card_amount_min_error_message_1s": "Minimum card purchase amount is %1$s",
"delete_card_confirmation_title": "Delete Card?",
"getting_payment_invoice_message": "Getting payment invoice",
"learn_more_button": "Learn More",
diff --git a/src/plugins/gift-cards/phazeApi.ts b/src/plugins/gift-cards/phazeApi.ts
index 7871ade69e2..0ef8fa56145 100644
--- a/src/plugins/gift-cards/phazeApi.ts
+++ b/src/plugins/gift-cards/phazeApi.ts
@@ -21,7 +21,7 @@ import {
// ---------------------------------------------------------------------------
/** Minimum card value in USD. Cards below this are filtered out. */
-export const MINIMUM_CARD_VALUE_USD = 5
+export const MINIMUM_CARD_VALUE_USD = 20
// ---------------------------------------------------------------------------
// Field definitions for different use cases
diff --git a/src/plugins/ramps/rampConstraints.ts b/src/plugins/ramps/rampConstraints.ts
index 37c76da714a..b7a32e0026c 100644
--- a/src/plugins/ramps/rampConstraints.ts
+++ b/src/plugins/ramps/rampConstraints.ts
@@ -107,4 +107,13 @@ export function* constraintGenerator(
if (params.rampPluginId === 'banxa') {
yield params.paymentType !== 'ach'
}
+
+ //
+ // Infinite
+ //
+
+ if (params.rampPluginId === 'infinite') {
+ // Disable Infinite completely
+ yield false
+ }
}
diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts
index d1818009e41..5b06f9dd8c3 100644
--- a/src/theme/variables/edgeDark.ts
+++ b/src/theme/variables/edgeDark.ts
@@ -497,6 +497,7 @@ export const edgeDark: Theme = {
// UI 4.0:
badgeDot: palette.accentRed,
+ badgeText: palette.white,
// Shadows
iconShadow: {
diff --git a/src/theme/variables/edgeLight.ts b/src/theme/variables/edgeLight.ts
index b7d7750dae7..e732cd2afb5 100644
--- a/src/theme/variables/edgeLight.ts
+++ b/src/theme/variables/edgeLight.ts
@@ -140,7 +140,7 @@ export const edgeLight: Theme = {
// Icons
icon: palette.darkestNavy,
iconTappable: palette.darkMint,
- iconDeactivated: palette.whiteOp75,
+ iconDeactivated: palette.darkestNavy,
dangerIcon: palette.accentRed,
warningIcon: palette.accentOrange,
iconLoadingOverlay: palette.whiteOp75,
@@ -362,13 +362,13 @@ export const edgeLight: Theme = {
toggleButtonOff: palette.darkGray,
// Confirmation slider
- confirmationSlider: palette.darkGray,
+ confirmationSlider: palette.lightGray,
confirmationSliderCompleted: palette.darkGreen,
confirmationSliderText: palette.darkestNavy,
- confirmationSliderArrow: palette.backgroundWhite,
+ confirmationSliderArrow: palette.darkestNavy,
confirmationSliderThumb: palette.darkMint,
- confirmationSliderTextDeactivated: palette.darkGray,
- confirmationThumbDeactivated: palette.darkGray,
+ confirmationSliderTextDeactivated: palette.gray,
+ confirmationThumbDeactivated: palette.gray,
confirmationSliderWidth: deviceWidth >= 340 ? 295 : deviceWidth - 45,
confirmationSliderThumbWidth: 55,
@@ -500,6 +500,7 @@ export const edgeLight: Theme = {
// UI 4.0:
badgeDot: palette.accentRed,
+ badgeText: palette.white,
// Shadows
iconShadow: {
diff --git a/src/theme/variables/testDark.ts b/src/theme/variables/testDark.ts
index 1190819d885..f8734f1ea07 100644
--- a/src/theme/variables/testDark.ts
+++ b/src/theme/variables/testDark.ts
@@ -38,32 +38,36 @@ const palette = {
darkMint: '#089e73',
edgeMint: '#00f1a2',
- darkAqua: '#1b2f3b',
- navyAqua: '#121d25',
- navyAquaMiddle: '#11191f', // For vertical gradient
- navyAquaDarker: '#0E141A', // For vertical gradient
- blueGray: '#A4C7DF',
- gray: '#87939E',
- lightGray: '#D9E3ED',
- mutedBlue: '#2F5E89',
+
+ gray: '#888888',
+ darkGray: '#494949',
+ darkGrayOp30: 'hsla(0, 0%, 53%, 0.3)',
+ lightGray: '#e2e2e2',
+
+ blueGray: '#D9E3ED',
+ blueGrayOp75: 'rgba(217, 227, 237, .75)',
+ blueGrayOp80: 'rgba(135, 147, 158, .8)',
+
accentGreen: '#77C513',
accentRed: '#E85466',
accentBlue: '#0073D9',
accentOrange: '#F1AA19',
darkBlueLightened: '#2B333A',
- blackOp25: 'rgba(0, 0, 0, .25)',
+ blackOp10: 'rgba(0, 0, 0, .1)',
+ blackOp35: 'rgba(0, 0, 0, .25)',
blackOp50: 'rgba(0, 0, 0, .5)',
+ blackOp70: 'rgba(0, 0, 0, .7)',
blackOp80: 'rgba(0, 0, 0, .8)',
whiteOp05: 'rgba(255, 255, 255, .05)',
whiteOp10: 'rgba(255, 255, 255, .1)',
whiteOp25: 'rgba(255, 255, 255, .25)',
+ whiteOp37: 'rgba(255, 255, 255, .37)',
+ whiteOp50: 'rgba(255, 255, 255, .5)',
whiteOp75: 'rgba(255, 255, 255, .75)',
- grayOp80: 'rgba(135, 147, 158, .8)',
accentOrangeOp30: 'rgba(241, 170, 25, .3)',
- lightGrayOp75: 'rgba(217, 227, 237, .75)',
transparent: 'rgba(255, 255, 255, 0)',
// Fonts
@@ -78,7 +82,7 @@ const palette = {
skyBlue: '#3dd9f4',
blackOp65: 'rgba(0, 0, 0, .65)',
redOp60: 'rgba(232, 84, 102, .6)',
- grayOp70: 'rgba(135, 147, 158, .7)',
+ blueGrayOp70: 'rgba(135, 147, 158, .7)',
greenOp60: 'rgba(119, 197, 19, .6)',
lightGreen: '#75C649',
greenOp50: 'rgba(51, 183, 36, 0.5)',
@@ -141,12 +145,12 @@ export const testDark: Theme = {
loadingIcon: palette.edgeMint,
// Background
- backgroundGradientColors: [palette.black, palette.black],
+ backgroundGradientColors: [palette.backgroundBlack, palette.backgroundBlack],
backgroundGradientStart: { x: 0, y: 0 },
- backgroundGradientEnd: { x: 1, y: 0 },
+ backgroundGradientEnd: { x: 1, y: 1 },
backgroundDots: {
blurRadius: scale(80),
- dotOpacity: 0.25,
+ dotOpacity: 0.1,
dots: [
{
// Top-left:
@@ -172,10 +176,13 @@ export const testDark: Theme = {
],
assetOverrideDots: [undefined, { accentColor: 'iconAccentColor' }, null]
},
- assetBackgroundGradientColors: [palette.darkAqua, palette.black],
+ assetBackgroundGradientColors: [
+ palette.backgroundBlack,
+ palette.backgroundBlack
+ ],
assetBackgroundGradientStart: { x: 0, y: 0 },
assetBackgroundGradientEnd: { x: 0, y: 1 },
- assetBackgroundColorScale: 0.3,
+ assetBackgroundColorScale: 0.1,
// Camera Overlay
cameraOverlayColor: palette.black,
@@ -183,18 +190,18 @@ export const testDark: Theme = {
cameraOverlayOpEnd: 0.3,
// Modal
- modal: palette.navyAqua,
+ modal: palette.backgroundBlack,
modalCloseIcon: palette.edgeMint,
modalBorderColor: palette.transparent,
modalBorderWidth: 0,
modalBorderRadiusRem: 1,
- modalBackground: palette.whiteOp10,
+ modalBackground: palette.whiteOp37,
modalSceneOverlayColor: palette.black,
- modalDragbarColor: palette.gray,
+ modalDragbarColor: palette.darkGrayOp30,
modalLikeBackground: '#333232',
- sideMenuBorderColor: palette.navyAqua,
+ sideMenuBorderColor: palette.backgroundBlack,
sideMenuBorderWidth: 0,
sideMenuFont: palette.QuicksandMedium,
@@ -204,18 +211,17 @@ export const testDark: Theme = {
tileBackgroundMuted: palette.transparent,
// Section Lists
- // listSectionHeaderBackgroundGradientColors: [palette.navyAquaMiddle], // For vertical gradient
listSectionHeaderBackgroundGradientColors: [`#000000aa`, `#00000000`],
// Commenting out will remove background gradient:
- listSectionHeaderBackgroundGradientStart: { x: 0, y: 0 },
- listSectionHeaderBackgroundGradientEnd: { x: 1, y: 0 },
+ listSectionHeaderBackgroundGradientStart: null,
+ listSectionHeaderBackgroundGradientEnd: null,
// Text
primaryText: palette.white,
secondaryText: palette.skyBlue,
warningText: palette.accentOrange,
positiveText: palette.accentGreen,
- negativeText: palette.gray,
+ negativeText: palette.blueGray,
negativeDeltaText: palette.accentRed,
dangerText: palette.accentRed,
textLink: palette.edgeMint,
@@ -335,7 +341,7 @@ export const testDark: Theme = {
textShadowRadius: 3
},
- tabBarBackground: [palette.blackOp25, palette.blackOp50],
+ tabBarBackground: [palette.transparent, palette.transparent],
tabBarBackgroundStart: { x: 0, y: 0.5 },
tabBarBackgroundEnd: { x: 0, y: 1 },
tabBarTopOutlineColors: [`${palette.white}22`, `${palette.white}22`],
@@ -353,10 +359,10 @@ export const testDark: Theme = {
toggleButtonOff: palette.gray,
// Confirmation slider
- confirmationSlider: palette.darkBlueLightened,
+ confirmationSlider: palette.darkGray,
confirmationSliderCompleted: palette.darkGreen,
confirmationSliderText: palette.white,
- confirmationSliderArrow: palette.darkAqua,
+ confirmationSliderArrow: palette.backgroundBlack,
confirmationSliderThumb: palette.edgeMint,
confirmationSliderTextDeactivated: palette.gray,
confirmationThumbDeactivated: palette.gray,
@@ -365,7 +371,7 @@ export const testDark: Theme = {
// Lines
lineDivider: palette.whiteOp10,
- titleLineDivider: palette.blueGray,
+ titleLineDivider: palette.whiteOp10,
thinLineWidth: 1,
mediumLineWidth: 2,
thickLineWidth: 3,
@@ -384,7 +390,7 @@ export const testDark: Theme = {
dateModalTextLight: palette.accentBlue,
dateModalTextDark: palette.white,
dateModalBackgroundLight: palette.white,
- dateModalBackgroundDark: palette.darkAqua,
+ dateModalBackgroundDark: palette.backgroundBlack,
// Wallet Icon Progress
walletProgressIconFill: palette.edgeMint,
@@ -438,20 +444,20 @@ export const testDark: Theme = {
textInputTextColor: palette.white,
textInputTextColorDisabled: palette.gray,
textInputTextColorFocused: palette.white,
- textInputBackgroundColor: palette.darkAqua,
- textInputBackgroundColorDisabled: palette.darkAqua,
- textInputBackgroundColorFocused: palette.darkAqua,
+ textInputBackgroundColor: palette.graySecondary,
+ textInputBackgroundColorDisabled: palette.transparent,
+ textInputBackgroundColorFocused: palette.graySecondary,
textInputBorderColor: `${palette.edgeMint}00`,
- textInputBorderColorDisabled: palette.gray,
+ textInputBorderColorDisabled: palette.graySecondary,
textInputBorderColorFocused: palette.edgeMint,
textInputBorderRadius: 100,
textInputBorderWidth: 1,
- textInputIconColor: palette.gray,
- textInputIconColorDisabled: palette.gray,
+ textInputIconColor: palette.whiteOp50,
+ textInputIconColorDisabled: palette.whiteOp50,
textInputIconColorFocused: palette.edgeMint,
- textInputPlaceholderColor: palette.gray,
- textInputPlaceholderColorDisabled: palette.gray,
- textInputPlaceholderColorFocused: palette.edgeMint,
+ textInputPlaceholderColor: palette.whiteOp50,
+ textInputPlaceholderColorDisabled: palette.whiteOp50,
+ textInputPlaceholderColorFocused: palette.whiteOp50,
textInputSelectionColor: palette.whiteOp25,
// Animation
@@ -491,6 +497,7 @@ export const testDark: Theme = {
// UI 4.0:
badgeDot: palette.accentRed,
+ badgeText: palette.white,
// Shadows
iconShadow: {
@@ -501,6 +508,7 @@ export const testDark: Theme = {
},
shadowOpacity: 0.6,
shadowRadius: 4,
+ // Disable Android shadow
elevation: 0
},
@@ -586,10 +594,10 @@ export const testDark: Theme = {
txDirBgReceive: palette.greenOp60,
txDirBgSend: palette.redOp60,
- txDirBgSwap: palette.grayOp70,
+ txDirBgSwap: palette.blueGrayOp70,
txDirFgReceive: palette.lightGreen,
txDirFgSend: palette.lightRed,
- txDirFgSwap: palette.lightGray,
+ txDirFgSwap: palette.blueGray,
giftCardOverlayGradient: {
colors: [
diff --git a/src/theme/variables/testLight.ts b/src/theme/variables/testLight.ts
index 8f554c2de5a..1569ed6e91a 100644
--- a/src/theme/variables/testLight.ts
+++ b/src/theme/variables/testLight.ts
@@ -140,7 +140,7 @@ export const testLight: Theme = {
// Icons
icon: palette.darkestNavy,
iconTappable: palette.darkMint,
- iconDeactivated: palette.whiteOp75,
+ iconDeactivated: palette.darkestNavy,
dangerIcon: palette.accentRed,
warningIcon: palette.accentOrange,
iconLoadingOverlay: palette.whiteOp75,
@@ -362,13 +362,13 @@ export const testLight: Theme = {
toggleButtonOff: palette.darkGray,
// Confirmation slider
- confirmationSlider: palette.darkGray,
+ confirmationSlider: palette.lightGray,
confirmationSliderCompleted: palette.darkGreen,
confirmationSliderText: palette.darkestNavy,
- confirmationSliderArrow: palette.backgroundWhite,
+ confirmationSliderArrow: palette.darkestNavy,
confirmationSliderThumb: palette.darkMint,
- confirmationSliderTextDeactivated: palette.darkGray,
- confirmationThumbDeactivated: palette.darkGray,
+ confirmationSliderTextDeactivated: palette.gray,
+ confirmationThumbDeactivated: palette.gray,
confirmationSliderWidth: deviceWidth >= 340 ? 295 : deviceWidth - 45,
confirmationSliderThumbWidth: 55,
@@ -500,6 +500,7 @@ export const testLight: Theme = {
// UI 4.0:
badgeDot: palette.accentRed,
+ badgeText: palette.white,
// Shadows
iconShadow: {
diff --git a/src/types/Theme.ts b/src/types/Theme.ts
index 2d46f466638..310663e0a24 100644
--- a/src/types/Theme.ts
+++ b/src/types/Theme.ts
@@ -453,6 +453,7 @@ export interface Theme {
// UI 4.0:
badgeDot: string
+ badgeText: string
// Shadows
iconShadow: ThemeShadowParams
diff --git a/src/types/routerTypes.tsx b/src/types/routerTypes.tsx
index 463f23d5754..e3ec4b65425 100644
--- a/src/types/routerTypes.tsx
+++ b/src/types/routerTypes.tsx
@@ -270,6 +270,7 @@ export type RootParamList = {} & {
edgeApp: NavigationCore.NavigatorScreenParams | undefined
gettingStarted: GettingStartedParams
login: LoginParams
+ securityAlerts: undefined
}
// Upgraded types to comply with the navigation upgrade requirements
@@ -322,6 +323,7 @@ export type WalletsTabSceneProps =
// defined above.
// -------------------------------------------------------------------------
+/** @deprecated Use one of the XyzParamList types instead */
export type AppParamList = RootParamList &
DrawerParamList &
EdgeAppStackParamList &
@@ -330,31 +332,32 @@ export type AppParamList = RootParamList &
BuySellTabParamList &
WalletsTabParamList
-export type RouteSceneKey = keyof AppParamList
-
/**
* The of the `navigation` prop passed to each scene,
* but without any scene-specific stuff.
+ * @deprecated Use one of the `XyzSceneProps<"routeName">['navigation']` types.
*/
export type NavigationBase = NavigationCore.NavigationProp &
StackActionHelpers
/**
* The `navigation` prop passed to each scene.
+ * @deprecated Use one of the `XyzSceneProps<"routeName">['navigation']` types.
*/
-
export type NavigationProp =
NavigationCore.NavigationProp &
StackActionHelpers
/**
* The `route` prop passed to each scene.
+ * @deprecated Use one of the `XyzSceneProps<"routeName">['route']` types.
*/
export type RouteProp =
NavigationCore.RouteProp
/**
* All the props passed to each scene.
+ * @deprecated Use one of the `XyzSceneProps<"routeName">` types.
*/
export interface EdgeSceneProps {
navigation: NavigationProp
diff --git a/src/types/types.ts b/src/types/types.ts
index 90d5ee93349..2f5cf24f931 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -221,13 +221,14 @@ const asLocalAccountSettingsInner = asObject({
})
export const asDefaultScreen = asValue('home', 'assets')
+export const asThemeMode = asValue('light', 'dark', 'system')
const asDeviceSettingsInner = asObject({
defaultScreen: asMaybe(asDefaultScreen, 'home'),
developerPluginUri: asMaybe(asString),
disableAnimations: asMaybe(asBoolean, false),
forceLightAccountCreate: asMaybe(asBoolean, false),
- isLightTheme: asMaybe(asBoolean, false),
+ themeMode: asMaybe(asThemeMode, 'dark'),
isSurveyDiscoverShown: asMaybe(asBoolean, false)
})
@@ -239,6 +240,7 @@ export const asDeviceSettings = asMaybe(asDeviceSettingsInner, () =>
)
export type DefaultScreen = ReturnType
+export type ThemeMode = ReturnType
export type PasswordReminder = ReturnType
export type LocalAccountSettings = ReturnType
export type DeviceSettings = ReturnType
diff --git a/src/util/tracking.ts b/src/util/tracking.ts
index df6272d60e0..b0aa4a64453 100644
--- a/src/util/tracking.ts
+++ b/src/util/tracking.ts
@@ -190,7 +190,7 @@ export function trackError(
error: unknown,
nameTag?: string,
metadata?: Record
-): void {
+): { eventId: string } | { aggregateId: string } {
const err = normalizeError(error)
if (err instanceof AggregateErrorFix) {
@@ -202,10 +202,10 @@ export function trackError(
trackError(e, nameTag, metadata)
})
})
- return
+ return { aggregateId }
}
- captureException(err, scope => {
+ const eventId = captureException(err, scope => {
scope.setTag('event.name', nameTag)
if (metadata != null) {
const context: Record = {}
@@ -214,6 +214,7 @@ export function trackError(
}
return scope
})
+ return { eventId }
}
/**
diff --git a/yarn.lock b/yarn.lock
index bb13e3ca75b..8a68ec96ff2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1063,7 +1063,20 @@
"@babel/parser" "^7.27.2"
"@babel/types" "^7.27.1"
-"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0":
+"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3":
+ version "7.28.0"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b"
+ integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==
+ dependencies:
+ "@babel/code-frame" "^7.27.1"
+ "@babel/generator" "^7.28.0"
+ "@babel/helper-globals" "^7.28.0"
+ "@babel/parser" "^7.28.0"
+ "@babel/template" "^7.27.2"
+ "@babel/types" "^7.28.0"
+ debug "^4.3.1"
+
+"@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0":
version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b"
integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==
@@ -2128,9 +2141,9 @@
randombytes "^2.1.0"
text-encoding "0.7.0"
-"@fioprotocol/fiosdk@https://github.com/jon-edge/fiosdk_typescript.git#92a0fb895b2ce57e5955cd30cb4b7fa2bcc66bf2":
+"@fioprotocol/fiosdk@https://github.com/EdgeApp/fiosdk_typescript.git#47df5818442edec69b735d6a723747aad33b8d71":
version "1.9.0"
- resolved "https://github.com/jon-edge/fiosdk_typescript.git#92a0fb895b2ce57e5955cd30cb4b7fa2bcc66bf2"
+ resolved "https://github.com/EdgeApp/fiosdk_typescript.git#47df5818442edec69b735d6a723747aad33b8d71"
dependencies:
"@fioprotocol/fiojs" "1.0.1"
"@types/text-encoding" "0.0.35"
@@ -9526,10 +9539,10 @@ ed25519@0.0.4:
bindings "^1.2.1"
nan "^2.0.9"
-edge-core-js@^2.41.0:
- version "2.41.0"
- resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.41.0.tgz#4262701cc0719a263b4132e72e7e1299ab363817"
- integrity sha512-ntTT0yEtr6xF6+u4KbfLK7LX2kmXBvpwD8LU26B9+k3L3o1UsVZ36OjY8B6w1Wvo+jSNldxNyhTqyQYgX0ysoQ==
+edge-core-js@^2.41.3:
+ version "2.41.3"
+ resolved "https://registry.yarnpkg.com/edge-core-js/-/edge-core-js-2.41.3.tgz#c228d3c1f545af0c13f07e08308f81c98edc66fa"
+ integrity sha512-UxKFxFK+3Ypd/bY1ZM3XjFbqeQAqzkMQA3YGiYifQtdT8E1Vw3UKrhvtlaOkT44lRyamJzv64bYs5aRx8CetZQ==
dependencies:
"@nymproject/mix-fetch" "^1.4.1"
aes-js "^3.1.0"
@@ -9552,10 +9565,10 @@ edge-core-js@^2.41.0:
yaob "^0.3.12"
yavent "^0.1.5"
-edge-currency-accountbased@^4.71.1:
- version "4.71.1"
- resolved "https://registry.yarnpkg.com/edge-currency-accountbased/-/edge-currency-accountbased-4.71.1.tgz#802dd5e2410737a04c09c4bd13683f7b3b620791"
- integrity sha512-BT4LjE79f5wDFjwK5t0QoQ7E/RXqLlBVvBn7RRzdgr8Haf/ZTmylFT02+fHAqzzjyB8k11ck/bMJRIAnp4Rf3A==
+edge-currency-accountbased@^4.71.4:
+ version "4.71.4"
+ resolved "https://registry.yarnpkg.com/edge-currency-accountbased/-/edge-currency-accountbased-4.71.4.tgz#5f819c9ec6998d4a9f474732809415609f9d6ad0"
+ integrity sha512-nJCH52SsnZm9W3Pnv49/iRt61Xn3Jo6ovFu+EZezFdyl9UVsq+pbbAN/R3fONZBRbcpH448pAeHjL83EkQuYlw==
dependencies:
"@chain-registry/client" "^2.0.28"
"@chain-registry/types" "^2.0.28"
@@ -9563,7 +9576,7 @@ edge-currency-accountbased@^4.71.1:
"@emurgo/cardano-serialization-lib-nodejs" "^14.1.1"
"@ethereumjs/common" "^4.0.0"
"@ethereumjs/tx" "^5.0.0"
- "@fioprotocol/fiosdk" "https://github.com/jon-edge/fiosdk_typescript.git#92a0fb895b2ce57e5955cd30cb4b7fa2bcc66bf2"
+ "@fioprotocol/fiosdk" "https://github.com/EdgeApp/fiosdk_typescript.git#47df5818442edec69b735d6a723747aad33b8d71"
"@greymass/eosio" "^0.6.8"
"@greymass/eosio-resources" "^0.7.0"
"@hashgraph/sdk" "^2.44.0"
@@ -9624,10 +9637,10 @@ edge-currency-monero@^2.2.0:
buffer "^5.0.6"
uri-js "^3.0.2"
-edge-currency-plugins@^3.8.10:
- version "3.8.10"
- resolved "https://registry.yarnpkg.com/edge-currency-plugins/-/edge-currency-plugins-3.8.10.tgz#b154ddf945287645bbb5ca0678187443a5f4ad2e"
- integrity sha512-N7MaPL2YuIS8ADH9oKvcyvl772PePf5SPoCyu5IYmpXUDPe1204wNEbGgyGAyeAVnzzflIrjfnzuohjPaWgpaA==
+edge-currency-plugins@^3.8.11:
+ version "3.8.11"
+ resolved "https://registry.yarnpkg.com/edge-currency-plugins/-/edge-currency-plugins-3.8.11.tgz#1e2c16e1599425d3b212c225cde10d665ca5548e"
+ integrity sha512-cMzwSt4PFjkSzSmtINtxiy+xgvxoxjYs62dItP2wbsWkmT+4sqqI3Zr23w6XnDS14B18NbMfUw28C6uZyd2Oqg==
dependencies:
"@bitcoinerlab/secp256k1" "^1.2.0"
altcoin-js "^1.0.0"
@@ -9654,10 +9667,10 @@ edge-currency-plugins@^3.8.10:
wifgrs "^2.0.6"
ws "^7.4.6"
-edge-exchange-plugins@^2.40.4:
- version "2.40.4"
- resolved "https://registry.yarnpkg.com/edge-exchange-plugins/-/edge-exchange-plugins-2.40.4.tgz#eafcea40175e5413fc0a6b5927017cfbed24c649"
- integrity sha512-zuzpl46GkDeZqr1Mn59dplmtel3oce3OHaUmsI6Sem1alUb1zlgSDi+m5RVlqR3d+QikrhKG7IsBaxOSLM9fcg==
+edge-exchange-plugins@^2.40.5:
+ version "2.40.5"
+ resolved "https://registry.yarnpkg.com/edge-exchange-plugins/-/edge-exchange-plugins-2.40.5.tgz#fe92fe78ff74cf09509697e085cce0bcfcf77826"
+ integrity sha512-RaeJ1q+Y3uLfNmD/vCtCfnBGzX+hWu0HaDT36iRobw7gpKN5kjqW6KREw9ali6riVZ/J/eFfLEBZzp8MPbKmPg==
dependencies:
"@cosmjs/encoding" "^0.32.2"
"@scure/base" "^1.2.6"
@@ -16317,10 +16330,10 @@ react-native-worklets@^0.6.1:
convert-source-map "^2.0.0"
semver "7.7.2"
-react-native-zano@^0.2.5:
- version "0.2.5"
- resolved "https://registry.yarnpkg.com/react-native-zano/-/react-native-zano-0.2.5.tgz#ccf604656220436a2a2652a6556381496ab87aa0"
- integrity sha512-aI/vk9tBW3pRBTQWj8jJiqqx1RKK1P78vefi/fYi4jGCzwLn82OQ5bsm5Ne5RA3XrzceKHE13spmES8EmgDUTQ==
+react-native-zano@^0.2.6:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/react-native-zano/-/react-native-zano-0.2.6.tgz#2fc61b2124faa5438d5558c55ceb8b70d36c384e"
+ integrity sha512-uNEBVCTqFiDWk+GGikcyaf0N8HjdWOE8B6cq6DF5sfKyNXdRHFPy6g6Ha9+d7q3qZvoaQklvqs4al1J0Es5RCw==
dependencies:
cleaners "^0.3.17"
@@ -17756,7 +17769,16 @@ string-length@^4.0.2:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -17865,7 +17887,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -17879,6 +17901,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
strip-ansi@^7.0.0, strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -19592,7 +19621,7 @@ wordwrapjs@^4.0.0:
reduce-flatten "^2.0.0"
typical "^5.2.0"
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -19610,6 +19639,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"