Skip to content

Commit 6351e4b

Browse files
authored
fix: move ephemeral state out of persisted wallet store (#1899)
1 parent 414e8c3 commit 6351e4b

File tree

7 files changed

+73
-24
lines changed

7 files changed

+73
-24
lines changed

src/bonsai/rest/compliance.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
setLocalAddressScreenV2Raw,
1414
setSourceAddressScreenV2Raw,
1515
} from '@/state/raw';
16+
import { getHdKeyNonce } from '@/state/walletSelectors';
1617

1718
import { signCompliancePayload } from '@/lib/compliance';
1819
import { mapIfPresent } from '@/lib/do';
@@ -62,9 +63,10 @@ export function setUpIndexerSourceAddressScreenV2Query(store: RootStore) {
6263
}
6364

6465
const selectChainIdAndLocalAddress = createAppSelector(
65-
[getSelectedDydxChainId, getUserWalletAddress, getSelectedNetwork],
66-
(chainId, address, network) => ({
66+
[getSelectedDydxChainId, getUserWalletAddress, getSelectedNetwork, getHdKeyNonce],
67+
(chainId, address, network, hdKeyNonce) => ({
6768
chainId,
69+
hdKeyNonce,
6870
address,
6971
network,
7072
})
@@ -81,12 +83,14 @@ const COMPLIANCE_PAYLOAD_MESSAGE = 'Verify account ownership';
8183
async function updateCompliance({
8284
chainId,
8385
address,
86+
hdKeyNonce,
8487
network,
8588
status,
8689
action,
8790
}: {
8891
chainId: DydxChainId;
8992
address: string;
93+
hdKeyNonce: number;
9094
network: DydxNetwork;
9195
status: ComplianceStatus;
9296
action: ComplianceAction;
@@ -104,7 +108,7 @@ async function updateCompliance({
104108
chainId,
105109
};
106110

107-
const signingResponse = await signCompliancePayload(address, payload);
111+
const signingResponse = await signCompliancePayload(address, hdKeyNonce, payload);
108112
if (!signingResponse) {
109113
return { status: ComplianceStatus.UNKNOWN };
110114
}
@@ -163,8 +167,8 @@ export function setUpIndexerLocalAddressScreenV2Query(store: RootStore) {
163167
address,
164168
network,
165169
],
166-
getQueryFn: (indexerClient, { chainId, address, network }) => {
167-
if (address == null) {
170+
getQueryFn: (indexerClient, { chainId, address, network, hdKeyNonce }) => {
171+
if (address == null || hdKeyNonce == null) {
168172
return null;
169173
}
170174
return async (): Promise<ComplianceResponse> => {
@@ -179,6 +183,7 @@ export function setUpIndexerLocalAddressScreenV2Query(store: RootStore) {
179183

180184
const updateResult = updateCompliance({
181185
address,
186+
hdKeyNonce,
182187
chainId,
183188
network,
184189
status: firstScreenResult.status,
@@ -211,15 +216,17 @@ export const triggerCompliance = (action: ComplianceAction) => {
211216
const currentLocalScreenStatus = selectRawLocalAddressScreenV2(state).data?.status;
212217
const chainId = getSelectedDydxChainId(state);
213218
const address = getUserWalletAddress(state);
219+
const hdKeyNonce = getHdKeyNonce(state);
214220
const network = getSelectedNetwork(state);
215221

216-
if (!address || !currentLocalScreenStatus) {
222+
if (!address || !currentLocalScreenStatus || !hdKeyNonce) {
217223
throw new Error('TriggerCompliance: No account connected or screen status not loaded');
218224
}
219225

220226
const result = await updateCompliance({
221227
chainId,
222228
address,
229+
hdKeyNonce,
223230
network,
224231
status: currentLocalScreenStatus,
225232
action,

src/lib/affiliates.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { store } from '@/state/_store';
88
import { getUserWalletAddress } from '@/state/accountInfoSelectors';
99
import { appQueryClient } from '@/state/appQueryClient';
1010
import { getSelectedDydxChainId, getSelectedNetwork } from '@/state/appSelectors';
11+
import { getHdKeyNonce } from '@/state/walletSelectors';
1112

1213
import { track } from './analytics/analytics';
1314
import { signCompliancePayload } from './compliance';
@@ -34,9 +35,10 @@ export const updateReferralCode = async (newCode: string) => {
3435
const chainId = getSelectedDydxChainId(state);
3536
const address = getUserWalletAddress(state);
3637
const network = getSelectedNetwork(state);
38+
const hdKeyNonce = getHdKeyNonce(state);
3739

38-
if (!address) {
39-
throw new Error('No account connected');
40+
if (!address || !hdKeyNonce) {
41+
throw new Error('No account connected or hdKey not set');
4042
}
4143

4244
const networkConfig = ENVIRONMENT_CONFIG_MAP[network];
@@ -46,7 +48,7 @@ export const updateReferralCode = async (newCode: string) => {
4648
throw new Error('No indexer URL available');
4749
}
4850

49-
const signingResponse = await signCompliancePayload(address, {
51+
const signingResponse = await signCompliancePayload(address, hdKeyNonce, {
5052
message: newCode,
5153
action: ReferralAction.UPDATE_CODE,
5254
status: '',

src/lib/compliance.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const signComplianceSignatureKeplr = async (
8787

8888
export const signCompliancePayload = async (
8989
address: string,
90+
nonce: number,
9091
params: {
9192
message: string;
9293
action: string;
@@ -95,7 +96,7 @@ export const signCompliancePayload = async (
9596
}
9697
): Promise<string> => {
9798
try {
98-
const hdkey = hdKeyManager.getHdkey(address);
99+
const hdkey = hdKeyManager.getHdkey(address, nonce);
99100
if (hdkey?.privateKey && hdkey.publicKey) {
100101
const { signedMessage, timestamp } = await signComplianceSignature(
101102
params.message,

src/lib/hdKeyManager.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { LocalWallet } from '@dydxprotocol/v4-client-js';
44
import { Hdkey } from '@/constants/account';
55

66
import type { RootStore } from '@/state/_store';
7-
import { setLocalWalletNonce } from '@/state/wallet';
7+
import { setHdKeyNonce, setLocalWalletNonce } from '@/state/wallet';
88

99
import { log } from './telemetry';
1010

@@ -13,22 +13,38 @@ class HDKeyManager {
1313

1414
private hdkey: Hdkey | undefined;
1515

16+
private hdKeyNonce: number | undefined;
17+
18+
private store: RootStore | undefined;
19+
20+
setStore(store: RootStore) {
21+
this.store = store;
22+
}
23+
1624
setHdkey(address: string | undefined, hdkey: Hdkey) {
25+
this.hdKeyNonce = this.hdKeyNonce != null ? this.hdKeyNonce + 1 : 0;
1726
this.address = address;
1827
this.hdkey = hdkey;
28+
29+
if (!this.store) {
30+
log('HDKeyManager: store has not been set');
31+
return;
32+
}
33+
this.store.dispatch(setHdKeyNonce(this.hdKeyNonce));
1934
}
2035

21-
getHdkey(localWalletAddress: string): Hdkey | undefined {
22-
if (localWalletAddress !== this.address) {
36+
getHdkey(localWalletAddress: string, hdKeyNonce: number): Hdkey | undefined {
37+
if (localWalletAddress !== this.address || hdKeyNonce !== this.hdKeyNonce) {
2338
return undefined;
2439
}
25-
2640
return this.hdkey;
2741
}
2842

2943
clearHdkey() {
3044
this.hdkey = undefined;
3145
this.address = undefined;
46+
this.hdKeyNonce = undefined;
47+
this.store?.dispatch(setHdKeyNonce(undefined));
3248
}
3349
}
3450

@@ -54,9 +70,10 @@ class LocalWalletManager {
5470

5571
if (!this.store) {
5672
log('LocalWalletManager: store has not been set');
73+
return;
5774
}
5875

59-
this.store?.dispatch(setLocalWalletNonce(this.localWalletNonce));
76+
this.store.dispatch(setLocalWalletNonce(this.localWalletNonce));
6077
}
6178

6279
getLocalWallet(localWalletNonce: number): LocalWallet | undefined {

src/state/_store.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { persistReducer, persistStore } from 'redux-persist';
66
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
77
import storage from 'redux-persist/lib/storage';
88

9-
import { localWalletManager } from '@/lib/hdKeyManager';
9+
import { hdKeyManager, localWalletManager } from '@/lib/hdKeyManager';
1010
import { transformOntologyObject } from '@/lib/transformOntology';
1111

1212
import { accountSlice } from './account';
@@ -33,7 +33,7 @@ import { tradingViewSlice } from './tradingView';
3333
import { transfersSlice } from './transfers';
3434
import { triggersFormSlice } from './triggersForm';
3535
import { vaultsSlice } from './vaults';
36-
import { walletSlice } from './wallet';
36+
import { walletEphemeralSlice, walletSlice } from './wallet';
3737

3838
const reducers = {
3939
account: accountSlice.reducer,
@@ -55,6 +55,7 @@ const reducers = {
5555
transfers: transfersSlice.reducer,
5656
vaults: vaultsSlice.reducer,
5757
wallet: walletSlice.reducer,
58+
walletEphemeral: walletEphemeralSlice.reducer,
5859
raw: rawSlice.reducer,
5960
} as const;
6061

@@ -124,6 +125,7 @@ export const persistor = persistStore(store);
124125

125126
// Set store so localWalletManager classes can getState and dispatch
126127
localWalletManager.setStore(store);
128+
hdKeyManager.setStore(store);
127129

128130
export type RootStore = typeof store;
129131
export type RootState = ReturnType<typeof store.getState>;

src/state/wallet.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export interface WalletState {
1919
address?: string;
2020
subaccountNumber?: number;
2121
};
22-
localWalletNonce?: number;
2322
turnkeyEmailOnboardingData?: TurnkeyEmailOnboardingData;
2423
turnkeyPrimaryWallet?: TurnkeyWallet;
2524
}
@@ -35,7 +34,6 @@ const initialState: WalletState = {
3534
address: undefined,
3635
subaccountNumber: 0,
3736
},
38-
localWalletNonce: undefined,
3937
turnkeyEmailOnboardingData: undefined,
4038
turnkeyPrimaryWallet: undefined,
4139
};
@@ -44,9 +42,6 @@ export const walletSlice = createSlice({
4442
name: 'wallet',
4543
initialState,
4644
reducers: {
47-
setLocalWalletNonce: (state, action: PayloadAction<number | undefined>) => {
48-
state.localWalletNonce = action.payload;
49-
},
5045
setSourceAddress: (
5146
state,
5247
action: PayloadAction<{ address: string; chain: WalletNetworkType }>
@@ -120,8 +115,30 @@ export const walletSlice = createSlice({
120115
},
121116
});
122117

118+
export interface WalletEphemeralState {
119+
localWalletNonce?: number;
120+
hdKeyNonce?: number;
121+
}
122+
123+
const initialEphemeralState: WalletEphemeralState = {
124+
localWalletNonce: undefined,
125+
hdKeyNonce: undefined,
126+
};
127+
128+
export const walletEphemeralSlice = createSlice({
129+
name: 'walletEphemeral',
130+
initialState: initialEphemeralState,
131+
reducers: {
132+
setLocalWalletNonce: (state, action: PayloadAction<number | undefined>) => {
133+
state.localWalletNonce = action.payload;
134+
},
135+
setHdKeyNonce: (state, action: PayloadAction<number | undefined>) => {
136+
state.hdKeyNonce = action.payload;
137+
},
138+
},
139+
});
140+
123141
export const {
124-
setLocalWalletNonce,
125142
setSourceAddress,
126143
setRequiresAddressUpload,
127144
setWalletInfo,
@@ -134,3 +151,5 @@ export const {
134151
setTurnkeyPrimaryWallet,
135152
clearTurnkeyPrimaryWallet,
136153
} = walletSlice.actions;
154+
155+
export const { setLocalWalletNonce, setHdKeyNonce } = walletEphemeralSlice.actions;

src/state/walletSelectors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export const selectTurnkeyWalletInfo = createAppSelector([selectWalletInfo], (wa
3131
return undefined;
3232
});
3333

34-
export const getLocalWalletNonce = (state: RootState) => state.wallet.localWalletNonce;
34+
export const getLocalWalletNonce = (state: RootState) => state.walletEphemeral.localWalletNonce;
35+
export const getHdKeyNonce = (state: RootState) => state.walletEphemeral.hdKeyNonce;
3536

3637
export const getTurnkeyEmailOnboardingData = (state: RootState) =>
3738
state.wallet.turnkeyEmailOnboardingData;

0 commit comments

Comments
 (0)