diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 328441d1e..39d8216b7 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -3504,6 +3504,18 @@
"restoreUserDataDescription": {
"message": "You can restore user settings containing preferences and account addresses from a previously backed up JSON file."
},
+ "resultPageError": {
+ "message": "Error"
+ },
+ "resultPageErrorDefaultMessage": {
+ "message": "The operation failed."
+ },
+ "resultPageSuccess": {
+ "message": "Success"
+ },
+ "resultPageSuccessDefaultMessage": {
+ "message": "The operation completed successfully."
+ },
"retryTransaction": {
"message": "Retry transaction"
},
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 66326a010..018e39aff 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -3963,6 +3963,12 @@ export default class MetamaskController extends EventEmitter {
this.approvalController.setFlowLoadingText.bind(
this.approvalController,
),
+ showApprovalSuccess: this.approvalController.success.bind(
+ this.approvalController,
+ ),
+ showApprovalError: this.approvalController.error.bind(
+ this.approvalController,
+ ),
sendMetrics: this.metaMetricsController.trackEvent.bind(
this.metaMetricsController,
),
diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json
index f3373a420..24f626987 100644
--- a/lavamoat/browserify/beta/policy.json
+++ b/lavamoat/browserify/beta/policy.json
@@ -775,6 +775,9 @@
}
},
"@metamask/approval-controller": {
+ "globals": {
+ "console.info": true
+ },
"packages": {
"@metamask/approval-controller>nanoid": true,
"@metamask/base-controller": true,
diff --git a/lavamoat/browserify/desktop/policy.json b/lavamoat/browserify/desktop/policy.json
index e9b8572ae..1ff496ed2 100644
--- a/lavamoat/browserify/desktop/policy.json
+++ b/lavamoat/browserify/desktop/policy.json
@@ -775,6 +775,9 @@
}
},
"@metamask/approval-controller": {
+ "globals": {
+ "console.info": true
+ },
"packages": {
"@metamask/approval-controller>nanoid": true,
"@metamask/base-controller": true,
diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json
index e9b8572ae..1ff496ed2 100644
--- a/lavamoat/browserify/flask/policy.json
+++ b/lavamoat/browserify/flask/policy.json
@@ -775,6 +775,9 @@
}
},
"@metamask/approval-controller": {
+ "globals": {
+ "console.info": true
+ },
"packages": {
"@metamask/approval-controller>nanoid": true,
"@metamask/base-controller": true,
diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json
index f3373a420..24f626987 100644
--- a/lavamoat/browserify/main/policy.json
+++ b/lavamoat/browserify/main/policy.json
@@ -775,6 +775,9 @@
}
},
"@metamask/approval-controller": {
+ "globals": {
+ "console.info": true
+ },
"packages": {
"@metamask/approval-controller>nanoid": true,
"@metamask/base-controller": true,
diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json
index a097c6d55..46ea1ba04 100644
--- a/lavamoat/browserify/mmi/policy.json
+++ b/lavamoat/browserify/mmi/policy.json
@@ -996,6 +996,9 @@
}
},
"@metamask/approval-controller": {
+ "globals": {
+ "console.info": true
+ },
"packages": {
"@metamask/approval-controller>nanoid": true,
"@metamask/base-controller": true,
diff --git a/package.json b/package.json
index b4ec6bc10..88754b540 100644
--- a/package.json
+++ b/package.json
@@ -100,7 +100,7 @@
"resolutions": {
"@babel/core": "patch:@babel/core@npm%3A7.21.5#./.yarn/patches/@babel-core-npm-7.21.5-c72c337956.patch",
"@babel/runtime": "patch:@babel/runtime@npm%3A7.18.9#./.yarn/patches/@babel-runtime-npm-7.18.9-28ca6b5f61.patch",
- "@metamask/approval-controller": "^3.3.0",
+ "@metamask/approval-controller": "^3.4.0",
"@types/react": "^16.9.53",
"analytics-node/axios": "^0.21.2",
"ganache-core/lodash": "^4.17.21",
@@ -226,7 +226,7 @@
"@metamask-institutional/transaction-update": "^0.1.21",
"@metamask/address-book-controller": "^3.0.0",
"@metamask/announcement-controller": "^4.0.0",
- "@metamask/approval-controller": "^3.3.0",
+ "@metamask/approval-controller": "^3.4.0",
"@metamask/assets-controllers": "^9.2.0",
"@metamask/base-controller": "^3.0.0",
"@metamask/browser-passworder": "^4.1.0",
diff --git a/test/e2e/tests/switch-custom-network.spec.js b/test/e2e/tests/switch-custom-network.spec.js
index 9e0cf6857..67c3ad1fc 100644
--- a/test/e2e/tests/switch-custom-network.spec.js
+++ b/test/e2e/tests/switch-custom-network.spec.js
@@ -2,7 +2,7 @@ const { strict: assert } = require('assert');
const FixtureBuilder = require('../fixture-builder');
const { convertToHexValue, withFixtures, openDapp } = require('../helpers');
-describe('Swtich ethereum chain', function () {
+describe('Switch ethereum chain', function () {
const ganacheOptions = {
accounts: [
{
diff --git a/ui/components/app/metamask-template-renderer/safe-component-list.js b/ui/components/app/metamask-template-renderer/safe-component-list.js
index f8ae5650e..68101cfb3 100644
--- a/ui/components/app/metamask-template-renderer/safe-component-list.js
+++ b/ui/components/app/metamask-template-renderer/safe-component-list.js
@@ -12,6 +12,8 @@ import TextField from '../../ui/text-field';
import ConfirmationNetworkSwitch from '../../../pages/confirmation/components/confirmation-network-switch';
import UrlIcon from '../../ui/url-icon';
import Tooltip from '../../ui/tooltip/tooltip';
+import { AvatarIcon } from '../../component-library';
+import ActionableMessage from '../../ui/actionable-message/actionable-message';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { SnapDelineator } from '../snaps/snap-delineator';
import { Copyable } from '../snaps/copyable';
@@ -21,19 +23,21 @@ import { SnapUIMarkdown } from '../snaps/snap-ui-markdown';
export const safeComponentList = {
a: 'a',
+ ActionableMessage,
+ AvatarIcon,
b: 'b',
- i: 'i',
- p: 'p',
- div: 'div',
- span: 'span',
Box,
Button,
Chip,
ConfirmationNetworkSwitch,
DefinitionList,
+ div: 'div',
+ i: 'i',
MetaMaskTranslation,
NetworkDisplay,
+ p: 'p',
Popover,
+ span: 'span',
TextArea,
TextField,
Tooltip,
@@ -41,9 +45,9 @@ export const safeComponentList = {
Typography,
UrlIcon,
///: BEGIN:ONLY_INCLUDE_IN(snaps)
- SnapDelineator,
Copyable,
- Spinner,
+ SnapDelineator,
SnapUIMarkdown,
+ Spinner,
///: END:ONLY_INCLUDE_IN
};
diff --git a/ui/pages/confirmation/templates/__snapshots__/error.test.js.snap b/ui/pages/confirmation/templates/__snapshots__/error.test.js.snap
new file mode 100644
index 000000000..30d96b667
--- /dev/null
+++ b/ui/pages/confirmation/templates/__snapshots__/error.test.js.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`error template matches the snapshot 1`] = `
+
+
+
+
+
+ Error mock
+
+
+
+
+
+
+ Error
+
+
+
+
+
+ The operation failed.
+
+
+
+
+
+
+
+
+
+`;
diff --git a/ui/pages/confirmation/templates/__snapshots__/success.test.js.snap b/ui/pages/confirmation/templates/__snapshots__/success.test.js.snap
new file mode 100644
index 000000000..2c473b06d
--- /dev/null
+++ b/ui/pages/confirmation/templates/__snapshots__/success.test.js.snap
@@ -0,0 +1,60 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`success template matches the snapshot 1`] = `
+
+
+
+
+
+ Success mock
+
+
+
+
+
+
+ Success
+
+
+ Success message
+
+
+
+
+
+
+
+`;
diff --git a/ui/pages/confirmation/templates/error.js b/ui/pages/confirmation/templates/error.js
new file mode 100644
index 000000000..d0c41acfe
--- /dev/null
+++ b/ui/pages/confirmation/templates/error.js
@@ -0,0 +1,94 @@
+import { IconName, IconSize } from '../../../components/component-library';
+import {
+ FontWeight,
+ TextAlign,
+ BlockSize,
+ AlignItems,
+ FlexDirection,
+ JustifyContent,
+ TypographyVariant,
+ IconColor,
+ BackgroundColor,
+} from '../../../helpers/constants/design-system';
+import { processError } from '../util';
+
+function getValues(pendingApproval, t, actions, _history) {
+ return {
+ content: [
+ {
+ key: 'header',
+ element: 'Box',
+ props: {
+ flexDirection: FlexDirection.Column,
+ alignItems: AlignItems.center,
+ height: BlockSize.Full,
+ padding: 4,
+ },
+ children: [
+ ...(pendingApproval.requestData.header || []),
+ {
+ key: 'content',
+ element: 'Box',
+ props: {
+ flexDirection: FlexDirection.Column,
+ alignItems: AlignItems.center,
+ justifyContent: JustifyContent.center,
+ height: BlockSize.Full,
+ paddingTop: 2,
+ paddingBottom: 2,
+ },
+ children: [
+ {
+ key: 'icon',
+ element: 'AvatarIcon',
+ props: {
+ iconName: IconName.Warning,
+ size: IconSize.Xl,
+ iconProps: { size: IconSize.Xl },
+ color: IconColor.errorDefault,
+ backgroundColor: BackgroundColor.errorMuted,
+ },
+ children: 'Icon',
+ },
+ {
+ key: 'heading',
+ element: 'Typography',
+ props: {
+ variant: TypographyVariant.H3,
+ fontWeight: FontWeight.Bold,
+ paddingBottom: 2,
+ },
+ children: t('resultPageError'),
+ },
+ {
+ key: 'message',
+ element: 'Box',
+ props: {
+ alignItems: AlignItems.center,
+ textAlign: TextAlign.Center,
+ },
+ children: processError(
+ pendingApproval.requestData.error,
+ t('resultPageErrorDefaultMessage'),
+ ),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ submitText: t('ok'),
+ onSubmit: () =>
+ actions.resolvePendingApproval(
+ pendingApproval.id,
+ pendingApproval.requestData,
+ ),
+ networkDisplay: false,
+ };
+}
+
+const error = {
+ getValues,
+};
+
+export default error;
diff --git a/ui/pages/confirmation/templates/error.test.js b/ui/pages/confirmation/templates/error.test.js
new file mode 100644
index 000000000..3c726eb0b
--- /dev/null
+++ b/ui/pages/confirmation/templates/error.test.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { waitFor } from '@testing-library/react';
+
+import { ApprovalType } from '@metamask/controller-utils';
+import { renderWithProvider } from '../../../../test/lib/render-helpers';
+import Confirmation from '../confirmation';
+
+jest.mock('../../../../shared/lib/fetch-with-cache');
+
+const middleware = [thunk];
+const mockApprovalId = 1;
+const mockApproval = {
+ id: mockApprovalId,
+ origin: 'https://test-dapp.metamask.io',
+ requestData: {
+ header: [
+ {
+ key: 'headerText',
+ element: 'Typography',
+ children: 'Error mock',
+ props: {
+ variant: 'h2',
+ class: 'header-mock-class',
+ },
+ },
+ ],
+ message: 'Error message',
+ },
+};
+
+const mockBaseStore = {
+ metamask: {
+ pendingApprovals: {
+ [mockApprovalId]: mockApproval,
+ },
+ approvalFlows: [],
+ subjectMetadata: {},
+ },
+};
+
+describe('error template', () => {
+ it('matches the snapshot', async () => {
+ const testStore = {
+ metamask: {
+ ...mockBaseStore.metamask,
+ pendingApprovals: {
+ [mockApprovalId]: {
+ ...mockApproval,
+ type: ApprovalType.ResultError,
+ },
+ },
+ },
+ };
+ const store = configureMockStore(middleware)(testStore);
+ const { getByText, container } = renderWithProvider(
+ ,
+ store,
+ );
+ await waitFor(() => {
+ expect(getByText('Error mock')).toBeInTheDocument();
+ expect(container).toMatchSnapshot();
+ });
+ });
+});
diff --git a/ui/pages/confirmation/templates/index.js b/ui/pages/confirmation/templates/index.js
index 36f1e8e05..5839ae47c 100644
--- a/ui/pages/confirmation/templates/index.js
+++ b/ui/pages/confirmation/templates/index.js
@@ -8,6 +8,8 @@ import {
} from '../../../store/actions';
import addEthereumChain from './add-ethereum-chain';
import switchEthereumChain from './switch-ethereum-chain';
+import success from './success';
+import error from './error';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import snapAlert from './snaps/snap-alert/snap-alert';
import snapConfirmation from './snaps/snap-confirmation/snap-confirmation';
@@ -17,6 +19,9 @@ import snapPrompt from './snaps/snap-prompt/snap-prompt';
const APPROVAL_TEMPLATES = {
[ApprovalType.AddEthereumChain]: addEthereumChain,
[ApprovalType.SwitchEthereumChain]: switchEthereumChain,
+ // Use ApprovalType from utils controller
+ [ApprovalType.ResultSuccess]: success,
+ [ApprovalType.ResultError]: error,
///: BEGIN:ONLY_INCLUDE_IN(snaps)
[ApprovalType.SnapDialogAlert]: snapAlert,
[ApprovalType.SnapDialogConfirmation]: snapConfirmation,
diff --git a/ui/pages/confirmation/templates/success.js b/ui/pages/confirmation/templates/success.js
new file mode 100644
index 000000000..3d324642b
--- /dev/null
+++ b/ui/pages/confirmation/templates/success.js
@@ -0,0 +1,94 @@
+import { IconName, IconSize } from '../../../components/component-library';
+import {
+ FontWeight,
+ BlockSize,
+ AlignItems,
+ FlexDirection,
+ JustifyContent,
+ TypographyVariant,
+ TextAlign,
+ IconColor,
+ BackgroundColor,
+} from '../../../helpers/constants/design-system';
+import { processString } from '../util';
+
+function getValues(pendingApproval, t, actions, _history) {
+ return {
+ content: [
+ {
+ key: 'header',
+ element: 'Box',
+ props: {
+ flexDirection: FlexDirection.Column,
+ alignItems: AlignItems.center,
+ height: BlockSize.Full,
+ padding: 4,
+ },
+ children: [
+ ...(pendingApproval.requestData.header || []),
+ {
+ key: 'content',
+ element: 'Box',
+ props: {
+ flexDirection: FlexDirection.Column,
+ alignItems: AlignItems.center,
+ justifyContent: JustifyContent.center,
+ height: BlockSize.Full,
+ paddingTop: 2,
+ paddingBottom: 2,
+ },
+ children: [
+ {
+ key: 'icon',
+ element: 'AvatarIcon',
+ props: {
+ iconName: IconName.Confirmation,
+ size: IconSize.Xl,
+ iconProps: { size: IconSize.Xl },
+ color: IconColor.successDefault,
+ backgroundColor: BackgroundColor.successMuted,
+ },
+ children: 'Icon',
+ },
+ {
+ key: 'heading',
+ element: 'Typography',
+ props: {
+ variant: TypographyVariant.H3,
+ fontWeight: FontWeight.Bold,
+ paddingBottom: 2,
+ },
+ children: t('resultPageSuccess'),
+ },
+ {
+ key: 'message',
+ element: 'Box',
+ props: {
+ alignItems: AlignItems.center,
+ textAlign: TextAlign.Center,
+ },
+ children: processString(
+ pendingApproval.requestData.message,
+ t('resultPageSuccessDefaultMessage'),
+ ),
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ submitText: t('ok'),
+ onSubmit: () =>
+ actions.resolvePendingApproval(
+ pendingApproval.id,
+ pendingApproval.requestData,
+ ),
+ networkDisplay: false,
+ };
+}
+
+const success = {
+ getValues,
+};
+
+export default success;
diff --git a/ui/pages/confirmation/templates/success.test.js b/ui/pages/confirmation/templates/success.test.js
new file mode 100644
index 000000000..b3c22d33e
--- /dev/null
+++ b/ui/pages/confirmation/templates/success.test.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { waitFor } from '@testing-library/react';
+
+import { ApprovalType } from '@metamask/controller-utils';
+import { renderWithProvider } from '../../../../test/lib/render-helpers';
+import Confirmation from '../confirmation';
+
+jest.mock('../../../../shared/lib/fetch-with-cache');
+
+const middleware = [thunk];
+const mockApprovalId = 1;
+const mockApproval = {
+ id: mockApprovalId,
+ origin: 'https://test-dapp.metamask.io',
+ requestData: {
+ header: [
+ {
+ key: 'headerText',
+ element: 'Typography',
+ children: 'Success mock',
+ props: {
+ variant: 'h2',
+ class: 'header-mock-class',
+ },
+ },
+ ],
+ message: 'Success message',
+ },
+};
+
+const mockBaseStore = {
+ metamask: {
+ pendingApprovals: {
+ [mockApprovalId]: mockApproval,
+ },
+ approvalFlows: [],
+ subjectMetadata: {},
+ },
+};
+
+describe('success template', () => {
+ it('matches the snapshot', async () => {
+ const testStore = {
+ metamask: {
+ ...mockBaseStore.metamask,
+ pendingApprovals: {
+ [mockApprovalId]: {
+ ...mockApproval,
+ type: ApprovalType.ResultSuccess,
+ },
+ },
+ },
+ };
+ const store = configureMockStore(middleware)(testStore);
+ const { getByText, container } = renderWithProvider(
+ ,
+ store,
+ );
+ await waitFor(() => {
+ expect(getByText('Success mock')).toBeInTheDocument();
+ expect(container).toMatchSnapshot();
+ });
+ });
+});
diff --git a/ui/pages/confirmation/util.test.ts b/ui/pages/confirmation/util.test.ts
new file mode 100644
index 000000000..7a0e85efa
--- /dev/null
+++ b/ui/pages/confirmation/util.test.ts
@@ -0,0 +1,60 @@
+import { ResultComponent } from '@metamask/approval-controller';
+import { processError, processString } from './util';
+
+const FALLBACK_MESSAGE = 'Fallback Message';
+const mockResultComponent: ResultComponent = {
+ key: 'mock-key',
+ name: 'mock-component',
+ properties: { message: 'mock1', message2: 'mock2' },
+ children: 'Mock child',
+};
+
+const expectedTemplateRendererComponent = {
+ key: 'mock-key',
+ props: {
+ message: 'mock1',
+ message2: 'mock2',
+ },
+ children: 'Mock child',
+ element: 'mock-component',
+};
+
+describe('processError', () => {
+ it('returns TemplateRendererComponent when input is not defined', () => {
+ const result = processError(undefined, FALLBACK_MESSAGE);
+ expect(result).toEqual({
+ key: 'error',
+ element: 'ActionableMessage',
+ props: { type: 'danger', message: FALLBACK_MESSAGE },
+ });
+ });
+
+ it('returns TemplateRendererComponent when input is a string', () => {
+ const result = processError('Error Message', FALLBACK_MESSAGE);
+ expect(result).toEqual({
+ key: 'error',
+ element: 'ActionableMessage',
+ props: { type: 'danger', message: 'Error Message' },
+ });
+ });
+ it('returns TemplateRendererComponent when input is a ResultComponent', () => {
+ const result = processError(mockResultComponent, FALLBACK_MESSAGE);
+ expect(result).toEqual(expectedTemplateRendererComponent);
+ });
+});
+
+describe('processString', () => {
+ it('returns string when input is not defined', () => {
+ const result = processString(undefined, FALLBACK_MESSAGE);
+ expect(result[0]).toEqual(FALLBACK_MESSAGE);
+ });
+
+ it('returns TemplateRendererComponent when input is a string', () => {
+ const result = processString('Hello', FALLBACK_MESSAGE);
+ expect(result).toEqual(['Hello']);
+ });
+ it('returns TemplateRendererComponent when input is a ResultComponent', () => {
+ const result = processString(mockResultComponent, FALLBACK_MESSAGE);
+ expect(result).toEqual(expectedTemplateRendererComponent);
+ });
+});
diff --git a/ui/pages/confirmation/util.ts b/ui/pages/confirmation/util.ts
new file mode 100644
index 000000000..ca75ee665
--- /dev/null
+++ b/ui/pages/confirmation/util.ts
@@ -0,0 +1,150 @@
+import { ResultComponent } from '@metamask/approval-controller';
+
+type TemplateRendererComponent = {
+ key: string;
+ element: string;
+ props?: Record;
+ children?:
+ | string
+ | TemplateRendererComponent
+ | (string | TemplateRendererComponent)[];
+};
+
+/**
+ * Processes an error message or ResultComponent and returns a TemplateRendererComponent
+ * or an array of strings | TemplateRendererComponents.
+ *
+ * @param input - The message or component to process.
+ * @param fallback - The fallback message to use when the input is not valid.
+ * @returns The processed error component.
+ */
+export function processError(
+ input: undefined | string | ResultComponent | ResultComponent[],
+ fallback: string,
+): TemplateRendererComponent | (string | TemplateRendererComponent)[] {
+ const currentInput = convertResultComponents(input) || fallback;
+
+ if (typeof currentInput !== 'string') {
+ return currentInput;
+ }
+
+ return {
+ key: 'error',
+ element: 'ActionableMessage',
+ props: { type: 'danger', message: currentInput },
+ };
+}
+
+/**
+ * Processes a string or ResultComponent and returns a string or TemplateRendererComponent
+ * or an array of strings | TemplateRendererComponents.
+ *
+ * @param input - The message or component to process.
+ * @param fallback - The fallback string to use when the input is not valid.
+ * @returns The processed message.
+ */
+export function processString(
+ input: undefined | string | ResultComponent | ResultComponent[],
+ fallback: string,
+): string | TemplateRendererComponent | (string | TemplateRendererComponent)[] {
+ const currentInput = convertResultComponents(input) || fallback;
+
+ if (typeof currentInput !== 'string') {
+ return currentInput;
+ }
+
+ return applyBold(currentInput);
+}
+
+/**
+ * Applies bold formatting to the message.
+ *
+ * @param message - The input message to apply bold formatting to.
+ * @returns The formatted message.
+ */
+function applyBold(message: string): (string | TemplateRendererComponent)[] {
+ const boldPattern = /\*\*(.+?)\*\*/gu;
+
+ return findMarkdown(message, boldPattern, (formattedText, index) => ({
+ key: `bold-${index}`,
+ element: 'b',
+ children: formattedText,
+ }));
+}
+
+/**
+ * Finds and formats markdown elements in the given text.
+ *
+ * @param text - The input text to search for markdown elements.
+ * @param pattern - The pattern to match the markdown elements.
+ * @param getElement - The callback function to create the formatted elements.
+ * @returns The array of formatted elements.
+ */
+function findMarkdown(
+ text: string,
+ pattern: RegExp,
+ getElement: (
+ formattedText: string,
+ index: number,
+ ) => TemplateRendererComponent,
+): (string | TemplateRendererComponent)[] {
+ let position = 0;
+ let index = 0;
+
+ const matches = Array.from(text.matchAll(pattern));
+ const elements = [];
+
+ for (const match of matches) {
+ const rawText = text.substring(position, match.index);
+
+ if (rawText.length) {
+ elements.push(rawText);
+ }
+
+ const formattedText = match[1];
+ const formattedElement = getElement(formattedText, index);
+
+ elements.push(formattedElement);
+
+ position = (match.index as number) + match[0].length;
+ index += 1;
+ }
+
+ const finalRawText = text.substring(position);
+
+ if (finalRawText.length) {
+ elements.push(finalRawText);
+ }
+
+ return elements;
+}
+
+function convertResultComponents(
+ input: undefined | string | ResultComponent | (string | ResultComponent)[],
+):
+ | undefined
+ | string
+ | TemplateRendererComponent
+ | (string | TemplateRendererComponent)[] {
+ if (input === undefined) {
+ return undefined;
+ }
+
+ if (typeof input === 'string') {
+ return input;
+ }
+
+ if (Array.isArray(input)) {
+ return input.map(convertResultComponents) as (
+ | string
+ | TemplateRendererComponent
+ )[];
+ }
+
+ return {
+ key: input.key,
+ element: input.name,
+ props: input.properties,
+ children: convertResultComponents(input.children),
+ };
+}
diff --git a/yarn.lock b/yarn.lock
index 6ecd9d1d5..70031d4d3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3857,16 +3857,16 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/approval-controller@npm:^3.3.0":
- version: 3.3.0
- resolution: "@metamask/approval-controller@npm:3.3.0"
+"@metamask/approval-controller@npm:^3.4.0":
+ version: 3.4.0
+ resolution: "@metamask/approval-controller@npm:3.4.0"
dependencies:
"@metamask/base-controller": ^3.0.0
"@metamask/utils": ^5.0.2
eth-rpc-errors: ^4.0.2
immer: ^9.0.6
nanoid: ^3.1.31
- checksum: 1fa6111a897d6f4aa369fd1a669fb5e16558277019f9d6c61449aea0d7b2672a62c189e5b3d9e84ab6e4d5826932c78d2bdb0f05aafb184a4ff07903c46abf2c
+ checksum: 153136800fbd8cd50e2d6012740526c55e74e3f8e5aa99a05d5788e8bae04ede622608a1adf67dcf0986162e9e7d1d11b6f6f0df438904b65db7435b881c2e3f
languageName: node
linkType: hard
@@ -24596,7 +24596,7 @@ __metadata:
"@metamask-institutional/transaction-update": ^0.1.21
"@metamask/address-book-controller": ^3.0.0
"@metamask/announcement-controller": ^4.0.0
- "@metamask/approval-controller": ^3.3.0
+ "@metamask/approval-controller": ^3.4.0
"@metamask/assets-controllers": ^9.2.0
"@metamask/auto-changelog": ^2.1.0
"@metamask/base-controller": ^3.0.0