Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade transaction controller to get incoming transactions using accounts API #28597

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@
"@metamask/snaps-sdk": "^6.11.0",
"@metamask/snaps-utils": "^8.6.0",
"@metamask/solana-wallet-snap": "^0.1.9",
"@metamask/transaction-controller": "^39.1.0",
"@metamask/transaction-controller": "npm:@metamask-previews/transaction-controller@39.1.0-preview-690da12e",
"@metamask/user-operation-controller": "^13.0.0",
"@metamask/utils": "^10.0.1",
"@ngraveio/bc-ur": "^1.1.12",
Expand Down
12 changes: 12 additions & 0 deletions test/e2e/fixture-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,18 @@ class FixtureBuilder {
});
}

withIncomingTransactionsPreferences(incomingTransactionsPreferences) {
return this.withPreferencesController({
featureFlags: {
showIncomingTransactions: incomingTransactionsPreferences,
},
});
}

withIncomingTransactionsCache(cache) {
return this.withTransactionController({ lastFetchedBlockNumbers: cache });
}

build() {
this.fixture.meta = {
version: FIXTURE_STATE_METADATA_VERSION,
Expand Down
23 changes: 23 additions & 0 deletions test/e2e/page-objects/pages/homepage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class HomePage {
tag: 'h6',
};

private readonly activityListAction =
'[data-testid="activity-list-item-action"]';

constructor(driver: Driver) {
this.driver = driver;
this.headerNavbar = new HeaderNavbar(driver);
Expand Down Expand Up @@ -360,6 +363,26 @@ class HomePage {
return uiState.metamask.hasAccountSyncingSyncedAtLeastOnce === true;
}, 10000);
}

async check_txAction(expectedAction: string, expectedNumber: number = 1) {
const transactionActions = await this.driver.findElements(
this.activityListAction,
);

const transactionActionText = await transactionActions[
expectedNumber - 1
].getText();

assert.equal(
transactionActionText,
expectedAction,
`${transactionActionText} is displayed as transaction action instead of ${expectedAction} for transaction ${expectedNumber}`,
);

console.log(
`Action for transaction ${expectedNumber} is displayed as ${expectedAction}`,
);
}
}

export default HomePage;
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@
"allDetectedTokens": {}
},
"TxController": {
"lastFetchedBlockNumbers": "object",
"methodData": "object",
"transactions": "object",
"lastFetchedBlockNumbers": "object",
Expand Down
201 changes: 201 additions & 0 deletions test/e2e/tests/transaction/incoming-transactions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import { Mockttp } from 'mockttp';
import { CHAIN_IDS } from '@metamask/transaction-controller';
import { loginWithoutBalanceValidation } from '../../page-objects/flows/login.flow';
import HomePage from '../../page-objects/pages/homepage';
import { Driver } from '../../webdriver/driver';
import { DEFAULT_FIXTURE_ACCOUNT } from '../../constants';
import { withFixtures } from '../../helpers';
import FixtureBuilder from '../../fixture-builder';
import { switchToNetworkFlow } from '../../page-objects/flows/network.flow';

const PAST_DURATION_MOCK = 1000 * 60 * 60 * 24; // 1 Day
const TIMESTAMP_MOCK = Date.now() - PAST_DURATION_MOCK;
const CURSOR_MOCK = 'test-123';

const RESPONSE_STANDARD_MOCK = {
hash: '0x1',
timestamp: new Date(TIMESTAMP_MOCK).toISOString(),
chainId: 1,
blockNumber: 1,
blockHash: '0x2',
gas: 1,
gasUsed: 1,
gasPrice: '1',
effectiveGasPrice: '1',
nonce: 1,
cumulativeGasUsed: 1,
methodId: null,
value: '1230000000000000000',
to: DEFAULT_FIXTURE_ACCOUNT.toLowerCase(),
from: '0x2',
isError: false,
valueTransfers: [],
};

const RESPONSE_STANDARD_2_MOCK = {
...RESPONSE_STANDARD_MOCK,
hash: '0x2',
value: '2340000000000000000',
};

const RESPONSE_TOKEN_TRANSFER_MOCK = {
...RESPONSE_STANDARD_MOCK,
to: '0x2',
valueTransfers: [
{
contractAddress: '0x123',
decimal: 18,
symbol: 'ABC',
from: '0x2',
to: DEFAULT_FIXTURE_ACCOUNT.toLowerCase(),
amount: '4560000000000000000',
},
],
};

const RESPONSE_OUTGOING_MOCK = {
...RESPONSE_STANDARD_MOCK,
from: DEFAULT_FIXTURE_ACCOUNT.toLowerCase(),
to: '0x2',
};

async function mockAccountsApi(
mockServer: Mockttp,
{
cursor,
transactions,
}: { cursor?: string; transactions?: Record<string, unknown>[] } = {},
) {
return [
await mockServer
.forGet(/https:\/\/accounts.api.cx.metamask.io\/v1\/accounts\//u)
Fixed Show fixed Hide fixed
.withQuery(cursor ? { cursor } : {})
.thenCallback(() => ({
statusCode: 200,
json: {
data: transactions ?? [
RESPONSE_STANDARD_MOCK,
RESPONSE_STANDARD_2_MOCK,
],
pageInfo: { hasNextPage: false },
},
})),
];
}

describe('Incoming Transactions', function () {
it('adds standard incoming transactions', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder()
.withIncomingTransactionsPreferences({
[CHAIN_IDS.MAINNET]: true,
})
.build(),
title: this.test?.fullTitle(),
testSpecificMock: mockAccountsApi,
},
async ({ driver }: { driver: Driver }) => {
await loginWithoutBalanceValidation(driver);
await switchToNetworkFlow(driver, 'Ethereum Mainnet');

const homepage = new HomePage(driver);
await homepage.goToActivityList();
await homepage.check_confirmedTxNumberDisplayedInActivity(2);

await homepage.check_txAction('Receive', 1);
await homepage.check_txAmountInActivity('1.23 ETH', 1);

await homepage.check_txAction('Receive', 2);
await homepage.check_txAmountInActivity('2.34 ETH', 2);
},
);
});

it('ignores token transfer transactions', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder()
.withIncomingTransactionsPreferences({
[CHAIN_IDS.MAINNET]: true,
})
.build(),
title: this.test?.fullTitle(),
testSpecificMock: (server: Mockttp) =>
mockAccountsApi(server, {
transactions: [
RESPONSE_STANDARD_MOCK,
RESPONSE_TOKEN_TRANSFER_MOCK,
],
}),
},
async ({ driver }: { driver: Driver }) => {
await loginWithoutBalanceValidation(driver);
await switchToNetworkFlow(driver, 'Ethereum Mainnet');

const homepage = new HomePage(driver);
await homepage.goToActivityList();
await homepage.check_confirmedTxNumberDisplayedInActivity(1);
},
);
});

