Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"@metamask/eslint-config-jest": "^12.1.0",
"@metamask/eslint-config-nodejs": "^12.1.0",
"@metamask/eslint-config-typescript": "^12.1.0",
"@metamask/eth-block-tracker": "^9.0.2",
"@metamask/eth-block-tracker": "^9.0.3",
"@metamask/eth-json-rpc-provider": "^4.0.0",
"@metamask/json-rpc-engine": "^9.0.0",
"@metamask/utils": "^8.3.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/network-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"dependencies": {
"@metamask/base-controller": "^6.0.0",
"@metamask/controller-utils": "^11.0.0",
"@metamask/eth-block-tracker": "^9.0.2",
"@metamask/eth-block-tracker": "^9.0.3",
"@metamask/eth-json-rpc-infura": "^9.1.0",
"@metamask/eth-json-rpc-middleware": "^12.1.1",
"@metamask/eth-json-rpc-provider": "^4.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ export class SubjectMetadataController extends BaseController<
this.subjectsWithoutPermissionsEncounteredSinceStartup.add(origin);

this.update((draftState) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore something about string-indexing
draftState.subjectMetadata[origin] = newMetadata;
if (typeof originToForget === 'string') {
delete draftState.subjectMetadata[originToForget];
Expand All @@ -231,7 +233,8 @@ export class SubjectMetadataController extends BaseController<
*/
trimMetadataState(): void {
this.update((draftState) => {
// @ts-expect-error ts(2589)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore infinite type-resursion srsly
return SubjectMetadataController.getTrimmedState(
draftState,
this.subjectHasPermissions,
Expand Down
2 changes: 1 addition & 1 deletion packages/transaction-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@metamask/gas-fee-controller": "^17.0.0",
"@metamask/metamask-eth-abis": "^3.1.1",
"@metamask/network-controller": "^19.0.0",
"@metamask/nonce-tracker": "^5.0.0",
"@metamask/nonce-tracker": "^6.0.0",
"@metamask/rpc-errors": "^6.2.1",
"@metamask/utils": "^8.3.0",
"async-mutex": "^0.5.0",
Expand Down
221 changes: 42 additions & 179 deletions packages/transaction-controller/src/TransactionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type {
Provider,
} from '@metamask/network-controller';
import { NetworkClientType, NetworkStatus } from '@metamask/network-controller';
import * as NonceTrackerPackage from '@metamask/nonce-tracker';
import { errorCodes, providerErrors, rpcErrors } from '@metamask/rpc-errors';
import { createDeferredPromise } from '@metamask/utils';
import assert from 'assert';
Expand Down Expand Up @@ -299,15 +298,13 @@ function waitForTransactionFinished(

const MOCK_PREFERENCES = { state: { selectedAddress: 'foo' } };
const INFURA_PROJECT_ID = '341eacb578dd44a1a049cbc5f6fd4035';
const GOERLI_PROVIDER = new HttpProvider(
`https://goerli.infura.io/v3/${INFURA_PROJECT_ID}`,
);
const MAINNET_PROVIDER = new HttpProvider(
`https://mainnet.infura.io/v3/${INFURA_PROJECT_ID}`,
);
const PALM_PROVIDER = new HttpProvider(
`https://palm-mainnet.infura.io/v3/${INFURA_PROJECT_ID}`,
);
const HTTP_PROVIDERS = {
mainnet: new HttpProvider(`https://mainnet.infura.io/v3/${INFURA_PROJECT_ID}`),
goerli: new HttpProvider(`https://palm-mainnet.infura.io/v3/${INFURA_PROJECT_ID}`),
linea: new HttpProvider(`https://palm-mainnet.infura.io/v3/${INFURA_PROJECT_ID}`),
linea_goerli: new HttpProvider(`https://palm-mainnet.infura.io/v3/${INFURA_PROJECT_ID}`),
custom: new HttpProvider(`http://127.0.0.123:456/ethrpc?apiKey=foobar`),
}

type MockNetwork = {
provider: Provider;
Expand All @@ -317,8 +314,8 @@ type MockNetwork = {
};

const MOCK_NETWORK: MockNetwork = {
provider: MAINNET_PROVIDER,
blockTracker: buildMockBlockTracker('0x102833C', MAINNET_PROVIDER),
provider: HTTP_PROVIDERS.mainnet,
blockTracker: buildMockBlockTracker('0x102833C', HTTP_PROVIDERS.mainnet),
state: {
selectedNetworkClientId: NetworkType.goerli,
networksMetadata: {
Expand All @@ -336,27 +333,9 @@ const MOCK_NETWORK: MockNetwork = {
},
subscribe: () => undefined,
};
const MOCK_NETWORK_WITHOUT_CHAIN_ID: MockNetwork = {
provider: GOERLI_PROVIDER,
blockTracker: buildMockBlockTracker('0x102833C', GOERLI_PROVIDER),
state: {
selectedNetworkClientId: NetworkType.goerli,
networksMetadata: {
[NetworkType.goerli]: {
EIPS: { 1559: false },
status: NetworkStatus.Available,
},
},
providerConfig: {
type: NetworkType.goerli,
} as NetworkState['providerConfig'],
networkConfigurations: {},
},
subscribe: () => undefined,
};
const MOCK_MAINNET_NETWORK: MockNetwork = {
provider: MAINNET_PROVIDER,
blockTracker: buildMockBlockTracker('0x102833C', MAINNET_PROVIDER),
provider: HTTP_PROVIDERS.mainnet,
blockTracker: buildMockBlockTracker('0x102833C', HTTP_PROVIDERS.mainnet),
state: {
selectedNetworkClientId: NetworkType.mainnet,
networksMetadata: {
Expand All @@ -376,8 +355,8 @@ const MOCK_MAINNET_NETWORK: MockNetwork = {
};

const MOCK_LINEA_MAINNET_NETWORK: MockNetwork = {
provider: PALM_PROVIDER,
blockTracker: buildMockBlockTracker('0xA6EDFC', PALM_PROVIDER),
provider: HTTP_PROVIDERS.linea,
blockTracker: buildMockBlockTracker('0xA6EDFC', HTTP_PROVIDERS.linea),
state: {
selectedNetworkClientId: NetworkType['linea-mainnet'],
networksMetadata: {
Expand All @@ -397,8 +376,8 @@ const MOCK_LINEA_MAINNET_NETWORK: MockNetwork = {
};

const MOCK_LINEA_GOERLI_NETWORK: MockNetwork = {
provider: PALM_PROVIDER,
blockTracker: buildMockBlockTracker('0xA6EDFC', PALM_PROVIDER),
provider: HTTP_PROVIDERS.linea_goerli,
blockTracker: buildMockBlockTracker('0xA6EDFC', HTTP_PROVIDERS.linea_goerli),
state: {
selectedNetworkClientId: NetworkType['linea-goerli'],
networksMetadata: {
Expand All @@ -418,8 +397,8 @@ const MOCK_LINEA_GOERLI_NETWORK: MockNetwork = {
};

const MOCK_CUSTOM_NETWORK: MockNetwork = {
provider: PALM_PROVIDER,
blockTracker: buildMockBlockTracker('0xA6EDFC', PALM_PROVIDER),
provider: HTTP_PROVIDERS.custom,
blockTracker: buildMockBlockTracker('0xA6EDFC', HTTP_PROVIDERS.custom),
state: {
selectedNetworkClientId: 'uuid-1',
networksMetadata: {
Expand Down Expand Up @@ -577,11 +556,14 @@ describe('TransactionController', () => {
);

const { messenger: givenRestrictedMessenger, ...otherOptions } = {
blockTracker: network.blockTracker,
disableHistory: false,
disableSendFlowHistory: false,
disableSwaps: false,
getCurrentNetworkEIP1559Compatibility: async () => false,
getGlobalProviderAndBlockTracker: () => ({
provider: network.provider,
blockTracker: network.blockTracker,
}),
getNetworkState: () => network.state,
// TODO: Replace with a real type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -591,7 +573,6 @@ describe('TransactionController', () => {
isMultichainEnabled: false,
hooks: {},
onNetworkStateChange: network.subscribe,
provider: network.provider,
sign: async (transaction: TypedTransaction) => transaction,
transactionHistoryLimit: 40,
...givenOptions,
Expand Down Expand Up @@ -785,21 +766,25 @@ describe('TransactionController', () => {
return pendingTransactionTrackerMock;
});

multichainTrackingHelperClassMock.mockImplementation(({ provider }) => {
multichainTrackingHelperMock = {
getEthQuery: jest.fn().mockImplementation(() => {
return new EthQuery(provider);
}),
getProvider: jest.fn().mockImplementation(() => {
return provider;
}),
checkForPendingTransactionAndStartPolling: jest.fn(),
getNonceLock: getNonceLockSpy,
initialize: jest.fn(),
has: jest.fn().mockReturnValue(false),
} as unknown as jest.Mocked<MultichainTrackingHelper>;
return multichainTrackingHelperMock;
});
multichainTrackingHelperClassMock.mockImplementation(
({ getGlobalProviderAndBlockTracker }) => {
multichainTrackingHelperMock = {
getEthQuery: jest.fn().mockImplementation(() => {
return new EthQuery(
getGlobalProviderAndBlockTracker()?.provider as Provider,
);
}),
getProvider: jest.fn().mockImplementation(() => {
return getGlobalProviderAndBlockTracker()?.provider as Provider;
}),
checkForPendingTransactionAndStartPolling: jest.fn(),
getNonceLock: getNonceLockSpy,
initialize: jest.fn(),
has: jest.fn().mockReturnValue(false),
} as unknown as jest.Mocked<MultichainTrackingHelper>;
return multichainTrackingHelperMock;
},
);

defaultGasFeeFlowClassMock.mockImplementation(() => {
defaultGasFeeFlowMock = {
Expand Down Expand Up @@ -887,117 +872,6 @@ describe('TransactionController', () => {
pendingTransactionTrackerMock.startIfPendingTransactions,
).toHaveBeenCalledTimes(1);
});

describe('nonce tracker', () => {
it('uses external pending transactions', async () => {
const nonceTrackerMock = jest
.spyOn(NonceTrackerPackage, 'NonceTracker')
.mockImplementation();

const externalPendingTransactions = [
{
from: '0x1',
},
{ from: '0x2' },
];

const getExternalPendingTransactions = jest
.fn()
.mockReturnValueOnce(externalPendingTransactions);

setupController({
options: {
getExternalPendingTransactions,
state: {
transactions: [
{
...TRANSACTION_META_MOCK,
chainId: MOCK_NETWORK.state.providerConfig.chainId,
status: TransactionStatus.submitted,
},
],
},
},
});

const pendingTransactions =
nonceTrackerMock.mock.calls[0][0].getPendingTransactions(
ACCOUNT_MOCK,
);

expect(nonceTrackerMock).toHaveBeenCalledTimes(1);
expect(pendingTransactions).toStrictEqual([
expect.any(Object),
...externalPendingTransactions,
]);
expect(getExternalPendingTransactions).toHaveBeenCalledTimes(1);
expect(getExternalPendingTransactions).toHaveBeenCalledWith(
ACCOUNT_MOCK,
// This is undefined for the base nonceTracker
undefined,
);
});
});

describe('onBootCleanup', () => {
afterEach(() => {
updateGasMock.mockReset();
updateGasFeesMock.mockReset();
});

it('submits approved transactions for all chains', async () => {
const mockTransactionMeta = {
from: ACCOUNT_MOCK,
status: TransactionStatus.approved,
txParams: {
from: ACCOUNT_MOCK,
to: ACCOUNT_2_MOCK,
},
};
const mockedTransactions = [
{
id: '123',
history: [{ ...mockTransactionMeta, id: '123' }],
chainId: toHex(5),
...mockTransactionMeta,
},
{
id: '456',
history: [{ ...mockTransactionMeta, id: '456' }],
chainId: toHex(1),
...mockTransactionMeta,
},
{
id: '789',
history: [{ ...mockTransactionMeta, id: '789' }],
chainId: toHex(16),
...mockTransactionMeta,
},
];

const mockedControllerState = {
transactions: mockedTransactions,
methodData: {},
lastFetchedBlockNumbers: {},
};

const { controller } = setupController({
options: {
// TODO: Replace `any` with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
state: mockedControllerState as any,
},
});

await flushPromises();

const { transactions } = controller.state;

expect(transactions[0].status).toBe(TransactionStatus.submitted);
expect(transactions[1].status).toBe(TransactionStatus.submitted);
expect(transactions[2].status).toBe(TransactionStatus.submitted);
});
});
});

describe('estimateGas', () => {
Expand Down Expand Up @@ -1446,6 +1320,8 @@ describe('TransactionController', () => {
to: ACCOUNT_MOCK,
});

await flushPromises();

const expectedInitialSnapshot = {
actionId: undefined,
chainId: expect.any(String),
Expand Down Expand Up @@ -2006,19 +1882,6 @@ describe('TransactionController', () => {
await expectTransactionToFail(controller, 'No sign method defined');
});

it('if no chainId defined', async () => {
const { controller } = setupController({
network: MOCK_NETWORK_WITHOUT_CHAIN_ID,
messengerOptions: {
addTransactionApprovalRequest: {
state: 'approved',
},
},
});

await expectTransactionToFail(controller, 'No chainId defined');
});

it('if unexpected status', async () => {
const { controller, messenger } = setupController();

Expand Down
Loading