Skip to content

Commit 3754576

Browse files
authored
feat: Add tracing to the STX controller (#515)
1 parent b658ebf commit 3754576

File tree

3 files changed

+202
-25
lines changed

3 files changed

+202
-25
lines changed

src/SmartTransactionsController.test.ts

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import * as sinon from 'sinon';
2222

2323
import packageJson from '../package.json';
2424
import { advanceTime, flushPromises, getFakeProvider } from '../tests/helpers';
25-
import { API_BASE_URL, SENTINEL_API_BASE_URL_MAP } from './constants';
25+
import {
26+
API_BASE_URL,
27+
SENTINEL_API_BASE_URL_MAP,
28+
SmartTransactionsTraceName,
29+
} from './constants';
2630
import SmartTransactionsController, {
2731
DEFAULT_INTERVAL,
2832
getDefaultSmartTransactionsControllerState,
@@ -2387,6 +2391,151 @@ describe('SmartTransactionsController', () => {
23872391
);
23882392
});
23892393
});
2394+
2395+
describe('Tracing', () => {
2396+
const createTraceCallback = () =>
2397+
jest.fn().mockImplementation(async (_request, fn) => {
2398+
return fn?.();
2399+
});
2400+
2401+
it('traces getFees API call with expected name', async () => {
2402+
const traceCallback = createTraceCallback();
2403+
2404+
await withController(
2405+
{
2406+
options: {
2407+
trace: traceCallback,
2408+
},
2409+
},
2410+
async ({ controller }) => {
2411+
const apiUrl = API_BASE_URL;
2412+
nock(apiUrl)
2413+
.post(`/networks/${ethereumChainIdDec}/getFees`)
2414+
.reply(200, createGetFeesApiResponse());
2415+
2416+
const tradeTx = createUnsignedTransaction(ethereumChainIdDec);
2417+
await controller.getFees(tradeTx);
2418+
2419+
expect(traceCallback).toHaveBeenCalledWith(
2420+
{ name: SmartTransactionsTraceName.GetFees },
2421+
expect.any(Function),
2422+
);
2423+
},
2424+
);
2425+
});
2426+
2427+
it('traces submitSignedTransactions API call with expected name', async () => {
2428+
const traceCallback = createTraceCallback();
2429+
2430+
await withController(
2431+
{
2432+
options: {
2433+
trace: traceCallback,
2434+
},
2435+
},
2436+
async ({ controller }) => {
2437+
const apiUrl = API_BASE_URL;
2438+
nock(apiUrl)
2439+
.post(
2440+
`/networks/${ethereumChainIdDec}/submitTransactions?stxControllerVersion=${packageJson.version}`,
2441+
)
2442+
.reply(200, createSubmitTransactionsApiResponse());
2443+
2444+
const signedTx = createSignedTransaction();
2445+
const signedCanceledTx = createSignedCanceledTransaction();
2446+
const txParams = createTxParams();
2447+
2448+
await controller.submitSignedTransactions({
2449+
signedTransactions: [signedTx],
2450+
signedCanceledTransactions: [signedCanceledTx],
2451+
txParams,
2452+
});
2453+
2454+
expect(traceCallback).toHaveBeenCalledWith(
2455+
{ name: SmartTransactionsTraceName.SubmitTransactions },
2456+
expect.any(Function),
2457+
);
2458+
},
2459+
);
2460+
});
2461+
2462+
it('traces cancelSmartTransaction API call with expected name', async () => {
2463+
const traceCallback = createTraceCallback();
2464+
2465+
await withController(
2466+
{
2467+
options: {
2468+
trace: traceCallback,
2469+
},
2470+
},
2471+
async ({ controller }) => {
2472+
const apiUrl = API_BASE_URL;
2473+
nock(apiUrl)
2474+
.post(`/networks/${ethereumChainIdDec}/cancel`)
2475+
.reply(200, {});
2476+
2477+
await controller.cancelSmartTransaction('uuid1');
2478+
2479+
expect(traceCallback).toHaveBeenCalledWith(
2480+
{ name: SmartTransactionsTraceName.CancelTransaction },
2481+
expect.any(Function),
2482+
);
2483+
},
2484+
);
2485+
});
2486+
2487+
it('traces fetchLiveness API call with expected name', async () => {
2488+
const traceCallback = createTraceCallback();
2489+
2490+
await withController(
2491+
{
2492+
options: {
2493+
trace: traceCallback,
2494+
},
2495+
},
2496+
async ({ controller }) => {
2497+
nock(SENTINEL_API_BASE_URL_MAP[ethereumChainIdDec])
2498+
.get(`/network`)
2499+
.reply(200, createSuccessLivenessApiResponse());
2500+
2501+
await controller.fetchLiveness();
2502+
2503+
expect(traceCallback).toHaveBeenCalledWith(
2504+
{ name: SmartTransactionsTraceName.FetchLiveness },
2505+
expect.any(Function),
2506+
);
2507+
},
2508+
);
2509+
});
2510+
2511+
it('returns correct result when tracing is enabled', async () => {
2512+
const traceCallback = createTraceCallback();
2513+
2514+
await withController(
2515+
{
2516+
options: {
2517+
trace: traceCallback,
2518+
},
2519+
},
2520+
async ({ controller }) => {
2521+
const apiUrl = API_BASE_URL;
2522+
const expectedResponse = createGetFeesApiResponse();
2523+
nock(apiUrl)
2524+
.post(`/networks/${ethereumChainIdDec}/getFees`)
2525+
.reply(200, expectedResponse);
2526+
2527+
const tradeTx = createUnsignedTransaction(ethereumChainIdDec);
2528+
const result = await controller.getFees(tradeTx);
2529+
2530+
expect(traceCallback).toHaveBeenCalled();
2531+
expect(result).toMatchObject({
2532+
tradeTxFees: expectedResponse.txs[0],
2533+
approvalTxFees: null,
2534+
});
2535+
},
2536+
);
2537+
});
2538+
});
23902539
});
23912540

