Skip to content

Commit 3dee20e

Browse files
fix(receive): address review comments
1 parent 4760fe1 commit 3dee20e

7 files changed

Lines changed: 155 additions & 84 deletions

File tree

packages/suite/src/storage/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Storage changelog
22

3+
## 26.8.0
4+
5+
- rename receive `revealedAddresses` to `touchedAddresses`
6+
- remove `isVerified` flag from receive address entries
7+
38
## 26.6.0
49

510
- purge desktop trading form draft keys (`trading-buy/*`, `trading-sell/`, `trading-exchange/`) from `formDrafts`
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { createMigration } from '@suite/idb-migration-utils';
2+
3+
import { type SuiteDBSchema } from 'src/storage/definitions';
4+
5+
import { updateAll } from '../utils';
6+
7+
type LegacyReceiveInfo = {
8+
path: string;
9+
address: string;
10+
};
11+
12+
type LegacyReceiveAccountState = {
13+
touchedAddresses?: LegacyReceiveInfo[];
14+
revealedAddresses?: (LegacyReceiveInfo & { isVerified?: boolean })[];
15+
currentFreshAddress?: LegacyReceiveInfo;
16+
};
17+
18+
export default createMigration<SuiteDBSchema>('26.8.0', async (_db, tx) => {
19+
await updateAll<'receive', LegacyReceiveAccountState>(tx, 'receive', oldReceiveState => {
20+
const sourceAddresses =
21+
oldReceiveState.touchedAddresses ?? oldReceiveState.revealedAddresses;
22+
23+
if (!sourceAddresses) {
24+
return undefined;
25+
}
26+
27+
return {
28+
touchedAddresses: sourceAddresses.map(({ path, address }) => ({
29+
path,
30+
address,
31+
})),
32+
currentFreshAddress: oldReceiveState.currentFreshAddress,
33+
};
34+
});
35+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import '@suite-common/test-utils/src/globalOverrides';
2+
import { type IDBPDatabase, deleteDB, openDB } from 'idb';
3+
4+
import { type SuiteDBSchema } from 'src/storage/definitions';
5+
6+
import migration from '../26.8.0';
7+
8+
const DB_NAME = 'suite-idb-test-26.8.0';
9+
const INITIAL_VERSION = 1;
10+
11+
const runMigration = () =>
12+
openDB(DB_NAME, INITIAL_VERSION + 1, {
13+
upgrade(db: IDBPDatabase<SuiteDBSchema>, _oldVersion, _newVersion, tx) {
14+
migration.migrate(db, tx);
15+
},
16+
});
17+
18+
describe('migration 26.8.0', () => {
19+
beforeEach(async () => {
20+
await deleteDB(DB_NAME);
21+
});
22+
23+
test('renames receive revealed addresses to touched addresses', async () => {
24+
const db = await openDB(DB_NAME, INITIAL_VERSION, {
25+
upgrade(db) {
26+
db.createObjectStore('receive');
27+
},
28+
});
29+
await db.put(
30+
'receive',
31+
{
32+
revealedAddresses: [
33+
{
34+
path: 'btc-path',
35+
address: 'btc-address',
36+
isVerified: true,
37+
},
38+
],
39+
currentFreshAddress: {
40+
path: 'fresh-btc-path',
41+
address: 'fresh-btc-address',
42+
},
43+
},
44+
'btc-account-key',
45+
);
46+
db.close();
47+
48+
const migratedDb = await runMigration();
49+
50+
expect(await migratedDb.get('receive', 'btc-account-key')).toEqual({
51+
touchedAddresses: [
52+
{
53+
path: 'btc-path',
54+
address: 'btc-address',
55+
},
56+
],
57+
currentFreshAddress: {
58+
path: 'fresh-btc-path',
59+
address: 'fresh-btc-address',
60+
},
61+
});
62+
63+
migratedDb.close();
64+
});
65+
});

packages/suite/src/storage/migrations/versions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ export { default as m26_6_0_2 } from './26.6.0.2';
2727
export { default as m26_7_0 } from './26.7.0';
2828
export { default as m26_7_0_1 } from './26.7.0.1';
2929
export { default as m26_7_0_2 } from './26.7.0.2';
30+
export { default as m26_8_0 } from './26.8.0';

packages/suite/src/views/wallet/sign-verify/index.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useState } from 'react';
22
import { type FieldError } from 'react-hook-form';
33

4-
import { selectFullSelectedAccount } from '@suite/account';
4+
import { selectSelectedAccount, selectSelectedAccountKey } from '@suite/account';
55
import { useDevice } from '@suite/device';
66
import { Translation, type TranslationKey, useTranslation } from '@suite/intl';
77
import { type ReceiveRootState, selectTouchedAddresses } from '@suite-common/receive';
@@ -41,9 +41,11 @@ const SignVerify = () => {
4141
const [page, setPage] = useState<'sign' | 'verify'>('sign');
4242
const [isCompleted, setIsCompleted] = useState(false);
4343

44-
const selectedAccount = useSelector(selectFullSelectedAccount);
44+
const selectedAccount = useSelector(selectSelectedAccount);
45+
const selectedAccountKey = useSelector(selectSelectedAccountKey);
46+
const selectedNetwork = useSelector(state => state.wallet.selectedAccount.network);
4547
const touchedAddresses = useSelector((state: ReceiveRootState) =>
46-
selectTouchedAddresses(state, selectedAccount.account?.key),
48+
selectTouchedAddresses(state, selectedAccountKey),
4749
);
4850
const dispatch = useDispatch();
4951

@@ -63,11 +65,11 @@ const SignVerify = () => {
6365
pathField,
6466
isElectrumField,
6567
cardanoPubKeyCoseField,
66-
} = useSignVerifyForm(isSignPage, selectedAccount.account!);
68+
} = useSignVerifyForm(isSignPage, selectedAccount!);
6769

6870
const { isLocked, device } = useDevice();
6971
const { translationString } = useTranslation();
70-
const { canCopy, copy } = useCopySignedMessage(formValues, selectedAccount.network);
72+
const { canCopy, copy } = useCopySignedMessage(formValues, selectedNetwork);
7173

7274
const getErrorMessage = (error?: FieldError) =>
7375
error ? translationString(error.message as TranslationKey) : undefined;

suite-common/receive/src/__tests__/receiveSlice.test.ts

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,6 @@ const ethereumAccount = mockWalletAccount({ symbol: 'eth' }, networkSpecificDefa
1717
const receiveReducer = prepareReceiveReducer(extraDependenciesCommonMock);
1818

1919
describe('receiveSlice', () => {
20-
it('loads persisted accounts and strips legacy verification flag on @storage/load', () => {
21-
const state = receiveReducer(undefined, {
22-
type: extraDependenciesCommonMock.actionTypes.storageLoad,
23-
payload: {
24-
receive: [
25-
{
26-
key: bitcoinAccount.key,
27-
value: {
28-
revealedAddresses: [
29-
{
30-
path: 'btc-path',
31-
address: 'btc-address',
32-
isVerified: true,
33-
},
34-
],
35-
currentFreshAddress: {
36-
path: 'fresh-btc',
37-
address: 'btc-fresh-address',
38-
},
39-
},
40-
},
41-
],
42-
},
43-
});
44-
45-
expect(state.accounts).toEqual({
46-
[bitcoinAccount.key]: {
47-
touchedAddresses: [
48-
{
49-
path: 'btc-path',
50-
address: 'btc-address',
51-
},
52-
],
53-
currentFreshAddress: {
54-
path: 'fresh-btc',
55-
address: 'btc-fresh-address',
56-
},
57-
},
58-
});
59-
});
60-
6120
it('loads persisted accounts with touched addresses on @storage/load', () => {
6221
const state = receiveReducer(undefined, {
6322
type: extraDependenciesCommonMock.actionTypes.storageLoad,

suite-common/receive/src/receiveSlice.ts

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type PayloadAction } from '@reduxjs/toolkit';
22

3-
import { type AnyAction, createSliceWithExtraDeps } from '@suite-common/redux-utils';
3+
import { createSliceWithExtraDeps, createWeakMapSelector } from '@suite-common/redux-utils';
44
import { accountsActions } from '@suite-common/wallet-core';
55
import { type AccountKey, type ReceiveInfo } from '@suite-common/wallet-types';
66

@@ -33,12 +33,6 @@ type SetCurrentFreshAddressPayload = {
3333
currentFreshAddress?: CurrentFreshAddress;
3434
};
3535

36-
type PersistedReceiveAccountState = {
37-
touchedAddresses?: ReceiveInfo[];
38-
revealedAddresses?: ReceiveInfo[];
39-
currentFreshAddress?: CurrentFreshAddress;
40-
};
41-
4236
export const receiveInitialState: ReceiveState = {
4337
accounts: {},
4438
};
@@ -103,44 +97,54 @@ export const receiveSlice = createSliceWithExtraDeps({
10397
});
10498
})
10599
.addCase(extra.actionTypes.storageLoad, (state, action) => {
106-
const actionWithPayload = action as AnyAction;
107-
108-
state.accounts = (
109-
(actionWithPayload.payload?.receive ?? []) as {
110-
key: string;
111-
value: PersistedReceiveAccountState;
112-
}[]
113-
).reduce<ReceiveState['accounts']>((accounts, { key, value }) => {
114-
const touchedAddresses =
115-
value.touchedAddresses ?? value.revealedAddresses ?? [];
116-
117-
accounts[key as AccountKey] = {
118-
touchedAddresses: touchedAddresses.map(({ path, address }) => ({
119-
path,
120-
address,
121-
})),
122-
currentFreshAddress: value.currentFreshAddress,
123-
};
124-
125-
return accounts;
126-
}, {});
100+
const entries = action.payload?.receive as
101+
| {
102+
key: string;
103+
value: ReceiveAccountState;
104+
}[]
105+
| undefined;
106+
107+
state.accounts =
108+
entries?.reduce<ReceiveState['accounts']>((accounts, { key, value }) => {
109+
accounts[key as AccountKey] = {
110+
touchedAddresses: value.touchedAddresses.map(({ path, address }) => ({
111+
path,
112+
address,
113+
})),
114+
currentFreshAddress: value.currentFreshAddress,
115+
};
116+
117+
return accounts;
118+
}, {}) ?? {};
127119
});
128120
},
129121
});
130122

131-
const selectReceiveAccountState = (state: ReceiveRootState, accountKey?: AccountKey) => {
132-
if (!accountKey) {
133-
return emptyReceiveAccountState;
134-
}
123+
const createMemoizedSelector = createWeakMapSelector.withTypes<ReceiveRootState>();
135124

136-
return state.receive.accounts[accountKey] ?? emptyReceiveAccountState;
137-
};
125+
const selectReceiveAccountState = createMemoizedSelector(
126+
[
127+
(state: ReceiveRootState) => state.receive.accounts,
128+
(_state: ReceiveRootState, accountKey?: AccountKey) => accountKey,
129+
],
130+
(accounts, accountKey) => {
131+
if (!accountKey) {
132+
return emptyReceiveAccountState;
133+
}
134+
135+
return accounts[accountKey] ?? emptyReceiveAccountState;
136+
},
137+
);
138138

139-
export const selectTouchedAddresses = (state: ReceiveRootState, accountKey?: AccountKey) =>
140-
selectReceiveAccountState(state, accountKey).touchedAddresses;
139+
export const selectTouchedAddresses = createMemoizedSelector(
140+
[selectReceiveAccountState],
141+
accountState => accountState.touchedAddresses,
142+
);
141143

142-
export const selectCurrentFreshAddress = (state: ReceiveRootState, accountKey?: AccountKey) =>
143-
selectReceiveAccountState(state, accountKey).currentFreshAddress;
144+
export const selectCurrentFreshAddress = createMemoizedSelector(
145+
[selectReceiveAccountState],
146+
accountState => accountState.currentFreshAddress,
147+
);
144148

145149
export const receiveActions = receiveSlice.actions;
146150
export const prepareReceiveReducer = receiveSlice.prepareReducer;

0 commit comments

Comments
 (0)