Skip to content

Commit d9d0d29

Browse files
committed
refactor(predict): read deposit-wallet state via usePredictAccountState
1 parent a6411d6 commit d9d0d29

3 files changed

Lines changed: 69 additions & 140 deletions

File tree

app/components/Views/confirmations/hooks/pay/useTransactionPayPostQuote.test.ts

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useTransactionMetadataRequest } from '../transactions/useTransactionMet
66
import { useTransactionPayWithdraw } from './useTransactionPayWithdraw';
77
import Engine from '../../../../../core/Engine';
88
import { computeProxyAddress } from '../../../../UI/Predict/providers/polymarket/safe/utils';
9+
import { usePredictAccountState } from '../../../../UI/Predict/hooks/usePredictAccountState';
910

1011
jest.mock('../transactions/useTransactionMetadataRequest');
1112
jest.mock('./useTransactionPayWithdraw');
@@ -14,14 +15,12 @@ jest.mock('../../../../../core/Engine', () => ({
1415
TransactionPayController: {
1516
setTransactionConfig: jest.fn(),
1617
},
17-
PredictController: {
18-
getAccountState: jest.fn(),
19-
},
2018
},
2119
}));
2220
jest.mock('../../../../UI/Predict/providers/polymarket/safe/utils', () => ({
2321
computeProxyAddress: jest.fn(),
2422
}));
23+
jest.mock('../../../../UI/Predict/hooks/usePredictAccountState');
2524