23922541
type WithControllerCallback<ReturnValue> = ({

src/SmartTransactionsController.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
safelyExecute,
1010
ChainId,
1111
isSafeDynamicKey,
12+
type TraceCallback,
1213
} from '@metamask/controller-utils';
1314
import EthQuery from '@metamask/eth-query';
1415
import type {
@@ -27,7 +28,11 @@ import { TransactionStatus } from '@metamask/transaction-controller';
2728
import { BigNumber } from 'bignumber.js';
2829
import cloneDeep from 'lodash/cloneDeep';
2930

30-
import { MetaMetricsEventCategory, MetaMetricsEventName } from './constants';
31+
import {
32+
MetaMetricsEventCategory,
33+
MetaMetricsEventName,
34+
SmartTransactionsTraceName,
35+
} from './constants';
3136
import type {
3237
Fees,
3338
Hex,
@@ -206,6 +211,7 @@ type SmartTransactionsControllerOptions = {
206211
getMetaMetricsProps: () => Promise<MetaMetricsProps>;
207212
getFeatureFlags: () => FeatureFlags;
208213
updateTransaction: (transaction: TransactionMeta, note: string) => void;
214+
trace?: TraceCallback;
209215
};
210216

211217
export type SmartTransactionsControllerPollingInput = {
@@ -245,6 +251,8 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
245251

246252
#updateTransaction: SmartTransactionsControllerOptions['updateTransaction'];
247253

254+
#trace: TraceCallback;
255+
248256
/* istanbul ignore next */
249257
async #fetch(request: string, options?: RequestInit) {
250258
const fetchOptions = {
@@ -272,6 +280,7 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
272280
getMetaMetricsProps,
273281
getFeatureFlags,
274282
updateTransaction,
283+
trace,
275284
}: SmartTransactionsControllerOptions) {
276285
super({
277286
name: controllerName,
@@ -295,6 +304,7 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
295304
this.#getMetaMetricsProps = getMetaMetricsProps;
296305
this.#getFeatureFlags = getFeatureFlags;
297306
this.#updateTransaction = updateTransaction;
307+
this.#trace = trace ?? (((_request, fn) => fn?.()) as TraceCallback);
298308

299309
this.initializeSmartTransactionsForChainId();
300310

@@ -860,7 +870,7 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
860870
const chainId = this.#getChainId({
861871
networkClientId: selectedNetworkClientId,
862872
});
863-
const transactions = [];
873+
const transactions: UnsignedTransaction[] = [];
864874
let unsignedTradeTransactionWithNonce;
865875
if (approvalTx) {
866876
const unsignedApprovalTransactionWithNonce =
@@ -880,14 +890,15 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
880890
);
881891
}
882892
transactions.push(unsignedTradeTransactionWithNonce);
883-
const data = await this.#fetch(
884-
getAPIRequestURL(APIType.GET_FEES, chainId),
885-
{
886-
method: 'POST',
887-
body: JSON.stringify({
888-
txs: transactions,
893+
const data = await this.#trace(
894+
{ name: SmartTransactionsTraceName.GetFees },
895+
async () =>
896+
await this.#fetch(getAPIRequestURL(APIType.GET_FEES, chainId), {
897+
method: 'POST',
898+
body: JSON.stringify({
899+
txs: transactions,
900+
}),
889901
}),
890-
},
891902
);
892903
let approvalTxFees: IndividualTxFees | null;
893904
let tradeTxFees: IndividualTxFees | null;
@@ -943,15 +954,19 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
943954
const ethQuery = this.#getEthQuery({
944955
networkClientId: selectedNetworkClientId,
945956
});
946-
const data = await this.#fetch(
947-
getAPIRequestURL(APIType.SUBMIT_TRANSACTIONS, chainId),
948-
{
949-
method: 'POST',
950-
body: JSON.stringify({
951-
rawTxs: signedTransactions,
952-
rawCancelTxs: signedCanceledTransactions,
953-
}),
954-
},
957+
const data = await this.#trace(
958+
{ name: SmartTransactionsTraceName.SubmitTransactions },
959+
async () =>
960+
await this.#fetch(
961+
getAPIRequestURL(APIType.SUBMIT_TRANSACTIONS, chainId),
962+
{
963+
method: 'POST',
964+
body: JSON.stringify({
965+
rawTxs: signedTransactions,
966+
rawCancelTxs: signedCanceledTransactions,
967+
}),
968+
},
969+
),
955970
);
956971
const time = Date.now();
957972
let preTxBalance;
@@ -1089,10 +1104,14 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
10891104
} = {},
10901105
): Promise<void> {
10911106
const chainId = this.#getChainId({ networkClientId });
1092-
await this.#fetch(getAPIRequestURL(APIType.CANCEL, chainId), {
1093-
method: 'POST',
1094-
body: JSON.stringify({ uuid }),
1095-
});
1107+
await this.#trace(
1108+
{ name: SmartTransactionsTraceName.CancelTransaction },
1109+
async () =>
1110+
await this.#fetch(getAPIRequestURL(APIType.CANCEL, chainId), {
1111+
method: 'POST',
1112+
body: JSON.stringify({ uuid }),
1113+
}),
1114+
);
10961115
}
10971116

10981117
async fetchLiveness({
@@ -1103,8 +1122,10 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo
11031122
const chainId = this.#getChainId({ networkClientId });
11041123
let liveness = false;
11051124
try {
1106-
const response = await this.#fetch(
1107-
getAPIRequestURL(APIType.LIVENESS, chainId),
1125+
const response = await this.#trace(
1126+
{ name: SmartTransactionsTraceName.FetchLiveness },
1127+
async () =>
1128+
await this.#fetch(getAPIRequestURL(APIType.LIVENESS, chainId)),
11081129
);
11091130
liveness = Boolean(response.smartTransactions);
11101131
} catch (error) {

src/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,10 @@ export enum MetaMetricsEventCategory {
2323
Transactions = 'Transactions',
2424
Navigation = 'Navigation',
2525
}
26+
27+
export enum SmartTransactionsTraceName {
28+
GetFees = 'Smart Transactions: Get Fees',
29+
SubmitTransactions = 'Smart Transactions: Submit Transactions',
30+
CancelTransaction = 'Smart Transactions: Cancel Transaction',
31+
FetchLiveness = 'Smart Transactions: Fetch Liveness',
32+
}

0 commit comments

Comments
 (0)