Skip to content

Commit

Permalink
improve migration
Browse files Browse the repository at this point in the history
  • Loading branch information
owencraston committed Jan 15, 2025
1 parent d4e2d35 commit bd54717
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 31 deletions.
60 changes: 54 additions & 6 deletions app/store/migrations/066.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {
EthMethod,
} from '@metamask/keyring-api';
import { AccountsControllerState } from '@metamask/accounts-controller';
import { captureException } from '@sentry/react-native';
import migration from './066';

jest.mock('../../util/Logger');
jest.mock('@sentry/react-native', () => ({
captureException: jest.fn(),
}));

const mockedCaptureException = jest.mocked(captureException);

interface StateType {
engine: {
backgroundState: {
Expand Down Expand Up @@ -143,25 +146,25 @@ describe('migration #66', () => {
},
};

it('should return state if not valid', () => {
it('returns state if not valid', () => {
const result = migration(MOCK_INVALID_STATE);
expect(result).toEqual(MOCK_INVALID_STATE);
});

it('should return state if empty accounts', () => {
it('returns state if empty accounts', () => {
const result = migration(MOCK_EMPTY_STATE);
expect(result).toEqual(MOCK_EMPTY_STATE);
});

it('should not modify accounts that already have valid scopes', () => {
it('preserves accounts that have valid scopes', () => {
const stateCopy = JSON.parse(
JSON.stringify(MOCK_STATE_WITH_EXISTING_SCOPES),
);
const result = migration(stateCopy) as StateType;
expect(result).toEqual(MOCK_STATE_WITH_EXISTING_SCOPES);
});

it('should add correct scopes for all account types', () => {
it('adds correct scopes for all account types', () => {
const stateCopy = JSON.parse(JSON.stringify(MOCK_STATE_WITH_ACCOUNTS));
const result = migration(stateCopy) as StateType;
const accounts =
Expand All @@ -185,7 +188,7 @@ describe('migration #66', () => {
]);
});

it('should handle malformed account objects gracefully', () => {
it('handles malformed account objects gracefully', () => {
const malformedState: StateType = {
engine: {
backgroundState: {
Expand Down Expand Up @@ -226,7 +229,7 @@ describe('migration #66', () => {
expect(accounts['valid-1']?.scopes).toEqual([EthScopes.Namespace]);
});

it('should handle invalid scopes property gracefully', () => {
it('handles invalid scopes property gracefully', () => {
const stateWithInvalidScopes: StateType = {
engine: {
backgroundState: {
Expand Down Expand Up @@ -304,4 +307,49 @@ describe('migration #66', () => {
expect(accounts['invalid-2']?.scopes).toEqual([EthScopes.Namespace]);
expect(accounts['invalid-3']?.scopes).toEqual([EthScopes.Namespace]);
});

it('logs unknown account types to Sentry', () => {
const stateWithUnknownType: StateType = {
engine: {
backgroundState: {
AccountsController: {
internalAccounts: {
selectedAccount: 'unknown-1',
accounts: {
'unknown-1': {
id: 'unknown-1',
// @ts-expect-error Testing unknown account type
type: 'unknown-type',
address: '0x123',
options: {},
metadata: {
name: 'Unknown Account',
keyring: { type: 'HD Key Tree' },
importTime: Date.now(),
},
methods: [],
scopes: [],
},
},
},
},
},
},
};

const result = migration(stateWithUnknownType) as StateType;
const accounts =
result.engine.backgroundState.AccountsController.internalAccounts
.accounts;

// Verify scopes are set to default EVM namespace
expect(accounts['unknown-1']?.scopes).toEqual([EthScopes.Namespace]);

// Verify Sentry exception was captured
expect(mockedCaptureException).toHaveBeenCalledWith(
new Error(
'Migration 66: Unknown account type unknown-type, defaulting to EVM namespace',
),
);
});
});
47 changes: 22 additions & 25 deletions app/store/migrations/066.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ import { captureException } from '@sentry/react-native';

const migrationVersion = 66;

function getScopesForAccountType(accountType: string): string[] {
switch (accountType) {
case EthAccountType.Eoa:
case EthAccountType.Erc4337:
return [EthScopes.Namespace];
case BtcAccountType.P2wpkh:
// Default to mainnet scope if address is missing or invalid
return [BtcScopes.Mainnet];
case SolAccountType.DataAccount:
return [SolScopes.Mainnet, SolScopes.Testnet, SolScopes.Devnet];
default:
// Default to EVM namespace for unknown account types
captureException(
new Error(
`Migration ${migrationVersion}: Unknown account type ${accountType}, defaulting to EVM namespace`,
),
);
return [EthScopes.Namespace];
}
}

/**
* Migration for adding scopes to accounts in the AccountsController.
* Each account type gets its appropriate scopes:
Expand Down Expand Up @@ -72,31 +93,7 @@ export default function migrate(state: unknown) {
`Migration ${migrationVersion}: Adding scopes for account type ${account.type}`,
);

switch (account.type) {
case EthAccountType.Eoa:
case EthAccountType.Erc4337:
account.scopes = [EthScopes.Namespace];
break;
case BtcAccountType.P2wpkh:
// Default to mainnet scope if address is missing or invalid
account.scopes = [BtcScopes.Mainnet];
break;
case SolAccountType.DataAccount:
account.scopes = [
SolScopes.Mainnet,
SolScopes.Testnet,
SolScopes.Devnet,
];
break;
default:
// Default to EVM namespace for unknown account types
account.scopes = [EthScopes.Namespace];
captureException(
new Error(
`Migration ${migrationVersion}: : Unknown account type ${account.type}, defaulting to EVM namespace`,
),
);
}
account.scopes = getScopesForAccountType(account.type as string);
}

return state;
Expand Down

0 comments on commit bd54717

Please sign in to comment.