Repository: LedgerHQ/ledger-live
Pull request #9671: feat: Add Copy/Paste input to EIP712 Signature
Author: login: RobinVncnt
Labels: name: desktop
description: Has changes in LLD
Created: 2025-03-26T10:24:28Z | Merged: 2025-03-27T13:38:47Z

feat: Add Copy/Paste input to EIP712 Signature


<!--
Thank you for your contribution! 👍
Please make sure to read CONTRIBUTING.md if you have not already. Pull Requests that do not comply with the rules will be arbitrarily closed.
-->

### ✅ Checklist

<!-- Pull Requests must pass the CI and be code reviewed. Set as Draft if the PR is not ready. -->

- [x] `npx changeset` was attached.
- [ ] **Covered by automatic tests.** <!-- if not, please explain. (Feature must be tested / Bug fix must bring non-regression) -->
- [ ] **Impact of the changes:** <!-- Please take some time to list the impact & what specific areas Quality Assurance (QA) should focus on -->
  - ...

### 📝 Description

feat: Add Copy/Paste input to EIP712 Signature

### ❓ Context

- **JIRA or GitHub link**: [LIVE-5584]<!-- Attach the relevant ticket number if applicable. (e.g., [JIRA-123] for Jira or #123 for a Github issue) -->



https://github.com/user-attachments/assets/1b00332a-1c02-46d5-87fd-6feb32c1baf2




---

### 🧐 Checklist for the PR Reviewers

<!-- Please do not edit this if you are the PR author -->

- **The code aligns with the requirements** described in the linked JIRA or GitHub issue.
- **The PR description clearly documents the changes** made and explains any technical trade-offs or design decisions.
- **There are no undocumented trade-offs**, technical debt, or maintainability issues.
- **The PR has been tested** thoroughly, and any potential edge cases have been considered and handled.
- **Any new dependencies** have been justified and documented.
- **Performance** considerations have been taken into account. (changes have been profiled or benchmarked if necessary)


[LIVE-5584]: https://ledgerhq.atlassian.net/browse/LIVE-5584?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/.changeset/angry-roses-allow.md b/.changeset/angry-roses-allow.md
new file mode 100644
index 00000000000..8a3d23a7bda
--- /dev/null
+++ b/.changeset/angry-roses-allow.md
@@ -0,0 +1,5 @@
+---
+"ledger-live-desktop": minor
+---
+
+feat: LLD - Add Copy/Paste input to EIP712 Signature
diff --git a/apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx
new file mode 100644
index 00000000000..aa4150bc32e
--- /dev/null
+++ b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx
@@ -0,0 +1,66 @@
+import React from "react";
+import { render, screen, waitFor } from "tests/testUtils";
+import CopyButton from "./CopyButton";
+
+jest.mock("i18next", () => {
+  return {
+    __esModule: true,
+    t: jest.fn(key => {
+      if (key === "common.copy") return "Copy";
+      if (key === "common.copied") return "Copied";
+      return key;
+    }),
+    default: {
+      use: jest.fn().mockReturnThis(),
+      init: jest.fn(),
+    },
+  };
+});
+
+describe("CopyButton", () => {
+  const testText = "Text to copy";
+  beforeAll(() => {
+    Object.assign(navigator, {
+      clipboard: {
+        writeText: jest.fn().mockResolvedValue(undefined),
+      },
+    });
+  });
+
+  afterAll(() => {
+    jest.restoreAllMocks();
+  });
+
+  it("renders correctly with copy icon and text", () => {
+    render(<CopyButton text={testText} />);
+
+    expect(screen.getByTestId("copy-wrapper")).toBeInTheDocument();
+    expect(screen.getByTestId("copy-wrapper").children.length).toEqual(2);
+    expect(screen.getByRole("button", { name: "Copy" })).toBeInTheDocument();
+  });
+
+  it("copies text to clipboard when clicked", async () => {
+    jest.spyOn(navigator.clipboard, "writeText").mockImplementation(() => Promise.resolve());
+
+    const { user } = render(<CopyButton text={testText} />);
+    const button = screen.getByRole("button", { name: "Copy" });
+
+    await user.click(button);
+    expect(button).toBeVisible();
+
+    await waitFor(() => {
+      expect(navigator.clipboard.writeText).toHaveBeenCalledWith(testText);
+    });
+  });
+
+  it("shows success state after copying", async () => {
+    const { user } = render(<CopyButton text={testText} />);
+    const button = screen.getByRole("button", { name: "Copy" });
+
+    await user.click(button);
+
+    expect(screen.getByTestId("copy-wrapper")).toBeInTheDocument();
+    expect(screen.getByTestId("copy-wrapper").children.length).toEqual(2);
+    expect(screen.getByRole("button", { name: "Copied" })).toBeInTheDocument();
+  });
+});
diff --git a/apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.tsx b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.tsx
new file mode 100644
index 00000000000..8c05148a58a
--- /dev/null
+++ b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.tsx
@@ -0,0 +1,59 @@
+import React, { useState, useCallback } from "react";
+import styled from "styled-components";
+import { Icons, Button, Flex } from "@ledgerhq/react-ui";
+import { t } from "i18next";
+
+type Props = {
+  readonly text: string;
+};
+
+export default function CopyButton({ text }: Props) {
+  const [copied, setCopied] = useState(false);
+
+  const handleCopy = useCallback(() => {
+    navigator.clipboard.writeText(text).then(() => {
+      setCopied(true);
+      setTimeout(() => setCopied(false), 1200);
+    });
+  }, [text]);
+
+  return (
+    <ButtonContainer variant="shade" onClick={handleCopy}>
+      <ContentWrapper data-testid="copy-wrapper">
+        {copied ? (
+          <>
+            <Icons.Check size={"S"} color="success.c50" />
+            <StyledText copied={true}>{t("common.copied")}</StyledText>
+          </>
+        ) : (
+          <>
+            <Icons.Copy size={"S"} color="neutral.c90" />
+            <StyledText copied={false}>{t("common.copy")}</StyledText>
+          </>
+        )}
+      </ContentWrapper>
+    </ButtonContainer>
+  );
+}
+
+const ContentWrapper = styled(Flex)`
+  align-items: center;
+  justify-content: center;
+`;
+
+const StyledText = styled.span<{ copied: boolean }>`
+  font-size: 14px;
+  color: ${({ theme }) => theme.colors.neutral.c90};
+  padding-left: 8px;
+`;
+
+const ButtonContainer = styled(Button)`
+  position: sticky;
+  bottom: -15px;
+  left: 50%;
+  transform: translateX(-50%);
+  border: none;
+  display: block;
+  height: 36px;
+  background-color: ${({ theme }) => theme.colors.neutral.c30};
+`;
diff --git a/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx
index bcd0411a349..d169d0fe19d 100644
--- a/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx
+++ b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx
@@ -18,6 +18,7 @@ import FormattedVal from "~/renderer/components/FormattedVal";
 import { getTokenUnit } from "~/renderer/utils";
 import { Account } from "@ledgerhq/types-live";
 import { Unit } from "@ledgerhq/types-cryptoassets";
+import CopyButton from "../Components/CopyButton";
 
 const Circle = styled.div`
   height: 32px;
@@ -64,6 +65,9 @@ const AdvancedMessageArea = styled.pre`
   word-wrap: break-word;
   white-space: pre-wrap;
   padding: 10px;
+  position: relative;
+  min-height: 60px;
+  word-break: break-word;
 `;
 
 const MessageProperty = memo(
@@ -211,6 +215,7 @@ export default function StepSummary({ account, message: messageData }: StepProps
                 {typeof messageData.message === "string"
                   ? `"${messageData.message}"`
                   : JSON.stringify(messageData.message, null, 2)}
+                <CopyButton text={messageData.message.toString()} />
               </AdvancedMessageArea>
             ) : null}
           </Box>


Reviews:
user: login: mcayuelas-ledger
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T10:57:02Z
user: login: mcayuelas-ledger
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T15:38:33Z
user: login: mcayuelas-ledger
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T11:04:57Z
user: login: mcayuelas-ledger
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T13:15:46Z

Review comments:
path: apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/CopyButton.tsx
user: login: mcayuelas-ledger
body: you can create a folder "components" in apps/ledger-live-desktop/src/renderer/modals/SignMessage
and add it 

also can try make a unit test using Jest called CopyButton.test.tsx? :) 


created_at: 2025-03-26T10:57:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+import React from "react";
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
path: apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx
user: login: mcayuelas-ledger
body: You can use 
```suggestion
import { render, screen, fireEvent, waitFor } from "tests/testUtils";
```

You will not need to wrap and you can remove 

`import { DefaultTheme, ThemeProvider } from "styled-components";`
created_at: 2025-03-26T15:34:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+import React from "react";
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
+import { DefaultTheme, ThemeProvider } from "styled-components";
+import CopyButton from "./CopyButton";
+
+jest.mock("i18next", () => ({
+  t: jest.fn(key => {
+    if (key === "common.copy") return "Copy";
+    if (key === "common.copied") return "Copied";
+    return key;
+  }),
+}));
+
+const mockTheme = {
+  colors: {
+    neutral: {
+      c30: "#F4F4F4",
+      c90: "#4D4D4D",
+    },
+    success: {
+      c50: "#5BA54C",
+    },
+  },
+} as DefaultTheme;
+
+jest.mock("@ledgerhq/react-ui", () => ({
+  __esModule: true,
+  Button: ({ children, onClick }: never) => (
+    <button onClick={onClick} data-testid="ledger-button">
+      {children}
+    </button>
+  ),
+  Icons: {
+    Check: () => <div data-testid="check-icon" />,
+    Copy: () => <div data-testid="copy-icon" />,
+  },
+}));
+
+beforeAll(() => {
+  Object.assign(navigator, {
+    clipboard: {
+      writeText: jest.fn().mockResolvedValue(undefined),
+    },
+  });
+});
+
+afterAll(() => {
+  jest.restoreAllMocks();
+});
+
+describe("CopyButton", () => {
+  const testText = "Text to copy";
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  const renderWithTheme = (component: React.ReactNode) => {
+    return render(<ThemeProvider theme={mockTheme}>{component}</ThemeProvider>, {
+      wrapper: ({ children }) => children,
+    });
+  };
path: apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx
user: login: mcayuelas-ledger
body: it will be removed
created_at: 2025-03-26T15:35:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+import React from "react";
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
+import { DefaultTheme, ThemeProvider } from "styled-components";
+import CopyButton from "./CopyButton";
+
+jest.mock("i18next", () => ({
+  t: jest.fn(key => {
+    if (key === "common.copy") return "Copy";
+    if (key === "common.copied") return "Copied";
+    return key;
+  }),
+}));
+
+const mockTheme = {
+  colors: {
+    neutral: {
+      c30: "#F4F4F4",
+      c90: "#4D4D4D",
+    },
+    success: {
+      c50: "#5BA54C",
+    },
+  },
+} as DefaultTheme;
+
+jest.mock("@ledgerhq/react-ui", () => ({
+  __esModule: true,
+  Button: ({ children, onClick }: never) => (
+    <button onClick={onClick} data-testid="ledger-button">
+      {children}
+    </button>
+  ),
+  Icons: {
+    Check: () => <div data-testid="check-icon" />,
+    Copy: () => <div data-testid="copy-icon" />,
+  },
+}));
+
+beforeAll(() => {
+  Object.assign(navigator, {
+    clipboard: {
+      writeText: jest.fn().mockResolvedValue(undefined),
+    },
+  });
+});
+
+afterAll(() => {
+  jest.restoreAllMocks();
+});
+
+describe("CopyButton", () => {
+  const testText = "Text to copy";
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  const renderWithTheme = (component: React.ReactNode) => {
+    return render(<ThemeProvider theme={mockTheme}>{component}</ThemeProvider>, {
+      wrapper: ({ children }) => children,
+    });
+  };
+
+  it("renders correctly with copy icon and text", async () => {
+    const { unmount } = renderWithTheme(<CopyButton text={testText} />);
+
+    expect(screen.getByTestId("copy-icon")).toBeInTheDocument();
+    expect(screen.getByText("Copy")).toBeInTheDocument();
+
+    unmount();
path: apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx
user: login: mcayuelas-ledger
body: why unmount()?
created_at: 2025-03-26T15:35:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+import React from "react";
+import { render, screen, fireEvent, waitFor } from "@testing-library/react";
+import { DefaultTheme, ThemeProvider } from "styled-components";
+import CopyButton from "./CopyButton";
+
+jest.mock("i18next", () => ({
+  t: jest.fn(key => {
+    if (key === "common.copy") return "Copy";
+    if (key === "common.copied") return "Copied";
+    return key;
+  }),
+}));
+
+const mockTheme = {
+  colors: {
+    neutral: {
+      c30: "#F4F4F4",
+      c90: "#4D4D4D",
+    },
+    success: {
+      c50: "#5BA54C",
+    },
+  },
+} as DefaultTheme;
+
+jest.mock("@ledgerhq/react-ui", () => ({
+  __esModule: true,
+  Button: ({ children, onClick }: never) => (
+    <button onClick={onClick} data-testid="ledger-button">
+      {children}
+    </button>
+  ),
+  Icons: {
+    Check: () => <div data-testid="check-icon" />,
+    Copy: () => <div data-testid="copy-icon" />,
+  },
+}));
+
+beforeAll(() => {
+  Object.assign(navigator, {
+    clipboard: {
+      writeText: jest.fn().mockResolvedValue(undefined),
+    },
+  });
+});
+
+afterAll(() => {
+  jest.restoreAllMocks();
+});
+
+describe("CopyButton", () => {
+  const testText = "Text to copy";
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  const renderWithTheme = (component: React.ReactNode) => {
+    return render(<ThemeProvider theme={mockTheme}>{component}</ThemeProvider>, {
+      wrapper: ({ children }) => children,
+    });
+  };
+
+  it("renders correctly with copy icon and text", async () => {
+    const { unmount } = renderWithTheme(<CopyButton text={testText} />);
+
+    expect(screen.getByTestId("copy-icon")).toBeInTheDocument();
+    expect(screen.getByText("Copy")).toBeInTheDocument();
+
+    unmount();
+  });
+
+  it("copies text to clipboard when clicked", async () => {
+    const { unmount } = renderWithTheme(<CopyButton text={testText} />);
+
+    await waitFor(() => {
+      fireEvent.click(screen.getByTestId("ledger-button"));
+    });
path: apps/ledger-live-desktop/src/renderer/modals/SignMessage/Components/CopyButton.test.tsx
user: login: mcayuelas-ledger
body: ```suggestion
    const { user } = renderWithTheme(<CopyButton text={testText} />);

    const btn = screen.getByRole("button","Copy");
    expect(btn).toBeVisible();
    await user.click(btn);
```

you can check this doc : https://testing-library.com/docs/queries/about/#priority


created_at: 2025-03-26T15:38:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-26T10:24:33Z
body: [vc]: #flMs+4UQoYSio9HuIgQSiqb/S41XKcC2SUbMhypHQgk=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJyZWFjdC11aS1zdG9yeWJvb2siLCJyb290RGlyZWN0b3J5IjpudWxsLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9sZWRnZXJocS9yZWFjdC11aS1zdG9yeWJvb2svOVJHRE13a21DVG44NTRTN29XZEJUNXVhcXBlUCIsIm5leHRDb21taXRTdGF0dXMiOiJJR05PUkVEIiwicHJldmlld1VybCI6InJlYWN0LXVpLXN0b3J5Ym9vay1naXQtZmVhdC1saXZlLTU1ODQtbGVkZ2VyaHEudmVyY2VsLmFwcCJ9LHsibmFtZSI6Im5hdGl2ZS11aS1zdG9yeWJvb2siLCJyb290RGlyZWN0b3J5IjpudWxsLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9sZWRnZXJocS9uYXRpdmUtdWktc3Rvcnlib29rL29hVkNDRjFYYmlNQ2I5cnRKbTc4dmFYZWpLVVMiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsInByZXZpZXdVcmwiOiJuYXRpdmUtdWktc3Rvcnlib29rLWdpdC1mZWF0LWxpdmUtNTU4NC1sZWRnZXJocS52ZXJjZWwuYXBwIn0seyJuYW1lIjoid2ViLXRvb2xzIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvd2ViLXRvb2xzIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vbGVkZ2VyaHEvd2ViLXRvb2xzLzNFemY1YVhiWEZhWHU1WEZhWENnRWJlNXR0dkUiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsInByZXZpZXdVcmwiOiJ3ZWItdG9vbHMtZ2l0LWZlYXQtbGl2ZS01NTg0LWxlZGdlcmhxLnZlcmNlbC5hcHAifSx7Im5hbWUiOiJsZWRnZXItbGl2ZS1naXRodWItYm90Iiwicm9vdERpcmVjdG9yeSI6InRvb2xzL2dpdGh1Yi1ib3QiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9sZWRnZXJocS9sZWRnZXItbGl2ZS1naXRodWItYm90LzdkVjRuVTJWaEhYVFRZcENSUnVOdEFjWExkNW8iLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsInByZXZpZXdVcmwiOiJsZWRnZXItbGl2ZS1naXRodWItYm90LWdpdC1mZWF0LWxpdmUtNTU4NC1sZWRnZXJocS52ZXJjZWwuYXBwIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)


<details><summary>4 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **ledger-live-github-bot** | ⬜️ Ignored ([Inspect](https://vercel.com/ledgerhq/ledger-live-github-bot/7dV4nU2VhHXTTYpCRRuNtAcXLd5o)) | [Visit Preview](https://ledger-live-github-bot-git-feat-live-5584-ledgerhq.vercel.app) |  | Mar 27, 2025 1:13pm |
| **native-ui-storybook** | ⬜️ Ignored ([Inspect](https://vercel.com/ledgerhq/native-ui-storybook/oaVCCF1XbiMCb9rtJm78vaXejKUS)) | [Visit Preview](https://native-ui-storybook-git-feat-live-5584-ledgerhq.vercel.app) |  | Mar 27, 2025 1:13pm |
| **react-ui-storybook** | ⬜️ Ignored ([Inspect](https://vercel.com/ledgerhq/react-ui-storybook/9RGDMwkmCTn854S7oWdBT5uaqpeP)) | [Visit Preview](https://react-ui-storybook-git-feat-live-5584-ledgerhq.vercel.app) |  | Mar 27, 2025 1:13pm |
| **web-tools** | ⬜️ Ignored ([Inspect](https://vercel.com/ledgerhq/web-tools/3Ezf5aXbXFaXu5XFaXCgEbe5ttvE)) | [Visit Preview](https://web-tools-git-feat-live-5584-ledgerhq.vercel.app) |  | Mar 27, 2025 1:13pm |
</details>





author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-03-27T13:34:25Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=LedgerHQ_ledger-live&pullRequest=9671) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=LedgerHQ_ledger-live&pullRequest=9671&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=LedgerHQ_ledger-live&pullRequest=9671&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=LedgerHQ_ledger-live&pullRequest=9671&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=LedgerHQ_ledger-live&pullRequest=9671&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=LedgerHQ_ledger-live&pullRequest=9671&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=LedgerHQ_ledger-live&pullRequest=9671)


author_association: NONE