Skip to content

Commit 9d722e3

Browse files
committed
fix(perps): prefer selected evm account
1 parent 8fbb9a4 commit 9d722e3

4 files changed

Lines changed: 132 additions & 21 deletions

File tree

app/controllers/perps/PerpsController.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ import {
120120
LastTransactionResult,
121121
TransactionStatus,
122122
} from './types/transactionTypes';
123-
import { getSelectedEvmAccount } from './utils/accountUtils';
123+
import { getSelectedEvmAccountFromMessenger } from './utils/accountUtils';
124124
import { ensureError } from './utils/errorUtils';
125125
import {
126126
hydrateFromDiskSync,
@@ -1155,11 +1155,7 @@ export class PerpsController extends BaseController<
11551155
// Get current user address for validation
11561156
let currentAddress: string | null = null;
11571157
try {
1158-
const evmAccount = getSelectedEvmAccount(
1159-
this.messenger.call(
1160-
'AccountTreeController:getAccountsFromSelectedAccountGroup',
1161-
),
1162-
);
1158+
const evmAccount = getSelectedEvmAccountFromMessenger(this.messenger);
11631159
currentAddress = evmAccount?.address ?? null;
11641160
} catch {
11651161
// Can't determine current account — trust the cache
@@ -2215,11 +2211,7 @@ export class PerpsController extends BaseController<
22152211
currentDepositId = depositId;
22162212

22172213
// Get current account address via messenger (outside of update() for proper typing)
2218-
const evmAccount = getSelectedEvmAccount(
2219-
this.messenger.call(
2220-
'AccountTreeController:getAccountsFromSelectedAccountGroup',
2221-
),
2222-
);
2214+
const evmAccount = getSelectedEvmAccountFromMessenger(this.messenger);
22232215
const accountAddress = evmAccount?.address ?? 'unknown';
22242216

22252217
this.update((state) => {
@@ -3090,11 +3082,7 @@ export class PerpsController extends BaseController<
30903082

30913083
// Watch for account changes via AccountTreeController
30923084
const accountChangeHandler = (): void => {
3093-
const evmAccount = getSelectedEvmAccount(
3094-
this.messenger.call(
3095-
'AccountTreeController:getAccountsFromSelectedAccountGroup',
3096-
),
3097-
);
3085+
const evmAccount = getSelectedEvmAccountFromMessenger(this.messenger);
30983086
const currentAddress = evmAccount?.address ?? null;
30993087

31003088
// If any cached entry belongs to a different account, clear all entries.
@@ -3332,11 +3320,7 @@ export class PerpsController extends BaseController<
33323320
}
33333321

33343322
// Get current user address
3335-
const evmAccount = getSelectedEvmAccount(
3336-
this.messenger.call(
3337-
'AccountTreeController:getAccountsFromSelectedAccountGroup',
3338-
),
3339-
);
3323+
const evmAccount = getSelectedEvmAccountFromMessenger(this.messenger);
33403324
if (!evmAccount?.address) {
33413325
return;
33423326
}

app/controllers/perps/types/messenger.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
AccountTreeControllerGetAccountsFromSelectedAccountGroupAction,
33
AccountTreeControllerSelectedAccountGroupChangeEvent,
44
} from '@metamask/account-tree-controller';
5+
import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';
56
import type { GeolocationControllerGetGeolocationAction } from '@metamask/geolocation-controller';
67
import type {
78
KeyringControllerGetStateAction,
@@ -32,6 +33,7 @@ export type PerpsControllerAllowedActions =
3233
| KeyringControllerSignTypedMessageAction
3334
| TransactionControllerAddTransactionAction
3435
| RemoteFeatureFlagControllerGetStateAction
36+
| AccountsControllerGetSelectedAccountAction
3537
| AccountTreeControllerGetAccountsFromSelectedAccountGroupAction
3638
| AuthenticationController.AuthenticationControllerGetBearerTokenAction;
3739

app/controllers/perps/utils/accountUtils.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,111 @@
1+
import type { InternalAccount } from '@metamask/keyring-internal-api';
2+
13
import { PERPS_CONSTANTS } from '../constants/perpsConfig';
4+
import type { PerpsControllerMessenger } from '../PerpsController';
25
import type { AccountState } from '../types';
36

47
import {
58
addSpotBalanceToAccountState,
69
aggregateAccountStates,
710
calculateWeightedReturnOnEquity,
11+
getSelectedEvmAccountFromMessenger,
812
getSpotBalance,
913
} from './accountUtils';
1014

15+
const SELECTED_ADDRESS = '0x1111111111111111111111111111111111111111';
16+
const GROUP_ADDRESS = '0x2222222222222222222222222222222222222222';
17+
const NON_EVM_ADDRESS = 'bc1qselectedaccount';
18+
19+
function buildAccount(
20+
address: string,
21+
id: string,
22+
type: InternalAccount['type'] = 'eip155:eoa',
23+
): InternalAccount {
24+
return {
25+
address,
26+
id,
27+
type,
28+
options: {},
29+
methods: [],
30+
metadata: {
31+
name: id,
32+
importTime: Date.now(),
33+
keyring: {
34+
type: 'HD Key Tree',
35+
},
36+
},
37+
scopes: ['eip155:0'],
38+
} as InternalAccount;
39+
}
40+
41+
function buildMessenger(
42+
call: (actionType: string) => InternalAccount | InternalAccount[],
43+
): Pick<PerpsControllerMessenger, 'call'> {
44+
return { call } as unknown as Pick<PerpsControllerMessenger, 'call'>;
45+
}
46+
47+
describe('getSelectedEvmAccountFromMessenger', () => {
48+
it('prefers the selected account over the first evm account in the selected group', () => {
49+
const selectedAccount = buildAccount(SELECTED_ADDRESS, 'selected');
50+
const groupedAccount = buildAccount(GROUP_ADDRESS, 'grouped');
51+
const messenger = buildMessenger((actionType: string) => {
52+
switch (actionType) {
53+
case 'AccountsController:getSelectedAccount':
54+
return selectedAccount;
55+
case 'AccountTreeController:getAccountsFromSelectedAccountGroup':
56+
return [groupedAccount];
57+
default:
58+
throw new Error(`Unexpected action: ${actionType}`);
59+
}
60+
});
61+
62+
expect(getSelectedEvmAccountFromMessenger(messenger)).toStrictEqual({
63+
address: SELECTED_ADDRESS,
64+
});
65+
});
66+
67+
it('falls back to the selected account group when selected account lookup is unavailable', () => {
68+
const groupedAccount = buildAccount(GROUP_ADDRESS, 'grouped');
69+
const messenger = buildMessenger((actionType: string) => {
70+
switch (actionType) {
71+
case 'AccountsController:getSelectedAccount':
72+
throw new Error('Selected account unavailable');
73+
case 'AccountTreeController:getAccountsFromSelectedAccountGroup':
74+
return [groupedAccount];
75+
default:
76+
throw new Error(`Unexpected action: ${actionType}`);
77+
}
78+
});
79+
80+
expect(getSelectedEvmAccountFromMessenger(messenger)).toStrictEqual({
81+
address: GROUP_ADDRESS,
82+
});
83+
});
84+
85+
it('falls back to the selected account group when the selected account is not evm', () => {
86+
const selectedAccount = buildAccount(
87+
NON_EVM_ADDRESS,
88+
'selected',
89+
'bip122:p2wpkh',
90+
);
91+
const groupedAccount = buildAccount(GROUP_ADDRESS, 'grouped');
92+
const messenger = buildMessenger((actionType: string) => {
93+
switch (actionType) {
94+
case 'AccountsController:getSelectedAccount':
95+
return selectedAccount;
96+
case 'AccountTreeController:getAccountsFromSelectedAccountGroup':
97+
return [groupedAccount];
98+
default:
99+
throw new Error(`Unexpected action: ${actionType}`);
100+
}
101+
});
102+
103+
expect(getSelectedEvmAccountFromMessenger(messenger)).toStrictEqual({
104+
address: GROUP_ADDRESS,
105+
});
106+
});
107+
});
108+
11109
describe('aggregateAccountStates', () => {
12110
const fallback: AccountState = {
13111
spendableBalance: PERPS_CONSTANTS.FallbackDataDisplay,

app/controllers/perps/utils/accountUtils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import type { InternalAccount } from '@metamask/keyring-internal-api';
66

77
import { PERPS_CONSTANTS } from '../constants/perpsConfig';
8+
import type { PerpsControllerMessenger } from '../PerpsController';
89
import type { AccountState, PerpsInternalAccount } from '../types';
910
import type { SpotClearinghouseStateResponse } from '../types/hyperliquid-types';
1011

@@ -37,6 +38,32 @@ export function getSelectedEvmAccount(
3738
return getEvmAccountFromAccountGroup(accounts);
3839
}
3940

41+
export function getSelectedEvmAccountFromMessenger(
42+
messenger: Pick<PerpsControllerMessenger, 'call'>,
43+
): { address: string } | undefined {
44+
try {
45+
const selectedAccount = messenger.call(
46+
'AccountsController:getSelectedAccount',
47+
);
48+
const evmAccount = findEvmAccount([selectedAccount]);
49+
if (evmAccount) {
50+
return { address: evmAccount.address };
51+
}
52+
} catch {
53+
// Fall back to the selected account group if the direct lookup is unavailable.
54+
}
55+
56+
try {
57+
return getSelectedEvmAccount(
58+
messenger.call(
59+
'AccountTreeController:getAccountsFromSelectedAccountGroup',
60+
),
61+
);
62+
} catch {
63+
return undefined;
64+
}
65+
}
66+
4067
export type ReturnOnEquityInput = {
4168
unrealizedPnl: string | number;
4269
returnOnEquity: string | number;

0 commit comments

Comments
 (0)