it('ignores outgoing transactions', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder()
.withIncomingTransactionsPreferences({
[CHAIN_IDS.MAINNET]: true,
})
.build(),
title: this.test?.fullTitle(),
testSpecificMock: (server: Mockttp) =>
mockAccountsApi(server, {
transactions: [RESPONSE_STANDARD_MOCK, RESPONSE_OUTGOING_MOCK],
}),
},
async ({ driver }: { driver: Driver }) => {
await loginWithoutBalanceValidation(driver);
await switchToNetworkFlow(driver, 'Ethereum Mainnet');

const homepage = new HomePage(driver);
await homepage.goToActivityList();
await homepage.check_confirmedTxNumberDisplayedInActivity(1);
},
);
});

it('queries transactions using saved cursor', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder()
.withIncomingTransactionsPreferences({
[CHAIN_IDS.MAINNET]: true,
})
.withIncomingTransactionsCache({
[`accounts-api#${
CHAIN_IDS.MAINNET
}#${DEFAULT_FIXTURE_ACCOUNT.toLowerCase()}`]: CURSOR_MOCK,
})
.build(),
title: this.test?.fullTitle(),
testSpecificMock: (server: Mockttp) =>
mockAccountsApi(server, {
cursor: CURSOR_MOCK,
transactions: [RESPONSE_STANDARD_MOCK],
}),
},
async ({ driver }: { driver: Driver }) => {
await loginWithoutBalanceValidation(driver);
await switchToNetworkFlow(driver, 'Ethereum Mainnet');

const homepage = new HomePage(driver);
await homepage.goToActivityList();
await homepage.check_confirmedTxNumberDisplayedInActivity(1);

await homepage.check_txAction('Receive', 1);
await homepage.check_txAmountInActivity('1.23 ETH', 1);
},
);
});
});
58 changes: 29 additions & 29 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6665,27 +6665,23 @@ __metadata:
languageName: node
linkType: hard