2625
const TRANSACTION_ID_MOCK = 'transaction-123';
2726
const FROM_MOCK = '0x1234567890123456789012345678901234567890' as Hex;
@@ -35,22 +34,24 @@ describe('useTransactionPayPostQuote', () => {
3534
const setTransactionConfigMock = jest.mocked(
3635
Engine.context.TransactionPayController.setTransactionConfig,
3736
);
38-
const getAccountStateMock = jest.mocked(
39-
Engine.context.PredictController.getAccountState,
40-
);
37+
const usePredictAccountStateMock = jest.mocked(usePredictAccountState);
4138
const computeProxyAddressMock = jest.mocked(computeProxyAddress);
4239

43-
const flushPromises = () => new Promise(setImmediate);
40+
function mockAccountState(walletType: 'safe' | 'deposit-wallet'): void {
41+
usePredictAccountStateMock.mockReturnValue({
42+
data: {
43+
address: '0xProxyAddress',
44+
isDeployed: true,
45+
walletType,
46+
},
47+
isLoading: false,
48+
} as never);
49+
}
4450

4551
beforeEach(() => {
4652
jest.clearAllMocks();
4753
computeProxyAddressMock.mockReturnValue(PROXY_ADDRESS_MOCK);
48-
getAccountStateMock.mockResolvedValue({
49-
address: '0xProxyAddress',
50-
isDeployed: true,
51-
walletType: 'safe',
52-
balance: 100,
53-
} as never);
54+
mockAccountState('safe');
5455
useTransactionPayWithdrawMock.mockReturnValue({
5556
isWithdraw: false,
5657
canSelectWithdrawToken: false,
@@ -292,66 +293,68 @@ describe('useTransactionPayPostQuote', () => {
292293
});
293294
});
294295

295-
it('flags transaction and clears refundTo when walletType is deposit-wallet', async () => {
296-
getAccountStateMock.mockResolvedValue({
297-
address: '0xDepositWallet',
298-
isDeployed: true,
299-
walletType: 'deposit-wallet',
300-
balance: 50,
301-
} as never);
296+
it('flags transaction and skips refundTo when walletType is deposit-wallet', () => {
297+
mockAccountState('deposit-wallet');
302298

303299
renderHook(() => useTransactionPayPostQuote());
304-
await flushPromises();
305300

306-
expect(setTransactionConfigMock).toHaveBeenCalledTimes(2);
301+
expect(setTransactionConfigMock).toHaveBeenCalledTimes(1);
307302

308-
const followUpCallback = setTransactionConfigMock.mock.calls[1][1];
309-
const config = {
310-
refundTo: PROXY_ADDRESS_MOCK,
311-
} as {
303+
const callback = setTransactionConfigMock.mock.calls[0][1];
304+
const config = {} as {
305+
isPostQuote?: boolean;
312306
isPolymarketDepositWallet?: boolean;
313307
refundTo?: Hex;
314308
};
315-
followUpCallback(config);
309+
callback(config);
316310

311+
expect(config.isPostQuote).toBe(true);
317312
expect(config.isPolymarketDepositWallet).toBe(true);
318313
expect(config.refundTo).toBeUndefined();
314+
expect(computeProxyAddressMock).not.toHaveBeenCalled();
319315
});
320316

321-
it('does not set deposit-wallet flag when walletType is safe', async () => {
322-
getAccountStateMock.mockResolvedValue({
323-
address: '0xSafeProxy',
324-
isDeployed: true,
325-
walletType: 'safe',
326-
balance: 100,
327-
} as never);
317+
it('does not set deposit-wallet flag when walletType is safe', () => {
318+
mockAccountState('safe');
328319

329320
renderHook(() => useTransactionPayPostQuote());
330-
await flushPromises();
331321

332322
expect(setTransactionConfigMock).toHaveBeenCalledTimes(1);
323+
324+
const callback = setTransactionConfigMock.mock.calls[0][1];
325+
const config = {} as {
326+
isPostQuote?: boolean;
327+
isPolymarketDepositWallet?: boolean;
328+
refundTo?: Hex;
329+
};
330+
callback(config);
331+
332+
expect(config.isPolymarketDepositWallet).toBeUndefined();
333333
});
334334

335-
it('does not resolve account state for non-predictWithdraw flows', async () => {
336-
useTransactionMetadataRequestMock.mockReturnValue({
337-
id: TRANSACTION_ID_MOCK,
338-
txParams: { from: FROM_MOCK },
339-
type: TransactionType.perpsWithdraw,
335+
it('defers setTransactionConfig until predict account state resolves', () => {
336+
usePredictAccountStateMock.mockReturnValue({
337+
data: undefined,
338+
isLoading: true,
340339
} as never);
341340

342341
renderHook(() => useTransactionPayPostQuote());
343-
await flushPromises();
344342

345-
expect(getAccountStateMock).not.toHaveBeenCalled();
346-
expect(setTransactionConfigMock).toHaveBeenCalledTimes(1);
343+
expect(setTransactionConfigMock).not.toHaveBeenCalled();
347344
});
348345

349-
it('swallows getAccountState errors without setting the deposit-wallet flag', async () => {
350-
getAccountStateMock.mockRejectedValue(new Error('boom'));
346+
it('does not resolve account state for non-predictWithdraw flows', () => {
347+
useTransactionMetadataRequestMock.mockReturnValue({
348+
id: TRANSACTION_ID_MOCK,
349+
txParams: { from: FROM_MOCK },
350+
type: TransactionType.perpsWithdraw,
351+
} as never);
351352

352353
renderHook(() => useTransactionPayPostQuote());
353-
await flushPromises();
354354

355+
expect(usePredictAccountStateMock).toHaveBeenCalledWith({
356+
enabled: false,
357+
});
355358
expect(setTransactionConfigMock).toHaveBeenCalledTimes(1);
356359
});
357360
});

app/components/Views/confirmations/hooks/pay/useTransactionPayPostQuote.ts

Lines changed: 20 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useTransactionPayWithdraw } from './useTransactionPayWithdraw';
66
import { useTransactionMetadataRequest } from '../transactions/useTransactionMetadataRequest';
77
import { computeProxyAddress } from '../../../../UI/Predict/providers/polymarket/safe/utils';
88
import { hasTransactionType } from '../../utils/transaction';
9+
import { usePredictAccountState } from '../../../../UI/Predict/hooks/usePredictAccountState';
910

1011
const log = createProjectLogger('transaction-pay-post-quote');
1112

@@ -37,6 +38,13 @@ export function useTransactionPayPostQuote(): void {
3738
TransactionType.predictWithdraw,
3839
]);
3940

41+
const { data: accountState } = usePredictAccountState({
42+
enabled: isPredictWithdraw,
43+
});
44+
45+
const isDepositWalletWithdraw =
46+
isPredictWithdraw && accountState?.walletType === 'deposit-wallet';
47+
4048
useEffect(() => {
4149
if (
4250
!canSelectWithdrawToken ||
@@ -46,6 +54,10 @@ export function useTransactionPayPostQuote(): void {
4654
return;
4755
}
4856

57+
if (isPredictWithdraw && !accountState) {
58+
return;
59+
}
60+
4961
try {
5062
const { TransactionPayController } = Engine.context;
5163
const from = transactionMeta?.txParams?.from as Hex | undefined;
@@ -55,7 +67,7 @@ export function useTransactionPayPostQuote(): void {
5567
// on the user's address directly (HyperCore -> Relay for perps; vault
5668
// teller -> user for money account).
5769
const refundTo =
58-
isPerpsWithdraw || isMoneyAccountWithdraw
70+
isPerpsWithdraw || isMoneyAccountWithdraw || isDepositWalletWithdraw
5971
? undefined
6072
: from
6173
? computeProxyAddress(from)
@@ -71,6 +83,10 @@ export function useTransactionPayPostQuote(): void {
7183
if (isPerpsWithdraw) {
7284
config.isHyperliquidSource = true;
7385
}
86+
87+
if (isDepositWalletWithdraw) {
88+
config.isPolymarketDepositWallet = true;
89+
}
7490
});
7591

7692
isSet.current = transactionId;
@@ -80,61 +96,22 @@ export function useTransactionPayPostQuote(): void {
8096
refundTo,
8197
isPerpsWithdraw,
8298
isMoneyAccountWithdraw,
99+
isDepositWalletWithdraw,
83100
});
84-
85-
if (isPredictWithdraw) {
86-
applyDepositWalletFlag(transactionId);
87-
}
88101
} catch (error) {
89102
log('Error initializing post-quote transaction', {
90103
error,
91104
transactionId,
92105
});
93106
}
94107
}, [
108+
accountState,
95109
canSelectWithdrawToken,
110+
isDepositWalletWithdraw,
96111
isMoneyAccountWithdraw,
97112
isPerpsWithdraw,
98113
isPredictWithdraw,
99114
transactionId,
100115
transactionMeta?.txParams?.from,
101116
]);
102117
}
103-
104-
// Deposit-wallet Predict withdrawals need a follow-up flag set after
105-
// resolving the user's Polymarket account state. The strategy switch
106-
// also drops refundTo because the bridge mints its own deposit address.
107-
async function applyDepositWalletFlag(transactionId: string): Promise<void> {
108-
try {
109-
const isDepositWallet = await isPolymarketDepositWalletWithdraw();
110-
if (!isDepositWallet) {
111-
return;
112-
}
113-
Engine.context.TransactionPayController.setTransactionConfig(
114-
transactionId,
115-
(config) => {
116-
config.isPolymarketDepositWallet = true;
117-
config.refundTo = undefined;
118-
},
119-
);
120-
log('Marked transaction as Polymarket deposit-wallet withdraw', {
121-
transactionId,
122-
});
123-
} catch (error) {
124-
log('Failed to apply Polymarket deposit-wallet flag', {
125-
error,
126-
transactionId,
127-
});
128-
}
129-
}
130-
131-
async function isPolymarketDepositWalletWithdraw(): Promise<boolean> {
132-
try {
133-
const accountState =
134-
await Engine.context.PredictController.getAccountState();
135-
return accountState.walletType === 'deposit-wallet';
136-
} catch (error) {
137-
log('Failed to resolve Polymarket account state', { error });
138-
return false;
139-
}
140-
}

app/selectors/transactionController.test.ts

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -344,57 +344,6 @@ describe('TransactionController Selectors', () => {
344344
]);
345345
});
346346

347-
it('keeps nonce-less transactions that share an actionId', () => {
348-
const activeEvmAddress = '0x0000000000000000000000000000000000000001';
349-
const state = {
350-
engine: {
351-
backgroundState: {
352-
AccountsController: {
353-
internalAccounts: {
354-
selectedAccount: 'account-1',
355-
accounts: {
356-
'account-1': {
357-
id: 'account-1',
358-
address: activeEvmAddress,
359-
type: 'eip155:eoa',
360-
},
361-
},
362-
},
363-
},
364-
TransactionController: {
365-
transactions: [
366-
{
367-
id: 'tx-a',
368-
chainId: '0x1',
369-
time: 100,
370-
txParams: {
371-
from: activeEvmAddress,
372-
actionId: 'shared-action',
373-
},
374-
},
375-
{
376-
id: 'tx-b',
377-
chainId: '0x1',
378-
time: 200,
379-
txParams: {
380-
from: activeEvmAddress,
381-
actionId: 'shared-action',
382-
},
383-
},
384-
],
385-
},
386-
},
387-
},
388-
pendingSmartTransactionsForGroup: [],
389-
} as unknown as RootState;
390-
391-
const ids = selectLocalTransactions(state).map(
392-
(t) => (t as { id: string }).id,
393-
);
394-
395-
expect(ids).toContain('tx-a');
396-
expect(ids).toContain('tx-b');
397-
});
398347
});
399348

400349
describe('selectTransactionMetadataById', () => {

0 commit comments

Comments
 (0)