Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ function getApi(
controller.updateSelectedGasFeeToken.bind(controller),
updateTransactionGasFees:
controller.updateTransactionGasFees.bind(controller),
updateTransactionSendFlowHistory:
controller.updateTransactionSendFlowHistory.bind(controller),
};
}

Expand Down
47 changes: 1 addition & 46 deletions app/scripts/fixtures/with-confirmed-transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,52 +22,6 @@ export const withConfirmedTransactions = (from, numEntries) => {
maxFeePerGas: '0x59682f0c',
maxPriorityFeePerGas: '0x59682f00',
},
history: [
{
chainId: network,
dappSuggestedGasFees: {
gas: '0x5208',
maxFeePerGas: '0x59682f0c',
maxPriorityFeePerGas: '0x59682f00',
},
id,
loadingDefaults: true,
origin: 'https://metamask.github.io',
status: 'confirmed',
time: Date.now(),
txParams: {
from,
gas: '0x5208',
maxFeePerGas: '0x59682f0c',
maxPriorityFeePerGas: '0x59682f00',
to: '0x2f318c334780961fb129d2a6c30d0763d9a5c970',
value: '0x29a2241af62c0000',
},
type: 'simpleSend',
},
[
{
note: 'Added new confirmed transaction.',
op: 'replace',
path: '/loadingDefaults',
timestamp: Date.now(),
value: false,
},
{
op: 'add',
path: '/simulationData',
value: {
error: {
code: 'disabled',
message: 'Simulation disabled',
},
tokenBalanceChanges: [],
},
note: 'TransactionController#updateSimulationData - Update simulation data',
timestamp: Date.now(),
},
],
],
simulationData: {
error: {
code: 'disabled',
Expand All @@ -88,6 +42,7 @@ export const withConfirmedTransactions = (from, numEntries) => {
to: '0x2f318c334780961fb129d2a6c30d0763d9a5c970',
value: '0x29a2241af62c0000',
},
history: [],
type: 'simpleSend',
};

Expand Down
2 changes: 1 addition & 1 deletion app/scripts/lib/transaction/containers/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export async function applyTransactionContainers({

log('Estimated gas', gas);

newGas = gas;
newGas = gas as Hex;
}

return {
Expand Down
130 changes: 130 additions & 0 deletions app/scripts/migrations/185.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { migrate, version } from './185';

const VERSION = version;
const oldVersion = VERSION - 1;

const sentryCaptureExceptionMock = jest.fn();
global.sentry = {
captureException: sentryCaptureExceptionMock,
};

describe(`migration #${VERSION} - reset transaction history fields`, () => {
afterEach(() => jest.resetAllMocks());

it('updates the version metadata', async () => {
const oldState = {
meta: { version: oldVersion },
data: {
TransactionController: { transactions: [] },
},
};

const newState = await migrate(oldState);
expect(newState.meta.version).toBe(VERSION);
});

it('skips migration if TransactionController.transactions is missing', async () => {
const oldState = {
meta: { version: oldVersion },
data: {
TransactionController: {},
},
};

const warn = jest.spyOn(console, 'warn').mockImplementation(() => {
// do nothing
});
const newState = await migrate(oldState);

expect(warn).toHaveBeenCalledWith(
`Migration ${VERSION}: state.TransactionController.transactions not found, skipping.`,
);
expect(newState.data).toStrictEqual(oldState.data);
});

it('sets history and sendFlowHistory to empty arrays on each transaction', async () => {
const oldState = {
meta: { version: oldVersion },
data: {
TransactionController: {
transactions: [
{
id: 1,
chainId: '0x1',
history: [{ a: 1 }],
sendFlowHistory: [{ b: 2 }],
},
{
id: 2,
chainId: '0x1', // no history fields
},
],
},
},
};

const newState = await migrate(oldState);

const expectedTransactions = {
transactions: [
{
id: 1,
chainId: '0x1',
history: [],
sendFlowHistory: [],
},
{
id: 2,
chainId: '0x1',
history: [],
sendFlowHistory: [],
},
],
};

expect(newState.data.TransactionController).toStrictEqual(
expectedTransactions,
);
});

it('leaves malformed transactions untouched', async () => {
const oldState = {
meta: { version: oldVersion },
data: {
TransactionController: {
transactions: [null, 123, 'bad'],
},
},
};

const newState = await migrate(oldState);

expect(newState.data.TransactionController).toStrictEqual(
oldState.data.TransactionController,
);
});

it('captures exception when TransactionController is not an object', async () => {
const oldState = {
meta: { version: oldVersion },
data: {
TransactionController: 99,
},
};

await migrate(oldState);
expect(sentryCaptureExceptionMock).toHaveBeenCalled();
});

it('captures exception when transactions is not an array', async () => {
const oldState = {
meta: { version: oldVersion },
data: {
TransactionController: { transactions: 'oops' },
},
};

await migrate(oldState);
expect(sentryCaptureExceptionMock).toHaveBeenCalled();
});
});
94 changes: 94 additions & 0 deletions app/scripts/migrations/185.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { cloneDeep } from 'lodash';
import { hasProperty, isObject, getErrorMessage } from '@metamask/utils';
import { captureException } from '../../../shared/lib/sentry';

type VersionedData = {
meta: { version: number };
data: Record<string, unknown>;
};

export const version = 185;

export async function migrate(
originalVersionedData: VersionedData,
): Promise<VersionedData> {
const versionedData = cloneDeep(originalVersionedData);
versionedData.meta.version = version;

try {
transformState(versionedData.data);
} catch (err) {
console.error(err);
const error = new Error(
`Migration #${version} failed: ${getErrorMessage(err)}`,
);
captureException(error);

// Keep original data to avoid corrupting state
versionedData.data = originalVersionedData.data;
}

return versionedData;
}

function transformState(state: Record<string, unknown>) {
//
// -- Step 1: Validate TransactionController exists
//
if (!hasProperty(state, 'TransactionController')) {
global.sentry?.captureException?.(
new Error(
`Migration ${version}: state.TransactionController is not defined`,
),
);
return state;
}

const txController = state.TransactionController;

if (!isObject(txController)) {
global.sentry?.captureException?.(
new Error(
`Migration ${version}: typeof state.TransactionController is ${typeof txController}`,
),
);
return state;
}

//
// -- Step 2: Validate transactions is an array
//
if (!hasProperty(txController, 'transactions')) {
console.warn(
`Migration ${version}: state.TransactionController.transactions not found, skipping.`,
);
return state;
}

if (!Array.isArray(txController.transactions)) {
global.sentry?.captureException?.(
new Error(
`Migration ${version}: state.TransactionController.transactions is not an array: ${typeof txController.transactions}`,
),
);
return state;
}

//
// -- Step 3: Clean fields from each tx
//
txController.transactions = txController.transactions.map((tx) => {
if (!isObject(tx)) {
return tx;
}

const updated = { ...tx };

updated.history = [];
updated.sendFlowHistory = [];

return updated;
});

return state;
}
1 change: 1 addition & 0 deletions app/scripts/migrations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ const migrations = [
require('./183'),
require('./183.1'),
require('./184'),
require('./185'),
];

export default migrations;
Loading
Loading