"@metamask/transaction-controller@npm:^34.0.0":
version: 34.0.0
resolution: "@metamask/transaction-controller@npm:34.0.0"
"@metamask/transaction-controller@npm:@metamask-previews/[email protected]":
version: 39.1.0-preview-690da12e
resolution: "@metamask-previews/transaction-controller@npm:39.1.0-preview-690da12e"
dependencies:
"@ethereumjs/common": "npm:^3.2.0"
"@ethereumjs/tx": "npm:^4.2.0"
"@ethereumjs/util": "npm:^8.1.0"
"@ethersproject/abi": "npm:^5.7.0"
"@ethersproject/contracts": "npm:^5.7.0"
"@ethersproject/providers": "npm:^5.7.0"
"@metamask/accounts-controller": "npm:^17.2.0"
"@metamask/approval-controller": "npm:^7.0.0"
"@metamask/base-controller": "npm:^6.0.0"
"@metamask/controller-utils": "npm:^11.0.0"
"@metamask/base-controller": "npm:^7.0.2"
"@metamask/controller-utils": "npm:^11.4.3"
"@metamask/eth-query": "npm:^4.0.0"
"@metamask/gas-fee-controller": "npm:^18.0.0"
"@metamask/metamask-eth-abis": "npm:^3.1.1"
"@metamask/network-controller": "npm:^19.0.0"
"@metamask/nonce-tracker": "npm:^5.0.0"
"@metamask/rpc-errors": "npm:^6.2.1"
"@metamask/utils": "npm:^8.3.0"
"@metamask/nonce-tracker": "npm:^6.0.0"
"@metamask/rpc-errors": "npm:^7.0.1"
"@metamask/utils": "npm:^10.0.0"
async-mutex: "npm:^0.5.0"
bn.js: "npm:^5.2.1"
eth-method-registry: "npm:^4.0.0"
Expand All @@ -6694,31 +6690,35 @@ __metadata:
uuid: "npm:^8.3.2"
peerDependencies:
"@babel/runtime": ^7.23.9
"@metamask/accounts-controller": ^17.0.0
"@metamask/accounts-controller": ^19.0.0
"@metamask/approval-controller": ^7.0.0
"@metamask/gas-fee-controller": ^18.0.0
"@metamask/network-controller": ^19.0.0
checksum: 10/170b760dec516ca191de8fa2e7afce6aeb445880fa5717387c047202643ec303382a9edbe6c80024cbfc2e219ef98de6f9785b13bb4032b7b1100f29e6f3948d
"@metamask/gas-fee-controller": ^22.0.0
"@metamask/network-controller": ^22.0.0
checksum: 10/969fc76414d624293000e19c9cbf75d3033a6ce4d69b4789e44d02d42b25a44220e2a55c390c75f5ff97ebb200f3bd5811e68d66745cdfa6512d5e2ea15a40c7
languageName: node
linkType: hard

"@metamask/transaction-controller@npm:^39.1.0":
version: 39.1.0
resolution: "@metamask/transaction-controller@npm:39.1.0"
"@metamask/transaction-controller@npm:^34.0.0":
version: 34.0.0
resolution: "@metamask/transaction-controller@npm:34.0.0"
dependencies:
"@ethereumjs/common": "npm:^3.2.0"
"@ethereumjs/tx": "npm:^4.2.0"
"@ethereumjs/util": "npm:^8.1.0"
"@ethersproject/abi": "npm:^5.7.0"
"@ethersproject/contracts": "npm:^5.7.0"
"@ethersproject/providers": "npm:^5.7.0"
"@metamask/base-controller": "npm:^7.0.2"
"@metamask/controller-utils": "npm:^11.4.3"
"@metamask/accounts-controller": "npm:^17.2.0"
"@metamask/approval-controller": "npm:^7.0.0"
"@metamask/base-controller": "npm:^6.0.0"
"@metamask/controller-utils": "npm:^11.0.0"
"@metamask/eth-query": "npm:^4.0.0"
"@metamask/gas-fee-controller": "npm:^18.0.0"
"@metamask/metamask-eth-abis": "npm:^3.1.1"
"@metamask/nonce-tracker": "npm:^6.0.0"
"@metamask/rpc-errors": "npm:^7.0.1"
"@metamask/utils": "npm:^10.0.0"
"@metamask/network-controller": "npm:^19.0.0"
"@metamask/nonce-tracker": "npm:^5.0.0"
"@metamask/rpc-errors": "npm:^6.2.1"
"@metamask/utils": "npm:^8.3.0"
async-mutex: "npm:^0.5.0"
bn.js: "npm:^5.2.1"
eth-method-registry: "npm:^4.0.0"
Expand All @@ -6727,11 +6727,11 @@ __metadata:
uuid: "npm:^8.3.2"
peerDependencies:
"@babel/runtime": ^7.23.9
"@metamask/accounts-controller": ^19.0.0
"@metamask/accounts-controller": ^17.0.0
"@metamask/approval-controller": ^7.0.0
"@metamask/gas-fee-controller": ^22.0.0
"@metamask/network-controller": ^22.0.0
checksum: 10/9c18f01167ca70556323190c3b3b8df29d5c1d45846e6d50208b49d27bd3d361ab89f103d5f4a784bbc70cee3e5ef595bab8cf568926c790236d32ace07a1283
"@metamask/gas-fee-controller": ^18.0.0
"@metamask/network-controller": ^19.0.0
checksum: 10/170b760dec516ca191de8fa2e7afce6aeb445880fa5717387c047202643ec303382a9edbe6c80024cbfc2e219ef98de6f9785b13bb4032b7b1100f29e6f3948d
languageName: node
linkType: hard

Expand Down Expand Up @@ -26855,7 +26855,7 @@ __metadata:
"@metamask/solana-wallet-snap": "npm:^0.1.9"
"@metamask/test-bundler": "npm:^1.0.0"
"@metamask/test-dapp": "npm:8.13.0"
"@metamask/transaction-controller": "npm:^39.1.0"
"@metamask/transaction-controller": "npm:@metamask-previews/transaction-controller@39.1.0-preview-690da12e"
"@metamask/user-operation-controller": "npm:^13.0.0"
"@metamask/utils": "npm:^10.0.1"
"@ngraveio/bc-ur": "npm:^1.1.12"
Expand Down
Loading