Skip to content

Commit

Permalink
refactor: generate nonces using nonce tracker (#7689)
Browse files Browse the repository at this point in the history
Use nonce tracker package.
Synchronise custom nonce logic.
Generate submit history.
  • Loading branch information
matthewwalsh0 authored Nov 24, 2023
1 parent 0163b8b commit 13acaa7
Show file tree
Hide file tree
Showing 6 changed files with 768 additions and 306 deletions.
58 changes: 57 additions & 1 deletion app/store/migrations.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { v1 as random, v4 } from 'uuid';
import { isObject, hasProperty } from '@metamask/utils';
import { NetworksChainId } from '@metamask/controller-utils';
import { NetworkType, NetworksChainId } from '@metamask/controller-utils';
import { captureException } from '@sentry/react-native';
import { mapValues } from 'lodash';
import AppConstants from '../core/AppConstants';
Expand Down Expand Up @@ -813,6 +813,62 @@ export const migrations = {

return state;
},
/**
* Populate the submitHistory in the TransactionController using any
* transaction metadata entries that have a rawTransaction value.
* @param {any} state - Redux state
* @returns
*/
27: (state) => {
const backgroundState = state.engine.backgroundState;

const transactionControllerState = backgroundState.TransactionController;

if (!transactionControllerState) return state;

const transactions = transactionControllerState.transactions || [];
const networkControllerState = backgroundState.NetworkController || {};
const providerConfig = networkControllerState.providerConfig || {};

const networkConfigurations =
networkControllerState.networkConfigurations || {};

const submitHistory = transactions
.filter((tx) => tx.rawTransaction?.length)
.map((tx) => {
const matchingProviderConfig =
providerConfig.chainId === tx.chainId ? providerConfig : undefined;

const matchingNetworkConfigurations = Object.values(
networkConfigurations,
).filter((c) => c.chainId === tx.chainId);

const networkUrl = matchingNetworkConfigurations.map((c) => c.rpcUrl);

const networkType = matchingProviderConfig
? matchingProviderConfig.type
: matchingNetworkConfigurations?.length
? NetworkType.rpc
: undefined;

return {
chainId: tx.chainId,
hash: tx.transactionHash,
migration: true,
networkType,
networkUrl,
origin: tx.origin,
time: tx.time,
transaction: tx.transaction,
rawTransaction: tx.rawTransaction,
};
});

state.engine.backgroundState.TransactionController.submitHistory =
submitHistory;

return state;
},
// If you are implementing a migration it will break the migration tests,
// please write a unit for your specific migration version
};
Expand Down
275 changes: 272 additions & 3 deletions app/store/migrations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ describe('Redux Persist Migrations', () => {
const oldState = {
engine: {
backgroundState: {
PhishingController: {
listState: {},
TransactionController: {
transactions: [],
},
},
},
Expand All @@ -49,7 +49,10 @@ describe('Redux Persist Migrations', () => {
expect(newState).toStrictEqual({
engine: {
backgroundState: {
PhishingController: {},
TransactionController: {
transactions: [],
submitHistory: [],
},
},
},
});
Expand Down Expand Up @@ -1041,4 +1044,270 @@ describe('Redux Persist Migrations', () => {
);
});
});

describe('#27', () => {
it('does nothing if no transaction controller state', () => {
const oldState = {
engine: {
backgroundState: {
OtherController: {
exampleState: {
testProperty: 'testValue',
},
},
},
},
};

const migration = migrations[27];
const newState = migration(oldState);

expect(newState).toStrictEqual(oldState);
});

it('sets empty submit history if no transactions', () => {
const oldState = {
engine: {
backgroundState: {
TransactionController: {
transactions: [],
},
},
},
};

const migration = migrations[27];
const newState = migration(oldState);

expect(newState).toStrictEqual({
engine: {
backgroundState: {
TransactionController: {
transactions: [],
submitHistory: [],
},
},
},
});
});

it('populates submit history using transactions', () => {
const oldState = {
engine: {
backgroundState: {
TransactionController: {
transactions: [
{
chainId: '5',
id: '1',
origin: 'test.com',
status: 'confirmed',
time: 1631714312,
transaction: {
from: '0x1',
},
transactionHash: '0x2',
rawTransaction: '0x3',
},
],
},
},
},
};

const migration = migrations[27];
const newState = migration(oldState);

expect(
newState.engine.backgroundState.TransactionController.submitHistory,
).toStrictEqual([
{
chainId: '5',
hash: '0x2',
migration: true,
networkType: undefined,
networkUrl: [],
origin: 'test.com',
rawTransaction: '0x3',
time: 1631714312,
transaction: {
from: '0x1',
},
},
]);
});

it('ignores transactions with no raw transaction', () => {
const oldState = {
engine: {
backgroundState: {
TransactionController: {
transactions: [
{
chainId: '5',
id: '1',
origin: 'test.com',
status: 'confirmed',
time: 1631714312,
transaction: {
from: '0x1',
},
transactionHash: '0x2',
rawTransaction: '0x3',
},
{
chainId: '5',
id: '2',
origin: 'test.com',
status: 'confirmed',
time: 1631714312,
transaction: {
from: '0x1',
},
transactionHash: '0x2',
},
{
chainId: '1',
id: '3',
origin: 'test2.com',
status: 'submitted',
time: 1631714313,
transaction: {
from: '0x6',
},
transactionHash: '0x4',
rawTransaction: '0x5',
},
],
},
},
},
};

const migration = migrations[27];
const newState = migration(oldState);

expect(
newState.engine.backgroundState.TransactionController.submitHistory,
).toStrictEqual([
{
chainId: '5',
hash: '0x2',
migration: true,
networkType: undefined,
networkUrl: [],
origin: 'test.com',
rawTransaction: '0x3',
time: 1631714312,
transaction: {
from: '0x1',
},
},
{
chainId: '1',
hash: '0x4',
migration: true,
networkType: undefined,
networkUrl: [],
origin: 'test2.com',
rawTransaction: '0x5',
time: 1631714313,
transaction: {
from: '0x6',
},
},
]);
});

it('sets network type and url using provider config and network configurations', () => {
const oldState = {
engine: {
backgroundState: {
NetworkController: {
providerConfig: {
chainId: '5',
type: 'goerli',
},
networkConfigurations: {
'1-2-3': {
chainId: '5',
rpcUrl: 'http://goerli.test.com',
},
'2-3-4': {
chainId: '5',
rpcUrl: 'http://goerli.test2.com',
},
'3-4-5': {
chainId: '1',
rpcUrl: 'http://mainnet.test.com',
},
},
},
TransactionController: {
transactions: [
{
chainId: '5',
id: '1',
origin: 'test.com',
status: 'confirmed',
time: 1631714312,
transaction: {
from: '0x1',
},
transactionHash: '0x2',
rawTransaction: '0x3',
},
{
chainId: '1',
id: '2',
origin: 'test2.com',
status: 'confirmed',
time: 1631714313,
transaction: {
from: '0x4',
},
transactionHash: '0x5',
rawTransaction: '0x6',
},
],
},
},
},
};

const migration = migrations[27];
const newState = migration(oldState);

expect(
newState.engine.backgroundState.TransactionController.submitHistory,
).toStrictEqual([
{
chainId: '5',
hash: '0x2',
migration: true,
networkType: 'goerli',
networkUrl: ['http://goerli.test.com', 'http://goerli.test2.com'],
origin: 'test.com',
rawTransaction: '0x3',
time: 1631714312,
transaction: {
from: '0x1',
},
},
{
chainId: '1',
hash: '0x5',
migration: true,
networkType: 'rpc',
networkUrl: ['http://mainnet.test.com'],
origin: 'test2.com',
rawTransaction: '0x6',
time: 1631714313,
transaction: {
from: '0x4',
},
},
]);
});
});
});
18 changes: 8 additions & 10 deletions app/util/networks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ import {
LINEA_MAINNET,
} from '../../../app/constants/network';
import { NetworkSwitchErrorType } from '../../../app/constants/error';
import {
NetworksChainId,
NetworkType,
query,
} from '@metamask/controller-utils';
import { NetworksChainId, NetworkType } from '@metamask/controller-utils';
import Engine from '../../core/Engine';
import { toLowerCaseEquals } from '../general';
import { fastSplit } from '../number';
Expand Down Expand Up @@ -334,12 +330,14 @@ export function isPrefixedFormattedHexString(value) {

export const getNetworkNonce = async ({ from }) => {
const { TransactionController } = Engine.context;
const networkNonce = await query(
TransactionController.ethQuery,
'getTransactionCount',
[from, 'pending'],

const { nextNonce, releaseLock } = await TransactionController.getNonceLock(
from,
);
return parseInt(networkNonce, 16);

releaseLock();

return nextNonce;
};

export function blockTagParamIndex(payload) {
Expand Down
Loading

0 comments on commit 13acaa7

Please sign in to comment.