From 2761726d738e9b7279ed87f0aa3946edec32b469 Mon Sep 17 00:00:00 2001 From: AaronDewes Date: Fri, 23 Dec 2022 14:22:31 +0000 Subject: [PATCH 01/12] feat: allow adding accounts --- src/app/router/Prompt/Prompt.tsx | 4 +- .../screens/ConfirmAddAccount/index.test.tsx | 54 +++++++++++ src/app/screens/ConfirmAddAccount/index.tsx | 94 +++++++++++++++++++ .../actions/accounts/index.ts | 15 ++- .../actions/accounts/promptAdd.ts | 17 ++++ src/extension/background-script/router.ts | 3 + src/extension/content-script/onend.js | 5 +- src/extension/content-script/onstart.ts | 3 + src/extension/inpage-script/alby.js | 5 + src/extension/ln/alby/index.ts | 60 ++++++++++++ src/i18n/locales/en/translation.json | 4 + src/manifest.json | 3 +- webpack.config.js | 1 + 13 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 src/app/screens/ConfirmAddAccount/index.test.tsx create mode 100644 src/app/screens/ConfirmAddAccount/index.tsx create mode 100644 src/extension/background-script/actions/accounts/promptAdd.ts create mode 100644 src/extension/inpage-script/alby.js create mode 100644 src/extension/ln/alby/index.ts diff --git a/src/app/router/Prompt/Prompt.tsx b/src/app/router/Prompt/Prompt.tsx index bf7db7d362..69fb085526 100644 --- a/src/app/router/Prompt/Prompt.tsx +++ b/src/app/router/Prompt/Prompt.tsx @@ -1,4 +1,5 @@ import AccountMenu from "@components/AccountMenu"; +import ConfirmAddAccount from "@screens/ConfirmAddAccount"; import ConfirmKeysend from "@screens/ConfirmKeysend"; import ConfirmPayment from "@screens/ConfirmPayment"; import ConfirmRequestPermission from "@screens/ConfirmRequestPermission"; @@ -13,7 +14,7 @@ import NostrConfirm from "@screens/Nostr/Confirm"; import NostrConfirmGetPublicKey from "@screens/Nostr/ConfirmGetPublicKey"; import NostrConfirmSignMessage from "@screens/Nostr/ConfirmSignMessage"; import Unlock from "@screens/Unlock"; -import { HashRouter, Outlet, Route, Routes, Navigate } from "react-router-dom"; +import { HashRouter, Navigate, Outlet, Route, Routes } from "react-router-dom"; import { ToastContainer } from "react-toastify"; import Providers from "~/app/context/Providers"; import RequireAuth from "~/app/router/RequireAuth"; @@ -91,6 +92,7 @@ function Prompt() { } /> } /> } /> + } /> } diff --git a/src/app/screens/ConfirmAddAccount/index.test.tsx b/src/app/screens/ConfirmAddAccount/index.test.tsx new file mode 100644 index 0000000000..2ceed7e498 --- /dev/null +++ b/src/app/screens/ConfirmAddAccount/index.test.tsx @@ -0,0 +1,54 @@ +import { act, render, screen } from "@testing-library/react"; +import { MemoryRouter } from "react-router-dom"; +import type { OriginData } from "~/types"; + +import ConfirmAddAccount from "./index"; + +const mockOrigin: OriginData = { + location: "https://getalby.com/demo", + domain: "https://getalby.com", + host: "getalby.com", + pathname: "/demo", + name: "Alby", + description: "", + icon: "https://getalby.com/assets/alby-503261fa1b83c396b7ba8d927db7072d15fea5a84d387a654c5d0a2cefd44604.svg", + metaData: { + title: "Alby Demo", + url: "https://getalby.com/demo", + provider: "Alby", + image: + "https://getalby.com/assets/alby-503261fa1b83c396b7ba8d927db7072d15fea5a84d387a654c5d0a2cefd44604.svg", + icon: "https://getalby.com/favicon.ico", + }, + external: true, +}; + +jest.mock("~/app/hooks/useNavigationState", () => { + return { + useNavigationState: jest.fn(() => ({ + origin: mockOrigin, + args: { + message: "Test message", + }, + })), + }; +}); + +describe("ConfirmAddAccount", () => { + test("render", async () => { + await act(async () => { + render( + + + + ); + }); + + expect( + await screen.findByText( + "This website wants to add a wallet of the following type to Alby:" + ) + ).toBeInTheDocument(); + expect(await screen.findByText("Test message")).toBeInTheDocument(); + }); +}); diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx new file mode 100644 index 0000000000..13057393e7 --- /dev/null +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -0,0 +1,94 @@ +import ConfirmOrCancel from "@components/ConfirmOrCancel"; +import Container from "@components/Container"; +import ContentMessage from "@components/ContentMessage"; +import PublisherCard from "@components/PublisherCard"; +import SuccessMessage from "@components/SuccessMessage"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { toast } from "react-toastify"; +import ScreenHeader from "~/app/components/ScreenHeader"; +import { useNavigationState } from "~/app/hooks/useNavigationState"; +import { USER_REJECTED_ERROR } from "~/common/constants"; +import msg from "~/common/lib/msg"; +import type { OriginData } from "~/types"; + +function ConfirmAddAccount() { + const navState = useNavigationState(); + const { t: tCommon } = useTranslation("common"); + const { t } = useTranslation("translation", { + keyPrefix: "confirm_add_account", + }); + const navigate = useNavigate(); + + const message = navState.args?.message as string; + const origin = navState.origin as OriginData; + const [loading, setLoading] = useState(false); + const [successMessage, setSuccessMessage] = useState(""); + + async function confirm() { + try { + setLoading(true); + const response = await msg.request("addAccount", { message }, { origin }); + msg.reply(response); + setSuccessMessage(tCommon("success")); + } catch (e) { + console.error(e); + if (e instanceof Error) toast.error(`${tCommon("error")}: ${e.message}`); + } finally { + setLoading(false); + } + } + + function reject(e: React.MouseEvent) { + e.preventDefault(); + msg.error(USER_REJECTED_ERROR); + } + + function close(e: React.MouseEvent) { + if (navState.isPrompt) { + window.close(); + } else { + e.preventDefault(); + navigate(-1); + } + } + + return ( +
+ + {!successMessage ? ( + +
+ + +
+ +
+ ) : ( + + + + + )} +
+ ); +} + +export default ConfirmAddAccount; diff --git a/src/extension/background-script/actions/accounts/index.ts b/src/extension/background-script/actions/accounts/index.ts index 29142f1621..9a7748aa33 100644 --- a/src/extension/background-script/actions/accounts/index.ts +++ b/src/extension/background-script/actions/accounts/index.ts @@ -1,3 +1,5 @@ +import promptAdd from "~/extension/background-script/actions/accounts/promptAdd"; + import add from "./add"; import all from "./all"; import decryptedDetails from "./decryptedDetails"; @@ -8,4 +10,15 @@ import remove from "./remove"; import select from "./select"; import unlock from "./unlock"; -export { all, unlock, lock, add, edit, select, info, remove, decryptedDetails }; +export { + all, + unlock, + lock, + add, + edit, + select, + info, + remove, + decryptedDetails, + promptAdd, +}; diff --git a/src/extension/background-script/actions/accounts/promptAdd.ts b/src/extension/background-script/actions/accounts/promptAdd.ts new file mode 100644 index 0000000000..f422df60fd --- /dev/null +++ b/src/extension/background-script/actions/accounts/promptAdd.ts @@ -0,0 +1,17 @@ +import utils from "~/common/lib/utils"; +import { Message } from "~/types"; + +export default async function promptAddAccount(message: Message) { + try { + const response = await utils.openPrompt({ + ...message, + action: "confirmAddAccount", + }); + return response; + } catch (e) { + console.error("Adding account cancelled", e); + if (e instanceof Error) { + return { error: e.message }; + } + } +} diff --git a/src/extension/background-script/router.ts b/src/extension/background-script/router.ts index 5dba8ab46f..7c0f24eb2f 100644 --- a/src/extension/background-script/router.ts +++ b/src/extension/background-script/router.ts @@ -63,6 +63,9 @@ const routes = { // Public calls that are accessible from the inpage script (through the content script) public: { + alby: { + addAccount: accounts.promptAdd, + }, webln: { enable: allowances.enable, getInfo: ln.getInfo, diff --git a/src/extension/content-script/onend.js b/src/extension/content-script/onend.js index d196a75d27..94f54b2b00 100644 --- a/src/extension/content-script/onend.js +++ b/src/extension/content-script/onend.js @@ -8,6 +8,7 @@ import shouldInject from "./shouldInject"; // WebLN calls that can be executed from the WebLNProvider. // Update when new calls are added const weblnCalls = [ + "alby/addAccount", "webln/enable", "webln/getInfo", "webln/lnurl", @@ -18,7 +19,7 @@ const weblnCalls = [ "webln/request", ]; // calls that can be executed when webln is not enabled for the current content page -const disabledCalls = ["webln/enable"]; +const disabledCalls = ["alby/addAccount", "webln/enable"]; let isEnabled = false; // store if webln is enabled for this content page let isRejected = false; // store if the webln enable call failed. if so we do not prompt again @@ -47,7 +48,7 @@ async function init() { if ( ev.source !== window || ev.data.application !== "LBE" || - ev.data.scope !== "webln" + (ev.data.scope !== "webln" && ev.data.scope !== "alby") ) { return; } diff --git a/src/extension/content-script/onstart.ts b/src/extension/content-script/onstart.ts index b0154ae2fa..9c574a0c1d 100644 --- a/src/extension/content-script/onstart.ts +++ b/src/extension/content-script/onstart.ts @@ -18,6 +18,9 @@ async function onstart() { if (nostrEnabled) { injectScript(browser.runtime.getURL("js/inpageScriptNostr.bundle.js")); } + + // window.alby + injectScript(browser.runtime.getURL("js/inpageScriptAlby.bundle.js")); } onstart(); diff --git a/src/extension/inpage-script/alby.js b/src/extension/inpage-script/alby.js new file mode 100644 index 0000000000..d5f336a519 --- /dev/null +++ b/src/extension/inpage-script/alby.js @@ -0,0 +1,5 @@ +import AlbyProvider from "../ln/alby"; + +if (document) { + window.alby = new AlbyProvider(); +} diff --git a/src/extension/ln/alby/index.ts b/src/extension/ln/alby/index.ts new file mode 100644 index 0000000000..1dee6ddb65 --- /dev/null +++ b/src/extension/ln/alby/index.ts @@ -0,0 +1,60 @@ +export default class AlbyProvider { + executing = false; + + /** + * Adds a wallet to the user's Alby extension + * + * @param name The name of the account + * @param connector The connector to use + * @param config The config to pass to the connector + * @returns Nothing, throws if user rejects + */ + addAccount(name: string, connector: string, config: Record) { + return this.execute("addAccount", { connector, name, config }); + } + + execute( + action: string, + args?: Record + ): Promise> { + return new Promise((resolve, reject) => { + // post the request to the content script. from there it gets passed to the background script and back + // in page script can not directly connect to the background script + window.postMessage( + { + application: "LBE", + prompt: true, + action: `alby/${action}`, + scope: "alby", + args, + }, + "*" // TODO use origin + ); + + function handleWindowMessage(messageEvent: MessageEvent) { + // check if it is a relevant message + // there are some other events happening + if ( + !messageEvent.data || + !messageEvent.data.response || + messageEvent.data.application !== "LBE" || + messageEvent.data.scope !== "alby" + ) { + return; + } + if (messageEvent.data.data.error) { + reject(new Error(messageEvent.data.data.error)); + } else { + // 1. data: the message data + // 2. data: the data passed as data to the message + // 3. data: the actual response data + resolve(messageEvent.data.data.data); + } + // For some reason must happen only at the end of this function + window.removeEventListener("message", handleWindowMessage); + } + + window.addEventListener("message", handleWindowMessage); + }); + } +} diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index eed3962801..b7d0765769 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -638,6 +638,10 @@ "allow": "Allow this website to execute:", "always_allow": "Remember my choice and don't ask again" }, + "confirm_add_account": { + "title": "Add account", + "content": "This website wants to add a wallet of the following type to Alby:" + }, "nostr": { "title": "Nostr", "allow": "Allow this website to:", diff --git a/src/manifest.json b/src/manifest.json index 06b4221385..29e9eb60ac 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -14,7 +14,8 @@ "web_accessible_resources": [ "js/inpageScript.bundle.js", "js/inpageScriptWebLN.bundle.js", - "js/inpageScriptNostr.bundle.js" + "js/inpageScriptNostr.bundle.js", + "js/inpageScriptAlby.bundle.js" ], "permissions": [ "notifications", diff --git a/webpack.config.js b/webpack.config.js index c1f225c8b2..d3a3542ef9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -67,6 +67,7 @@ var options = { inpageScript: "./src/extension/inpage-script/index.js", inpageScriptWebLN: "./src/extension/inpage-script/webln.js", inpageScriptNostr: "./src/extension/inpage-script/nostr.js", + inpageScriptAlby: "./src/extension/inpage-script/alby.js", popup: "./src/app/router/Popup/index.tsx", prompt: "./src/app/router/Prompt/index.tsx", options: "./src/app/router/Options/index.tsx", From 1f08a5ef5d1dbd4c7d2be23420cc8f1b72d57793 Mon Sep 17 00:00:00 2001 From: AaronDewes Date: Fri, 23 Dec 2022 15:23:48 +0000 Subject: [PATCH 02/12] fix: pass correct data to the prompt --- src/app/screens/ConfirmAddAccount/index.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx index 13057393e7..3d5d1ab2df 100644 --- a/src/app/screens/ConfirmAddAccount/index.tsx +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -11,7 +11,6 @@ import ScreenHeader from "~/app/components/ScreenHeader"; import { useNavigationState } from "~/app/hooks/useNavigationState"; import { USER_REJECTED_ERROR } from "~/common/constants"; import msg from "~/common/lib/msg"; -import type { OriginData } from "~/types"; function ConfirmAddAccount() { const navState = useNavigationState(); @@ -21,15 +20,24 @@ function ConfirmAddAccount() { }); const navigate = useNavigate(); - const message = navState.args?.message as string; - const origin = navState.origin as OriginData; + const { name, connector, config } = navState.args as unknown as { + name: string; + connector: string; + config: unknown; + }; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const origin = navState.origin!; const [loading, setLoading] = useState(false); const [successMessage, setSuccessMessage] = useState(""); async function confirm() { try { setLoading(true); - const response = await msg.request("addAccount", { message }, { origin }); + const response = await msg.request( + "addAccount", + { name, connector, config }, + { origin } + ); msg.reply(response); setSuccessMessage(tCommon("success")); } catch (e) { @@ -67,7 +75,7 @@ function ConfirmAddAccount() { /> Date: Tue, 3 Jan 2023 18:34:12 +0000 Subject: [PATCH 03/12] fix: get test to run again --- src/app/screens/ConfirmAddAccount/index.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.test.tsx b/src/app/screens/ConfirmAddAccount/index.test.tsx index 2ceed7e498..700ea2a895 100644 --- a/src/app/screens/ConfirmAddAccount/index.test.tsx +++ b/src/app/screens/ConfirmAddAccount/index.test.tsx @@ -28,7 +28,7 @@ jest.mock("~/app/hooks/useNavigationState", () => { useNavigationState: jest.fn(() => ({ origin: mockOrigin, args: { - message: "Test message", + connector: "The connector type", }, })), }; @@ -49,6 +49,6 @@ describe("ConfirmAddAccount", () => { "This website wants to add a wallet of the following type to Alby:" ) ).toBeInTheDocument(); - expect(await screen.findByText("Test message")).toBeInTheDocument(); + expect(await screen.findByText("The connector type")).toBeInTheDocument(); }); }); From 9a25dfb21e321ec42e114a6c3636fb7bd41a07f9 Mon Sep 17 00:00:00 2001 From: AaronDewes Date: Tue, 3 Jan 2023 18:55:26 +0000 Subject: [PATCH 04/12] fix: improve information screen --- src/app/screens/ConfirmAddAccount/index.test.tsx | 7 ++++--- src/app/screens/ConfirmAddAccount/index.tsx | 7 +++++-- src/i18n/locales/en/translation.json | 13 ++++++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.test.tsx b/src/app/screens/ConfirmAddAccount/index.test.tsx index 700ea2a895..141e283693 100644 --- a/src/app/screens/ConfirmAddAccount/index.test.tsx +++ b/src/app/screens/ConfirmAddAccount/index.test.tsx @@ -28,7 +28,8 @@ jest.mock("~/app/hooks/useNavigationState", () => { useNavigationState: jest.fn(() => ({ origin: mockOrigin, args: { - connector: "The connector type", + connector: "nativecitadel", + name: "Your Citadel wallet", }, })), }; @@ -46,9 +47,9 @@ describe("ConfirmAddAccount", () => { expect( await screen.findByText( - "This website wants to add a wallet of the following type to Alby:" + 'getalby.com wants to add a wallet (with the "Citadel (over Tor)" connector) to Alby:' ) ).toBeInTheDocument(); - expect(await screen.findByText("The connector type")).toBeInTheDocument(); + expect(await screen.findByText("Your Citadel wallet")).toBeInTheDocument(); }); }); diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx index 3d5d1ab2df..d6729d8a00 100644 --- a/src/app/screens/ConfirmAddAccount/index.tsx +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -74,8 +74,11 @@ function ConfirmAddAccount() { url={origin.host} /> Date: Fri, 21 Apr 2023 16:52:57 +0530 Subject: [PATCH 05/12] chore: fix typos and typing --- src/app/screens/ConfirmAddAccount/index.tsx | 12 +++++------- src/i18n/locales/en/translation.json | 8 ++++---- src/types.ts | 3 +++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx index d6729d8a00..2d2031cc3d 100644 --- a/src/app/screens/ConfirmAddAccount/index.tsx +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -11,6 +11,7 @@ import ScreenHeader from "~/app/components/ScreenHeader"; import { useNavigationState } from "~/app/hooks/useNavigationState"; import { USER_REJECTED_ERROR } from "~/common/constants"; import msg from "~/common/lib/msg"; +import type { OriginData } from "~/types"; function ConfirmAddAccount() { const navState = useNavigationState(); @@ -20,13 +21,10 @@ function ConfirmAddAccount() { }); const navigate = useNavigate(); - const { name, connector, config } = navState.args as unknown as { - name: string; - connector: string; - config: unknown; - }; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const origin = navState.origin!; + const name = navState.args?.name as string; + const connector = navState.args?.connector as string; + const config = navState.args?.config as string; + const origin = navState.origin as OriginData; const [loading, setLoading] = useState(false); const [successMessage, setSuccessMessage] = useState(""); diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index c0b9b4add1..7f2e20a0d2 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -736,10 +736,10 @@ "content": "{{host}} wants to add a wallet (with the \"{{connector}}\" connector) to Alby:", "lnd": "LND", "nativelnd": "LND (over Tor)", - "lndhub": "LndHub", - "nativelndhub": "LndHub (over Tor)", - "lnbits": "LnBits", - "nativelnbits": "LnBits (over Tor)", + "lndhub": "LNDHub", + "nativelndhub": "LNDHub (over Tor)", + "lnbits": "LNBits", + "nativelnbits": "LNBits (over Tor)", "galoy": "Galoy", "eclair": "Eclair", "citadel": "Citadel", diff --git a/src/types.ts b/src/types.ts index 8e3738e3ad..927b0c099c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -139,6 +139,9 @@ export type NavigationState = { destination?: string; amount?: string; customRecords?: Record; + connector?: string; + name?: string; + config?: unknown; message?: string; event?: Event; sigHash?: string; From 450a067ed57453e566c4d2144c58f53c287249ab Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 21 Apr 2023 19:12:38 +0530 Subject: [PATCH 06/12] chore: separate alby provider --- src/app/router/Prompt/Prompt.tsx | 4 + src/app/screens/ConfirmAddAccount/index.tsx | 2 +- src/extension/background-script/router.ts | 1 + src/extension/content-script/onend.js | 5 +- src/extension/content-script/onendalby.js | 103 ++++++++++++++++++++ src/extension/content-script/onendnostr.js | 8 +- src/extension/ln/alby/index.ts | 21 +++- src/manifest.json | 4 +- src/types.ts | 2 +- webpack.config.js | 12 ++- 10 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 src/extension/content-script/onendalby.js diff --git a/src/app/router/Prompt/Prompt.tsx b/src/app/router/Prompt/Prompt.tsx index dc394d2b32..089716c3c7 100644 --- a/src/app/router/Prompt/Prompt.tsx +++ b/src/app/router/Prompt/Prompt.tsx @@ -67,6 +67,10 @@ function Prompt() { /> } /> + } // prompt will always have an `origin` set, just the type is optional to support usage via PopUp + /> } // prompt will always have an `origin` set, just the type is optional to support usage via PopUp diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx index 2d2031cc3d..6361568bfa 100644 --- a/src/app/screens/ConfirmAddAccount/index.tsx +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -23,7 +23,7 @@ function ConfirmAddAccount() { const name = navState.args?.name as string; const connector = navState.args?.connector as string; - const config = navState.args?.config as string; + const config = navState.args?.config as unknown; const origin = navState.origin as OriginData; const [loading, setLoading] = useState(false); const [successMessage, setSuccessMessage] = useState(""); diff --git a/src/extension/background-script/router.ts b/src/extension/background-script/router.ts index f0a6c0b56f..b32d0eab45 100644 --- a/src/extension/background-script/router.ts +++ b/src/extension/background-script/router.ts @@ -67,6 +67,7 @@ const routes = { // Public calls that are accessible from the inpage script (through the content script) public: { alby: { + enable: allowances.enable, addAccount: accounts.promptAdd, }, webln: { diff --git a/src/extension/content-script/onend.js b/src/extension/content-script/onend.js index 94f54b2b00..d196a75d27 100644 --- a/src/extension/content-script/onend.js +++ b/src/extension/content-script/onend.js @@ -8,7 +8,6 @@ import shouldInject from "./shouldInject"; // WebLN calls that can be executed from the WebLNProvider. // Update when new calls are added const weblnCalls = [ - "alby/addAccount", "webln/enable", "webln/getInfo", "webln/lnurl", @@ -19,7 +18,7 @@ const weblnCalls = [ "webln/request", ]; // calls that can be executed when webln is not enabled for the current content page -const disabledCalls = ["alby/addAccount", "webln/enable"]; +const disabledCalls = ["webln/enable"]; let isEnabled = false; // store if webln is enabled for this content page let isRejected = false; // store if the webln enable call failed. if so we do not prompt again @@ -48,7 +47,7 @@ async function init() { if ( ev.source !== window || ev.data.application !== "LBE" || - (ev.data.scope !== "webln" && ev.data.scope !== "alby") + ev.data.scope !== "webln" ) { return; } diff --git a/src/extension/content-script/onendalby.js b/src/extension/content-script/onendalby.js new file mode 100644 index 0000000000..042f12743e --- /dev/null +++ b/src/extension/content-script/onendalby.js @@ -0,0 +1,103 @@ +import browser from "webextension-polyfill"; + +import getOriginData from "./originData"; +import shouldInject from "./shouldInject"; + +// Alby calls that can be executed from the AlbyProvider. +// Update when new calls are added +const albyCalls = ["alby/enable", "alby/addAccount"]; +// calls that can be executed when alby is not enabled for the current content page +const disabledCalls = ["alby/enable"]; + +let isEnabled = false; // store if alby is enabled for this content page +let isRejected = false; // store if the alby enable call failed. if so we do not prompt again +let callActive = false; // store if a alby call is currently active. Used to prevent multiple calls in parallel + +async function init() { + const inject = await shouldInject(); + if (!inject) { + return; + } + + // message listener to listen to inpage alby calls + // those calls get passed on to the background script + // (the inpage script can not do that directly, but only the inpage script can make alby available to the page) + window.addEventListener("message", (ev) => { + // Only accept messages from the current window + if ( + ev.source !== window || + ev.data.application !== "LBE" || + ev.data.scope !== "alby" + ) { + return; + } + + if (ev.data && !ev.data.response) { + // if an enable call railed we ignore the request to prevent spamming the user with prompts + if (isRejected) { + console.error( + "Enable had failed. Rejecting further Alby calls until the next reload" + ); + return; + } + // if a call is active we ignore the request + if (callActive) { + console.error("alby call already executing"); + return; + } + // limit the calls that can be made from alby + // only listed calls can be executed + // if not enabled only enable can be called. + const availableCalls = isEnabled ? albyCalls : disabledCalls; + if (!availableCalls.includes(ev.data.action)) { + console.error( + "Function not available. Is the provider enabled?", + ev.data.action + ); + return; + } + + const messageWithOrigin = { + // every call call is scoped in `public` + // this prevents websites from accessing internal actions + action: `public/${ev.data.action}`, + args: ev.data.args, + application: "LBE", + public: true, // indicate that this is a public call from the content script + prompt: true, + origin: getOriginData(), + }; + + const replyFunction = (response) => { + callActive = false; // reset call is active + // if it is the enable call we store if alby is enabled for this content script + if (ev.data.action === "alby/enable") { + isEnabled = response.data?.enabled; + if (response.error) { + console.error(response.error); + console.info("Enable was rejected ignoring further alby calls"); + isRejected = true; + } + } + window.postMessage( + { + application: "LBE", + response: true, + data: response, + scope: "alby", + }, + "*" // TODO use origin + ); + }; + callActive = true; + return browser.runtime + .sendMessage(messageWithOrigin) + .then(replyFunction) + .catch(replyFunction); + } + }); +} + +init(); + +export {}; diff --git a/src/extension/content-script/onendnostr.js b/src/extension/content-script/onendnostr.js index 3cbe842ae9..6dee2a1684 100644 --- a/src/extension/content-script/onendnostr.js +++ b/src/extension/content-script/onendnostr.js @@ -14,7 +14,7 @@ const nostrCalls = [ "nostr/encryptOrPrompt", "nostr/decryptOrPrompt", ]; -// calls that can be executed when webln is not enabled for the current content page +// calls that can be executed when nostr is not enabled for the current content page const disabledCalls = ["nostr/enable"]; let isEnabled = false; // store if nostr is enabled for this content page @@ -27,9 +27,9 @@ async function init() { return; } - // message listener to listen to inpage webln calls + // message listener to listen to inpage nostr calls // those calls get passed on to the background script - // (the inpage script can not do that directly, but only the inpage script can make webln available to the page) + // (the inpage script can not do that directly, but only the inpage script can make nostr available to the page) window.addEventListener("message", (ev) => { // Only accept messages from the current window if ( @@ -44,7 +44,7 @@ async function init() { // if an enable call railed we ignore the request to prevent spamming the user with prompts if (isRejected) { console.error( - "Enable had failed. Rejecting further WebLN calls until the next reload" + "Enable had failed. Rejecting further Nostr calls until the next reload" ); return; } diff --git a/src/extension/ln/alby/index.ts b/src/extension/ln/alby/index.ts index 1dee6ddb65..479f72f5d2 100644 --- a/src/extension/ln/alby/index.ts +++ b/src/extension/ln/alby/index.ts @@ -1,5 +1,21 @@ export default class AlbyProvider { - executing = false; + enabled: boolean; + + constructor() { + this.enabled = false; + } + + enable() { + if (this.enabled) { + return Promise.resolve({ enabled: true }); + } + return this.execute("enable").then((result) => { + if (typeof result.enabled === "boolean") { + this.enabled = result.enabled; + } + return result; + }); + } /** * Adds a wallet to the user's Alby extension @@ -10,6 +26,9 @@ export default class AlbyProvider { * @returns Nothing, throws if user rejects */ addAccount(name: string, connector: string, config: Record) { + if (!this.enabled) { + throw new Error("Provider must be enabled before calling getInfo"); + } return this.execute("addAccount", { connector, name, config }); } diff --git a/src/manifest.json b/src/manifest.json index 7a4a320c45..05816f3a8f 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -25,7 +25,8 @@ "resources": [ "js/inpageScript.bundle.js", "js/inpageScriptWebLN.bundle.js", - "js/inpageScriptNostr.bundle.js" + "js/inpageScriptNostr.bundle.js", + "js/inpageScriptAlby.bundle.js" ], "matches": ["https://*/*"] } @@ -169,6 +170,7 @@ "run_at": "document_end", "js": [ "js/contentScriptOnEnd.bundle.js", + "js/contentScriptOnEndAlby.bundle.js", "js/contentScriptOnEndNostr.bundle.js" ] }, diff --git a/src/types.ts b/src/types.ts index 927b0c099c..9bec540166 100644 --- a/src/types.ts +++ b/src/types.ts @@ -324,7 +324,7 @@ export interface MessageAllowanceEnable extends MessageDefault { args: { host: Allowance["host"]; }; - action: "public/webln/enable" | "public/nostr/enable"; + action: "public/webln/enable" | "public/nostr/enable" | "public/alby/enable"; } export interface MessageAllowanceDelete extends MessageDefault { diff --git a/webpack.config.js b/webpack.config.js index d3a3542ef9..13409ae95c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,7 +9,8 @@ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const WextManifestWebpackPlugin = require("wext-manifest-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); -const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const BundleAnalyzerPlugin = + require("webpack-bundle-analyzer").BundleAnalyzerPlugin; // default value is set in the code where it is used if (!process.env.WALLET_CREATE_URL) { @@ -62,6 +63,7 @@ var options = { manifest: "./src/manifest.json", background: "./src/extension/background-script/index.ts", contentScriptOnEnd: "./src/extension/content-script/onend.js", + contentScriptOnEndAlby: "./src/extension/content-script/onendalby.js", contentScriptOnEndNostr: "./src/extension/content-script/onendnostr.js", contentScriptOnStart: "./src/extension/content-script/onstart.ts", inpageScript: "./src/extension/inpage-script/index.js", @@ -206,10 +208,10 @@ var options = { patterns: [{ from: "static/assets", to: "assets" }], }), new BundleAnalyzerPlugin({ - generateStatsFile: (nodeEnv !== "development" ? true : false), - analyzerMode: (nodeEnv !== "development" ? 'static' : 'disabled'), - reportFilename: '../bundle-report.html', - statsFilename: '../bundle-stats.json', + generateStatsFile: nodeEnv !== "development" ? true : false, + analyzerMode: nodeEnv !== "development" ? "static" : "disabled", + reportFilename: "../bundle-report.html", + statsFilename: "../bundle-stats.json", openAnalyzer: nodeEnv !== "development", }), ], From ad21442079b63c392ac1ab84a5b3041965c57ad1 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 1 May 2023 22:28:58 +0530 Subject: [PATCH 07/12] chore: translations --- src/app/screens/ConfirmAddAccount/index.tsx | 5 ++-- src/i18n/locales/en/translation.json | 26 +++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx index 6361568bfa..55817b111c 100644 --- a/src/app/screens/ConfirmAddAccount/index.tsx +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -73,8 +73,9 @@ function ConfirmAddAccount() { /> diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 7f2e20a0d2..a31d584bcb 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -733,18 +733,7 @@ }, "confirm_add_account": { "title": "Add account", - "content": "{{host}} wants to add a wallet (with the \"{{connector}}\" connector) to Alby:", - "lnd": "LND", - "nativelnd": "LND (over Tor)", - "lndhub": "LNDHub", - "nativelndhub": "LNDHub (over Tor)", - "lnbits": "LNBits", - "nativelnbits": "LNBits (over Tor)", - "galoy": "Galoy", - "eclair": "Eclair", - "citadel": "Citadel", - "nativecitadel": "Citadel (over Tor)", - "commando": "Commando" + "content": "This website wants to add an account ({{connector}}) to Alby:" }, "nostr": { "title": "Nostr", @@ -839,6 +828,19 @@ "log_in": "Log in", "remember": "Remember my choice and don't ask again" }, + "connectors": { + "lnd": "LND", + "nativelnd": "LND (over Tor)", + "lndhub": "LNDHub", + "nativelndhub": "LNDHub (over Tor)", + "lnbits": "LNBits", + "nativelnbits": "LNBits (over Tor)", + "galoy": "Galoy", + "eclair": "Eclair", + "citadel": "Citadel", + "nativecitadel": "Citadel (over Tor)", + "commando": "Commando" + }, "errors": { "connection_failed": "Connection failed", "payment_failed": "Payment Failed" From dfb9b0c79e29291982467c4e75b480c4ce49f0ef Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 1 May 2023 22:33:49 +0530 Subject: [PATCH 08/12] fix: tests --- src/app/screens/ConfirmAddAccount/index.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/screens/ConfirmAddAccount/index.test.tsx b/src/app/screens/ConfirmAddAccount/index.test.tsx index 141e283693..71db63181e 100644 --- a/src/app/screens/ConfirmAddAccount/index.test.tsx +++ b/src/app/screens/ConfirmAddAccount/index.test.tsx @@ -47,7 +47,7 @@ describe("ConfirmAddAccount", () => { expect( await screen.findByText( - 'getalby.com wants to add a wallet (with the "Citadel (over Tor)" connector) to Alby:' + "This website wants to add an account (Citadel (over Tor)) to Alby:" ) ).toBeInTheDocument(); expect(await screen.findByText("Your Citadel wallet")).toBeInTheDocument(); From 2283023fca18bdfbd611beae9b9fda7b10ba3bce Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 3 May 2023 15:38:09 +0530 Subject: [PATCH 09/12] chore: remove success message and other minor fixes --- src/app/screens/ConfirmAddAccount/index.tsx | 61 ++++++------------- .../actions/accounts/promptAdd.ts | 4 +- src/extension/content-script/onendalby.js | 39 ++++++------ src/extension/ln/alby/index.ts | 2 +- 4 files changed, 42 insertions(+), 64 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.tsx b/src/app/screens/ConfirmAddAccount/index.tsx index 55817b111c..35fbe84ec1 100644 --- a/src/app/screens/ConfirmAddAccount/index.tsx +++ b/src/app/screens/ConfirmAddAccount/index.tsx @@ -2,10 +2,8 @@ import ConfirmOrCancel from "@components/ConfirmOrCancel"; import Container from "@components/Container"; import ContentMessage from "@components/ContentMessage"; import PublisherCard from "@components/PublisherCard"; -import SuccessMessage from "@components/SuccessMessage"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import ScreenHeader from "~/app/components/ScreenHeader"; import { useNavigationState } from "~/app/hooks/useNavigationState"; @@ -19,14 +17,12 @@ function ConfirmAddAccount() { const { t } = useTranslation("translation", { keyPrefix: "confirm_add_account", }); - const navigate = useNavigate(); const name = navState.args?.name as string; const connector = navState.args?.connector as string; const config = navState.args?.config as unknown; const origin = navState.origin as OriginData; const [loading, setLoading] = useState(false); - const [successMessage, setSuccessMessage] = useState(""); async function confirm() { try { @@ -37,7 +33,6 @@ function ConfirmAddAccount() { { origin } ); msg.reply(response); - setSuccessMessage(tCommon("success")); } catch (e) { console.error(e); if (e instanceof Error) toast.error(`${tCommon("error")}: ${e.message}`); @@ -51,52 +46,32 @@ function ConfirmAddAccount() { msg.error(USER_REJECTED_ERROR); } - function close(e: React.MouseEvent) { - if (navState.isPrompt) { - window.close(); - } else { - e.preventDefault(); - navigate(-1); - } - } - return (
- {!successMessage ? ( - -
- - -
- -
- ) : ( - + +
- - - )} + +
+ +
); } diff --git a/src/extension/background-script/actions/accounts/promptAdd.ts b/src/extension/background-script/actions/accounts/promptAdd.ts index f422df60fd..8f9ee36209 100644 --- a/src/extension/background-script/actions/accounts/promptAdd.ts +++ b/src/extension/background-script/actions/accounts/promptAdd.ts @@ -3,11 +3,11 @@ import { Message } from "~/types"; export default async function promptAddAccount(message: Message) { try { - const response = await utils.openPrompt({ + await utils.openPrompt({ ...message, action: "confirmAddAccount", }); - return response; + return { data: true }; } catch (e) { console.error("Adding account cancelled", e); if (e instanceof Error) { diff --git a/src/extension/content-script/onendalby.js b/src/extension/content-script/onendalby.js index 042f12743e..35f095bbfc 100644 --- a/src/extension/content-script/onendalby.js +++ b/src/extension/content-script/onendalby.js @@ -35,25 +35,23 @@ async function init() { if (ev.data && !ev.data.response) { // if an enable call railed we ignore the request to prevent spamming the user with prompts if (isRejected) { - console.error( - "Enable had failed. Rejecting further Alby calls until the next reload" - ); + postMessage(ev, { + error: + "window.alby call cancelled (rejecting further window.alby calls until the next reload)", + }); return; } // if a call is active we ignore the request if (callActive) { - console.error("alby call already executing"); + postMessage(ev, { error: "window.alby call already executing" }); return; } - // limit the calls that can be made from alby + // limit the calls that can be made from window.alby // only listed calls can be executed // if not enabled only enable can be called. const availableCalls = isEnabled ? albyCalls : disabledCalls; if (!availableCalls.includes(ev.data.action)) { - console.error( - "Function not available. Is the provider enabled?", - ev.data.action - ); + console.error("Function not available."); return; } @@ -79,15 +77,7 @@ async function init() { isRejected = true; } } - window.postMessage( - { - application: "LBE", - response: true, - data: response, - scope: "alby", - }, - "*" // TODO use origin - ); + postMessage(ev, response); }; callActive = true; return browser.runtime @@ -98,6 +88,19 @@ async function init() { }); } +function postMessage(ev, response) { + window.postMessage( + { + id: ev.data.id, + application: "LBE", + response: true, + data: response, + scope: "alby", + }, + "*" + ); +} + init(); export {}; diff --git a/src/extension/ln/alby/index.ts b/src/extension/ln/alby/index.ts index 479f72f5d2..8e9c4e2cf4 100644 --- a/src/extension/ln/alby/index.ts +++ b/src/extension/ln/alby/index.ts @@ -27,7 +27,7 @@ export default class AlbyProvider { */ addAccount(name: string, connector: string, config: Record) { if (!this.enabled) { - throw new Error("Provider must be enabled before calling getInfo"); + throw new Error("Provider must be enabled before calling addAccount"); } return this.execute("addAccount", { connector, name, config }); } From d776ad415e90fb765e356ee267ce9afc86ffde5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 23 May 2023 10:43:55 +0200 Subject: [PATCH 10/12] fix: cleanup inpage scripts --- src/extension/inpage-script/alby.js | 2 +- src/extension/ln/alby/index.ts | 79 --------------------------- src/extension/providers/alby/index.ts | 43 +++++++++++++++ 3 files changed, 44 insertions(+), 80 deletions(-) delete mode 100644 src/extension/ln/alby/index.ts create mode 100644 src/extension/providers/alby/index.ts diff --git a/src/extension/inpage-script/alby.js b/src/extension/inpage-script/alby.js index d5f336a519..e56f40eb63 100644 --- a/src/extension/inpage-script/alby.js +++ b/src/extension/inpage-script/alby.js @@ -1,4 +1,4 @@ -import AlbyProvider from "../ln/alby"; +import AlbyProvider from "../providers/alby"; if (document) { window.alby = new AlbyProvider(); diff --git a/src/extension/ln/alby/index.ts b/src/extension/ln/alby/index.ts deleted file mode 100644 index 8e9c4e2cf4..0000000000 --- a/src/extension/ln/alby/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -export default class AlbyProvider { - enabled: boolean; - - constructor() { - this.enabled = false; - } - - enable() { - if (this.enabled) { - return Promise.resolve({ enabled: true }); - } - return this.execute("enable").then((result) => { - if (typeof result.enabled === "boolean") { - this.enabled = result.enabled; - } - return result; - }); - } - - /** - * Adds a wallet to the user's Alby extension - * - * @param name The name of the account - * @param connector The connector to use - * @param config The config to pass to the connector - * @returns Nothing, throws if user rejects - */ - addAccount(name: string, connector: string, config: Record) { - if (!this.enabled) { - throw new Error("Provider must be enabled before calling addAccount"); - } - return this.execute("addAccount", { connector, name, config }); - } - - execute( - action: string, - args?: Record - ): Promise> { - return new Promise((resolve, reject) => { - // post the request to the content script. from there it gets passed to the background script and back - // in page script can not directly connect to the background script - window.postMessage( - { - application: "LBE", - prompt: true, - action: `alby/${action}`, - scope: "alby", - args, - }, - "*" // TODO use origin - ); - - function handleWindowMessage(messageEvent: MessageEvent) { - // check if it is a relevant message - // there are some other events happening - if ( - !messageEvent.data || - !messageEvent.data.response || - messageEvent.data.application !== "LBE" || - messageEvent.data.scope !== "alby" - ) { - return; - } - if (messageEvent.data.data.error) { - reject(new Error(messageEvent.data.data.error)); - } else { - // 1. data: the message data - // 2. data: the data passed as data to the message - // 3. data: the actual response data - resolve(messageEvent.data.data.data); - } - // For some reason must happen only at the end of this function - window.removeEventListener("message", handleWindowMessage); - } - - window.addEventListener("message", handleWindowMessage); - }); - } -} diff --git a/src/extension/providers/alby/index.ts b/src/extension/providers/alby/index.ts new file mode 100644 index 0000000000..647bb43f61 --- /dev/null +++ b/src/extension/providers/alby/index.ts @@ -0,0 +1,43 @@ +import { postMessage } from "../postMessage"; + +export default class AlbyProvider { + enabled: boolean; + + constructor() { + this.enabled = false; + } + + enable() { + if (this.enabled) { + return Promise.resolve({ enabled: true }); + } + return this.execute("enable").then((result) => { + if (typeof result.enabled === "boolean") { + this.enabled = result.enabled; + } + return result; + }); + } + + /** + * Adds a wallet to the user's Alby extension + * + * @param name The name of the account + * @param connector The connector to use + * @param config The config to pass to the connector + * @returns Nothing, throws if user rejects + */ + addAccount(name: string, connector: string, config: Record) { + if (!this.enabled) { + throw new Error("Provider must be enabled before calling addAccount"); + } + return this.execute("addAccount", { connector, name, config }); + } + + execute( + action: string, + args?: Record + ): Promise> { + return postMessage("alby", action, args); + } +} From b0e56f5c910fac4df6398cb39795d98737b0b7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Wed, 7 Jun 2023 01:46:35 +0200 Subject: [PATCH 11/12] fix: updated method signature to named params, added validation --- .../screens/ConfirmAddAccount/index.test.tsx | 3 ++- .../actions/accounts/promptAdd.ts | 23 +++++++++++++++++-- src/i18n/locales/en/translation.json | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/app/screens/ConfirmAddAccount/index.test.tsx b/src/app/screens/ConfirmAddAccount/index.test.tsx index 71db63181e..98a5f235e8 100644 --- a/src/app/screens/ConfirmAddAccount/index.test.tsx +++ b/src/app/screens/ConfirmAddAccount/index.test.tsx @@ -30,6 +30,7 @@ jest.mock("~/app/hooks/useNavigationState", () => { args: { connector: "nativecitadel", name: "Your Citadel wallet", + config: { connectionString: "..." }, }, })), }; @@ -47,7 +48,7 @@ describe("ConfirmAddAccount", () => { expect( await screen.findByText( - "This website wants to add an account (Citadel (over Tor)) to Alby:" + "This website wants to add an account (Citadel (over Tor)):" ) ).toBeInTheDocument(); expect(await screen.findByText("Your Citadel wallet")).toBeInTheDocument(); diff --git a/src/extension/background-script/actions/accounts/promptAdd.ts b/src/extension/background-script/actions/accounts/promptAdd.ts index 8f9ee36209..891d53a625 100644 --- a/src/extension/background-script/actions/accounts/promptAdd.ts +++ b/src/extension/background-script/actions/accounts/promptAdd.ts @@ -2,16 +2,35 @@ import utils from "~/common/lib/utils"; import { Message } from "~/types"; export default async function promptAddAccount(message: Message) { + if (typeof message.args.name !== "string") { + return { + error: "Name missing.", + }; + } + + if (typeof message.args.connector !== "string") { + return { + error: "Connector missing.", + }; + } + + if (typeof message.args.config !== "object") { + return { + error: "Config missing.", + }; + } + try { await utils.openPrompt({ ...message, action: "confirmAddAccount", }); - return { data: true }; + return { data: { success: true } }; } catch (e) { console.error("Adding account cancelled", e); if (e instanceof Error) { - return { error: e.message }; + return { success: false, error: e.message }; } + return { success: false }; } } diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 92d5091764..1551e6d29f 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -733,7 +733,7 @@ }, "confirm_add_account": { "title": "Add account", - "content": "This website wants to add an account ({{connector}}) to Alby:" + "content": "This website wants to add an account ({{connector}}):" }, "nostr": { "title": "Nostr", From 35065860b5da4a63c71390e208341f610f5c5e41 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 7 Jun 2023 14:35:13 +0530 Subject: [PATCH 12/12] fix: promptadd import --- src/extension/background-script/actions/accounts/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/extension/background-script/actions/accounts/index.ts b/src/extension/background-script/actions/accounts/index.ts index 06ef025c95..1cc99d998a 100644 --- a/src/extension/background-script/actions/accounts/index.ts +++ b/src/extension/background-script/actions/accounts/index.ts @@ -1,5 +1,3 @@ -import promptAdd from "~/extension/background-script/actions/accounts/promptAdd"; - import add from "./add"; import all from "./all"; import decryptedDetails from "./decryptedDetails"; @@ -7,6 +5,7 @@ import edit from "./edit"; import get from "./get"; import info from "./info"; import lock from "./lock"; +import promptAdd from "./promptAdd"; import remove from "./remove"; import select from "./select"; import unlock from "./unlock";