Skip to content

Commit 74f469c

Browse files
authored
Merge pull request #6044 from EdgeApp/jon/deprecate-botanix-keysonly
Deprecate Botanix to keys-only mode on July 9, 2026
2 parents e2fffa4 + 839214d commit 74f469c

4 files changed

Lines changed: 83 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- added: Reverse-resolve recipient addresses to ENS / Unstoppable Domains / ZNS names in the send flow, address modal, and transaction history.
1717
- changed: Migrate Monero to the react-native-monero implementation, replacing edge-currency-monero
1818
- changed: Migrate package manager from yarn to npm.
19+
- changed: Deprecate Botanix by switching it to keys-only mode on July 9, 2026.
1920
- fixed: Android build failure from the home screen long-press shortcuts feature, caused by an expo-quick-actions Kotlin compile error under Kotlin 2.3.
2021

2122
## 4.48.2 (2026-06-03)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { afterEach, describe, expect, jest, test } from '@jest/globals'
2+
3+
import {
4+
isKeysOnlyModeDate,
5+
SPECIAL_CURRENCY_INFO
6+
} from '../../constants/WalletAndCurrencyConstants'
7+
8+
const DEPRECATION_MS = new Date('2026-07-09T00:00:00.000Z').getTime()
9+
10+
describe('isKeysOnlyModeDate', function () {
11+
afterEach(() => {
12+
jest.restoreAllMocks()
13+
})
14+
15+
const date = new Date('2026-07-09T00:00:00.000Z')
16+
17+
test('returns false before the date', function () {
18+
jest.spyOn(Date, 'now').mockReturnValue(DEPRECATION_MS - 1)
19+
expect(isKeysOnlyModeDate(date)).toBe(false)
20+
})
21+
22+
test('returns true exactly on the date', function () {
23+
jest.spyOn(Date, 'now').mockReturnValue(DEPRECATION_MS)
24+
expect(isKeysOnlyModeDate(date)).toBe(true)
25+
})
26+
27+
test('returns true after the date', function () {
28+
jest.spyOn(Date, 'now').mockReturnValue(DEPRECATION_MS + 86400000)
29+
expect(isKeysOnlyModeDate(date)).toBe(true)
30+
})
31+
})
32+
33+
describe('botanix keysOnlyMode', function () {
34+
afterEach(() => {
35+
jest.restoreAllMocks()
36+
})
37+
38+
// The flag is a getter so it re-evaluates the date on each read, rather than
39+
// freezing the value at module load.
40+
test('re-evaluates the date on each read', function () {
41+
const nowSpy = jest.spyOn(Date, 'now')
42+
43+
nowSpy.mockReturnValue(DEPRECATION_MS - 1)
44+
expect(SPECIAL_CURRENCY_INFO.botanix.keysOnlyMode).toBe(false)
45+
46+
nowSpy.mockReturnValue(DEPRECATION_MS)
47+
expect(SPECIAL_CURRENCY_INFO.botanix.keysOnlyMode).toBe(true)
48+
})
49+
})

src/components/modals/WalletListModal.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,6 @@ interface Props {
8686
parentWalletId?: string
8787
}
8888

89-
const keysOnlyModeAssets: EdgeAsset[] = Object.keys(SPECIAL_CURRENCY_INFO)
90-
.filter(pluginId => isKeysOnlyPlugin(pluginId))
91-
.map(pluginId => ({
92-
pluginId,
93-
tokenId: null
94-
}))
95-
9689
export const WalletListModal: React.FC<Props> = props => {
9790
const {
9891
bridge,
@@ -140,10 +133,16 @@ export const WalletListModal: React.FC<Props> = props => {
140133

141134
// #region Init
142135

143-
// Prevent plugins that are "watch only" from being used unless it's explicitly allowed
136+
// Prevent plugins that are "watch only" from being used unless it's explicitly allowed.
137+
// Computed per render (not once at import) so date-gated keysOnlyMode plugins are
138+
// honored mid-session without an app restart.
144139
const walletListExcludeAssets = React.useMemo(() => {
145140
const result = excludeAssets
146-
return allowKeysOnlyMode ? result : keysOnlyModeAssets.concat(result ?? [])
141+
if (allowKeysOnlyMode) return result
142+
const keysOnlyModeAssets: EdgeAsset[] = Object.keys(SPECIAL_CURRENCY_INFO)
143+
.filter(pluginId => isKeysOnlyPlugin(pluginId))
144+
.map(pluginId => ({ pluginId, tokenId: null }))
145+
return keysOnlyModeAssets.concat(result ?? [])
147146
}, [allowKeysOnlyMode, excludeAssets])
148147

149148
// #endregion Init

src/constants/WalletAndCurrencyConstants.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,12 @@ export const SPECIAL_CURRENCY_INFO: Record<string, SpecialCurrencyInfo> = {
609609
showChainIcon: true,
610610
dummyPublicAddress: '0x0d73358506663d484945ba85d0cd435ad610b0a0',
611611
isImportKeySupported: true,
612+
// Getter, not a fixed boolean: this is read on each access (e.g. via
613+
// isKeysOnlyPlugin) so the date gate re-evaluates during a running session
614+
// and takes effect at the cutover without requiring an app restart.
615+
get keysOnlyMode(): boolean {
616+
return isKeysOnlyModeDate(new Date('2026-07-09T00:00:00.000Z'))
617+
},
612618
walletConnectV2ChainId: {
613619
namespace: 'eip155',
614620
reference: '3637'
@@ -1094,6 +1100,25 @@ function isZecBroken(): boolean {
10941100
return false
10951101
}
10961102

1103+
/**
1104+
* Generic time-gate for deprecating an asset into keysOnlyMode (watch-only) on
1105+
* a specific date. Returns true once the current time is on or after `date`. On
1106+
* and after the date the asset becomes keys-only: existing wallets remain
1107+
* accessible (keys-only) but no new wallets can be created.
1108+
*
1109+
* Not specific to any single plugin. Call it from a `keysOnlyMode` getter so
1110+
* the gate re-evaluates on each read and takes effect at the cutover without an
1111+
* app restart:
1112+
* get keysOnlyMode(): boolean { return isKeysOnlyModeDate(new Date('YYYY-MM-DD')) }
1113+
*
1114+
* Declared as a hoisted function (not a `const`) because `SPECIAL_CURRENCY_INFO`
1115+
* references it during module initialization, before a later `const` would be
1116+
* assigned (temporal dead zone).
1117+
*/
1118+
export function isKeysOnlyModeDate(date: Date): boolean {
1119+
return Date.now() >= date.getTime()
1120+
}
1121+
10971122
export const USD_FIAT = 'iso:USD'
10981123
/**
10991124
* Get the fiat symbol from an iso:[fiat] OR fiat currency code

0 commit comments

Comments
 (0)