diff --git a/coverage-targets.js b/coverage-targets.js
index e221e9466c31..6157a706d7be 100644
--- a/coverage-targets.js
+++ b/coverage-targets.js
@@ -6,10 +6,10 @@
// subset of files to check against these targets.
module.exports = {
global: {
- lines: 65,
+ lines: 65.5,
branches: 53.5,
- statements: 64,
- functions: 57.4,
+ statements: 64.75,
+ functions: 58,
},
transforms: {
branches: 100,
diff --git a/ui/components/institutional/compliance-settings/__snapshots__/compliance-settings.test.js.snap b/ui/components/institutional/compliance-settings/__snapshots__/compliance-settings.test.js.snap
index bddb1ef055ce..946a5a7f59f6 100644
--- a/ui/components/institutional/compliance-settings/__snapshots__/compliance-settings.test.js.snap
+++ b/ui/components/institutional/compliance-settings/__snapshots__/compliance-settings.test.js.snap
@@ -44,17 +44,17 @@ exports[`Compliance Settings shows start btn when Compliance its not activated 1
class="box institutional-feature__content box--flex-direction-row"
>
DeFi raises AML/CFT risk for institutions, given the decentralised pools and pseudonymous counterparties.
Codefi Compliance is the only product capable of running AML/CFT analysis on DeFi pools. This allows you to identify and avoid pools and counterparties that fail your risk setting.
Steps to enable AML/CFT Compliance:
diff --git a/ui/components/multichain/address-copy-button/address-copy-button.js b/ui/components/multichain/address-copy-button/address-copy-button.js
new file mode 100644
index 000000000000..f88fabdd8f40
--- /dev/null
+++ b/ui/components/multichain/address-copy-button/address-copy-button.js
@@ -0,0 +1,64 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import { ICON_NAMES, ButtonBase } from '../../component-library';
+import {
+ BackgroundColor,
+ TextVariant,
+ TextColor,
+ Size,
+ BorderRadius,
+ AlignItems,
+} from '../../../helpers/constants/design-system';
+import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
+import { shortenAddress } from '../../../helpers/utils/util';
+import Tooltip from '../../ui/tooltip/tooltip';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+
+export const AddressCopyButton = ({
+ address,
+ shorten = false,
+ wrap = false,
+}) => {
+ const displayAddress = shorten ? shortenAddress(address) : address;
+ const [copied, handleCopy] = useCopyToClipboard();
+ const t = useI18nContext();
+
+ return (
+
+ handleCopy(address)}
+ paddingRight={4}
+ paddingLeft={4}
+ size={Size.SM}
+ variant={TextVariant.bodyXs}
+ color={TextColor.primaryDefault}
+ endIconName={copied ? ICON_NAMES.COPY_SUCCESS : ICON_NAMES.COPY}
+ className={classnames('multichain-address-copy-button', {
+ 'multichain-address-copy-button__address--wrap': wrap,
+ })}
+ borderRadius={BorderRadius.pill}
+ alignItems={AlignItems.center}
+ data-testid="address-copy-button-text"
+ >
+ {displayAddress}
+
+
+ );
+};
+
+AddressCopyButton.propTypes = {
+ /**
+ * Address to be copied
+ */
+ address: PropTypes.string.isRequired,
+ /**
+ * Represents if the address should be shortened
+ */
+ shorten: PropTypes.bool,
+ /**
+ * Represents if the element should wrap to multiple lines
+ */
+ wrap: PropTypes.bool,
+};
diff --git a/ui/components/multichain/address-copy-button/address-copy-button.stories.js b/ui/components/multichain/address-copy-button/address-copy-button.stories.js
new file mode 100644
index 000000000000..869bfec255d7
--- /dev/null
+++ b/ui/components/multichain/address-copy-button/address-copy-button.stories.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { AddressCopyButton } from '.';
+
+export default {
+ title: 'Components/Multichain/AddressCopyButton',
+ component: AddressCopyButton,
+ argTypes: {
+ address: {
+ control: 'text',
+ },
+ shorten: {
+ control: 'boolean',
+ },
+ wrap: {
+ control: 'boolean',
+ },
+ },
+ args: {
+ address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+ },
+};
+
+export const DefaultStory = (args) => ;
+DefaultStory.storyName = 'Default';
+
+export const ShortenedStory = (args) => ;
+ShortenedStory.storyName = 'Shortened';
+ShortenedStory.args = { shorten: true };
+
+export const WrappedStory = (args) => (
+
+);
+WrappedStory.storyName = 'Wrapped';
+WrappedStory.args = { wrap: true };
diff --git a/ui/components/multichain/address-copy-button/address-copy-button.test.js b/ui/components/multichain/address-copy-button/address-copy-button.test.js
new file mode 100644
index 000000000000..a885fe45dff9
--- /dev/null
+++ b/ui/components/multichain/address-copy-button/address-copy-button.test.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import { fireEvent, render } from '@testing-library/react';
+import { AddressCopyButton } from '.';
+
+const SAMPLE_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc';
+
+describe('AccountListItem', () => {
+ it('renders the full address by default', () => {
+ render();
+ expect(
+ document.querySelector('[data-testid="address-copy-button-text"]')
+ .textContent,
+ ).toStrictEqual(SAMPLE_ADDRESS);
+ });
+
+ it('renders a shortened address when it should', () => {
+ render();
+ expect(
+ document.querySelector('[data-testid="address-copy-button-text"]')
+ .textContent,
+ ).toStrictEqual('0x0dc...e7bc');
+ });
+
+ it('changes icon when clicked', () => {
+ render();
+ fireEvent.click(document.querySelector('button'));
+ expect(document.querySelector('.mm-icon').style.maskImage).toContain(
+ 'copy-success.svg',
+ );
+ });
+});
diff --git a/ui/components/multichain/address-copy-button/index.js b/ui/components/multichain/address-copy-button/index.js
new file mode 100644
index 000000000000..8b93ba9b9378
--- /dev/null
+++ b/ui/components/multichain/address-copy-button/index.js
@@ -0,0 +1 @@
+export { AddressCopyButton } from './address-copy-button';
diff --git a/ui/components/multichain/address-copy-button/index.scss b/ui/components/multichain/address-copy-button/index.scss
new file mode 100644
index 000000000000..12226fec171a
--- /dev/null
+++ b/ui/components/multichain/address-copy-button/index.scss
@@ -0,0 +1,7 @@
+.multichain-address-copy-button {
+ &__address--wrap {
+ word-break: break-word;
+ min-height: 32px;
+ height: auto;
+ }
+}
diff --git a/ui/components/multichain/index.js b/ui/components/multichain/index.js
index 29562dc8373a..edf6785b94cc 100644
--- a/ui/components/multichain/index.js
+++ b/ui/components/multichain/index.js
@@ -5,3 +5,4 @@ export { DetectedTokensBanner } from './detected-token-banner';
export { GlobalMenu } from './global-menu';
export { MultichainImportTokenLink } from './multichain-import-token-link';
export { MultichainTokenListItem } from './multichain-token-list-item';
+export { AddressCopyButton } from './address-copy-button';
diff --git a/ui/components/multichain/multichain-components.scss b/ui/components/multichain/multichain-components.scss
index e8ee40ffa49e..8fe555f0ba21 100644
--- a/ui/components/multichain/multichain-components.scss
+++ b/ui/components/multichain/multichain-components.scss
@@ -4,6 +4,7 @@
* This will help improve specificity and reduce the chance of
* unintended overrides.
**/
+@import 'address-copy-button/index';
@import 'account-list-item/index';
@import 'account-list-menu/index';
@import 'multichain-token-list-item/multichain-token-list-item';
diff --git a/ui/components/ui/qr-code/qr-code.js b/ui/components/ui/qr-code/qr-code.js
index 758620092839..995424c5f553 100644
--- a/ui/components/ui/qr-code/qr-code.js
+++ b/ui/components/ui/qr-code/qr-code.js
@@ -8,6 +8,8 @@ import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils
import Tooltip from '../tooltip';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { Icon, ICON_NAMES, ICON_SIZES } from '../../component-library';
+import { AddressCopyButton } from '../../multichain/address-copy-button';
+import Box from '../box/box';
export default connect(mapStateToProps)(QrCodeView);
@@ -56,25 +58,31 @@ function QrCodeView(props) {
__html: qrImage.createTableTag(4),
}}
/>
-
- {
- handleCopy(toChecksumHexAddress(data));
- }}
+ {process.env.MULTICHAIN ? (
+
+
+
+ ) : (
+
- {toChecksumHexAddress(data)}
-
-
-
+ {
+ handleCopy(toChecksumHexAddress(data));
+ }}
+ >
+
{toChecksumHexAddress(data)}
+
+
+
+ )}
);
}