Skip to content

Commit a97ee7b

Browse files
fix: address comments
1 parent ccbcf69 commit a97ee7b

3 files changed

Lines changed: 55 additions & 5 deletions

File tree

packages/snap/src/handlers/accountResolver.test.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
RESOLVE_ACCOUNT_KEYRING_AND_WALLET,
77
ResolveAccountSource,
88
} from './accountResolver';
9-
import { AccountService } from '../services/account';
9+
import {
10+
AccountService,
11+
DerivedAccountAddressMismatchException,
12+
} from '../services/account';
1013
import { generateStellarKeyringAccount } from '../services/account/__mocks__/account.fixtures';
1114
import { AccountNotActivatedException } from '../services/network';
1215
import {
@@ -20,7 +23,10 @@ import {
2023
mockOnChainAccountService,
2124
} from '../services/on-chain-account/__mocks__/onChainAccount.fixtures';
2225
import { WalletService } from '../services/wallet';
23-
import { getTestWallet } from '../services/wallet/__mocks__/wallet.fixtures';
26+
import {
27+
generateStellarAddress,
28+
getTestWallet,
29+
} from '../services/wallet/__mocks__/wallet.fixtures';
2430

2531
jest.mock('../utils/logger');
2632

@@ -189,6 +195,34 @@ describe('AccountResolver', () => {
189195
).rejects.toThrow(AccountNotActivatedException);
190196
});
191197

198+
it('throws DerivedAccountAddressMismatchException when state snapshot address differs from keyring', async () => {
199+
const { accountResolver, resolveOnChainAccountByKeyringAccountIdSpy } =
200+
setup();
201+
202+
const otherAddress = generateStellarAddress();
203+
const mockRawAccount = createMockAccountWithBalances(
204+
otherAddress,
205+
'1',
206+
DEFAULT_MOCK_ACCOUNT_WITH_BALANCES,
207+
);
208+
const mismatchedOnChainAccount = new OnChainAccount(
209+
mockRawAccount,
210+
scope,
211+
horizonSource(mockRawAccount, scope),
212+
);
213+
resolveOnChainAccountByKeyringAccountIdSpy.mockResolvedValue(
214+
mismatchedOnChainAccount,
215+
);
216+
217+
await expect(
218+
accountResolver.resolveAccount({
219+
accountId: keyringAccountId,
220+
scope,
221+
options: RESOLVE_ACCOUNT_FULL_FROM_KEYRING_STATE,
222+
}),
223+
).rejects.toThrow(DerivedAccountAddressMismatchException);
224+
});
225+
192226
it('omits wallet when wallet load is disabled', async () => {
193227
const { accountResolver, account, onChainAccount, resolveWalletSpy } =
194228
setup();

packages/snap/src/handlers/accountResolver.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { KnownCaip2ChainId } from '../api';
22
import { AppConfig } from '../config';
3-
import type {
4-
AccountService,
5-
StellarKeyringAccount,
3+
import {
4+
assertSameAddress,
5+
type AccountService,
6+
type StellarKeyringAccount,
67
} from '../services/account';
78
import { AccountNotActivatedException } from '../services/network/exceptions';
89
import type { OnChainAccountService } from '../services/on-chain-account';
@@ -167,6 +168,9 @@ export class AccountResolver {
167168
if (onChainAccount === null) {
168169
throw new AccountNotActivatedException(account.address, scope);
169170
}
171+
172+
assertSameAddress(account.address, onChainAccount?.accountId);
173+
170174
return onChainAccount;
171175
}
172176
}

packages/snap/src/handlers/clientRequest/base.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Struct } from '@metamask/superstruct';
22
import type { Json, JsonRpcRequest } from '@metamask/utils';
33

44
import type { JsonRpcRequestWithAccount } from './api';
5+
import type { KnownCaip2ChainId } from '../../api';
56
import { AccountNotActivatedException } from '../../services/network';
67
import { render as renderAccountActivationPrompt } from '../../ui/confirmation/views/AccountActivationPrompt/render';
78
import type { ILogger } from '../../utils/logger';
@@ -41,6 +42,16 @@ export abstract class BaseClientRequestHandler<
4142
return request.params.accountId;
4243
}
4344

45+
/**
46+
* Get the scope from the JSON-RPC request.
47+
*
48+
* @param request - The JSON-RPC request to get the scope from.
49+
* @returns The scope or undefined if not provided.
50+
*/
51+
protected getScope(request: RequestType): KnownCaip2ChainId | undefined {
52+
return (request.params as { scope?: KnownCaip2ChainId }).scope;
53+
}
54+
4455
readonly #accountResolver: AccountResolver;
4556

4657
readonly #resolveAccountOptions: FullActivatedAccountResolveOptions;
@@ -87,6 +98,7 @@ export abstract class BaseClientRequestHandler<
8798
try {
8899
return await this.#accountResolver.resolveAccount({
89100
accountId: this.getAccountId(request),
101+
scope: this.getScope(request),
90102
options: this.#resolveAccountOptions,
91103
});
92104
} catch (error: unknown) {

0 commit comments

Comments
 (0)