From cc4b5bcbe9a1dda5c56134d1559adc24852f7cef Mon Sep 17 00:00:00 2001 From: aristides Date: Fri, 15 Nov 2024 10:32:24 -0700 Subject: [PATCH 01/11] adds size constraints for the favicon container to avoid overflowing icons of non standard sizes (#1673) --- extension/src/popup/components/PunycodedDomain/index.tsx | 2 +- extension/src/popup/components/PunycodedDomain/styles.scss | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/extension/src/popup/components/PunycodedDomain/index.tsx b/extension/src/popup/components/PunycodedDomain/index.tsx index 0aa1cb9bdd..e47a48b084 100644 --- a/extension/src/popup/components/PunycodedDomain/index.tsx +++ b/extension/src/popup/components/PunycodedDomain/index.tsx @@ -23,7 +23,7 @@ export const PunycodedDomain = ({ className={`PunycodedDomain ${isRow ? "PunycodedDomain--row" : ""}`} {...props} > -
+
Date: Fri, 15 Nov 2024 10:43:40 -0700 Subject: [PATCH 02/11] [BUG] makes swap amount persist across destination asset changes (#1675) * makes swap amount persist across destination asset changes * updates swap test state to init with balances --- .../sendPayment/SendAmount/AssetSelect/index.tsx | 4 ++++ .../src/popup/components/sendPayment/SendAmount/index.tsx | 1 + extension/src/popup/views/__tests__/Swap.test.tsx | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/extension/src/popup/components/sendPayment/SendAmount/AssetSelect/index.tsx b/extension/src/popup/components/sendPayment/SendAmount/AssetSelect/index.tsx index 599225aa7d..47c58480cf 100644 --- a/extension/src/popup/components/sendPayment/SendAmount/AssetSelect/index.tsx +++ b/extension/src/popup/components/sendPayment/SendAmount/AssetSelect/index.tsx @@ -12,6 +12,7 @@ import { saveAssetSelectSource, saveAssetSelectType, AssetSelectType, + saveAmount, } from "popup/ducks/transactionSubmission"; import { isContractId } from "popup/helpers/soroban"; import { useIsSwap } from "popup/helpers/useIsSwap"; @@ -121,6 +122,9 @@ export const PathPayAssetSelect = ({ ), ); dispatch(saveAssetSelectSource(source)); + if (source) { + dispatch(saveAmount("0")); + } navigateTo(ROUTES.manageAssets, isSwap ? "?swap=true" : ""); }; diff --git a/extension/src/popup/components/sendPayment/SendAmount/index.tsx b/extension/src/popup/components/sendPayment/SendAmount/index.tsx index 0a1890f4b8..6b536ef96c 100644 --- a/extension/src/popup/components/sendPayment/SendAmount/index.tsx +++ b/extension/src/popup/components/sendPayment/SendAmount/index.tsx @@ -553,6 +553,7 @@ export const SendAmount = ({ e.target.selectionStart || 1, ); formik.setFieldValue("amount", newAmount); + dispatch(saveAmount(newAmount)); runAfterUpdate(() => { input.selectionStart = newCursor; input.selectionEnd = newCursor; diff --git a/extension/src/popup/views/__tests__/Swap.test.tsx b/extension/src/popup/views/__tests__/Swap.test.tsx index 3875226129..c0856fdc47 100644 --- a/extension/src/popup/views/__tests__/Swap.test.tsx +++ b/extension/src/popup/views/__tests__/Swap.test.tsx @@ -10,6 +10,7 @@ import { import * as ApiInternal from "@shared/api/internal"; import * as UseNetworkFees from "popup/helpers/useNetworkFees"; import * as BlockaidHelpers from "popup/helpers/blockaid"; +import { initialState as transactionSubmissionInitialState } from "popup/ducks/transactionSubmission"; import { TESTNET_NETWORK_DETAILS, DEFAULT_NETWORKS, @@ -379,6 +380,13 @@ describe("Swap", () => { networkDetails: TESTNET_NETWORK_DETAILS, networksList: DEFAULT_NETWORKS, }, + transactionSubmission: { + ...transactionSubmissionInitialState, + transactionData: { + ...transactionSubmissionInitialState.transactionData, + }, + accountBalances: swapMockBalances, + }, }} > From a2a5a60e9a996cdb1b08f66c486a4a80babad9fc Mon Sep 17 00:00:00 2001 From: aristides Date: Fri, 15 Nov 2024 10:45:59 -0700 Subject: [PATCH 03/11] adds enough padding to accomodate action modal on scrollable lists (#1676) --- .../popup/components/manageAssets/ManageAssetRows/styles.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extension/src/popup/components/manageAssets/ManageAssetRows/styles.scss b/extension/src/popup/components/manageAssets/ManageAssetRows/styles.scss index f60029afba..9ca5201442 100644 --- a/extension/src/popup/components/manageAssets/ManageAssetRows/styles.scss +++ b/extension/src/popup/components/manageAssets/ManageAssetRows/styles.scss @@ -35,6 +35,10 @@ align-items: center; justify-content: space-between; + &:last-child { + padding-bottom: 4rem; + } + &__info { color: var(--sds-clr-gray-12); flex-grow: 1; From fa92210e3acffb454ee43d6f7df5acf0ae9b985f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Marcos=20Goulart?= <3228151+CassioMG@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:13:10 -0300 Subject: [PATCH 04/11] Allow creating a new wallet from the `Enter Password` screen (#1648) * Allow creating a new wallet from initial screen * Added translations --- extension/src/popup/locales/en/translation.json | 15 +++++++++++---- extension/src/popup/locales/pt/translation.json | 15 +++++++++++---- extension/src/popup/views/UnlockAccount/index.tsx | 12 ++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/extension/src/popup/locales/en/translation.json b/extension/src/popup/locales/en/translation.json index 906d6f2bcd..08110bea3f 100644 --- a/extension/src/popup/locales/en/translation.json +++ b/extension/src/popup/locales/en/translation.json @@ -6,6 +6,7 @@ "A Stellar wallet for every website": "A Stellar wallet for every website", "About": "About", "Account": "Account", + "Account details": "Account details", "Account ID": "Account ID", "Account Migration": "Account Migration", "Account minimum balance is too low": "Account minimum balance is too low", @@ -103,6 +104,7 @@ "Confirm": "Confirm", "Confirm Data": "Confirm Data", "Confirm delete": "Confirm delete", + "Confirm Entry": "Confirm Entry", "Confirm password": "Confirm password", "Confirm removing Network": "Confirm removing Network", "Confirm Send": "Confirm Send", @@ -113,6 +115,7 @@ "Connect anyway": "Connect anyway", "Connect device to computer": "Connect device to computer", "Connect to domain without SSL certificate": "Connect to domain without SSL certificate", + "Connected apps": "Connected apps", "CONNECTION ERROR": "CONNECTION ERROR", "Connection Request": "Connection Request", "Continue": "Continue", @@ -124,6 +127,7 @@ "Copy address": "Copy address", "Create a new Stellar address": "Create a new Stellar address", "Create a password": "Create a password", + "Create a wallet": "Create a wallet", "Create Wallet": "Create Wallet", "Custom": "Custom", "data entries": "data entries", @@ -167,6 +171,7 @@ "Error code": "Error code", "Executable Type": "Executable Type", "Executable Wasm Hash": "Executable Wasm Hash", + "Expand view": "Expand view", "Extend To": "Extend To", "Failed to fetch your account balances": "Failed to fetch your account balances.", "FEDERATION ADDRESS": "FEDERATION ADDRESS", @@ -274,8 +279,7 @@ "Make sure it is a funded Stellar account and try again": "Make sure it is a funded Stellar account and try again.", "Make sure you have your 12 word backup phrase": "Make sure you have your 12 word backup phrase", "Make sure you have your current 12 words backup phrase before continuing": "Make sure you have your current 12 words backup phrase before continuing.", - "Manage Assets": "Manage Assets", - "Manage connected apps": "Manage connected apps", + "Manage assets": "Manage assets", "Manage network settings": "Manage network settings", "Master Weight": "Master Weight", "Max Amount A": "Max Amount A", @@ -324,6 +328,9 @@ "Not funded": "Not funded", "Not migrated": "Not migrated", "Not on your lists": "Not on your lists", + "Note that you will need to reload this tab to load any account changes that happen outside this session": { + " For your own safety, please close this window when you are done": "Note that you will need to reload this tab to load any account changes that happen outside this session. For your own safety, please close this window when you are done." + }, "Now, let’s create a new mnemonic phrase": "Now, let’s create a new mnemonic phrase", "Number of seconds that can pass before this transaction can no longer be accepted by the network": "Number of seconds that can pass before this transaction can no longer be accepted by the network", "Offer ID": "Offer ID", @@ -478,7 +485,6 @@ "This invocation authorizes the following transfers, please review the invocation tree and confirm that you want to proceed": "This invocation authorizes the following transfers, please review the invocation tree and confirm that you want to proceed.", "This is a relatively new asset": "This is a relatively new asset.", "This new backup phrase will be used for your new accounts": "This new backup phrase will be used for your new accounts.", - "This site has been scanned and verified": "This site has been scanned and verified", "This site was flagged as malicious": "This site was flagged as malicious", "This transaction could not be completed": "This transaction could not be completed.", "This transaction is expected to fail": "This transaction is expected to fail", @@ -516,7 +522,7 @@ "Verification": "Verification", "Verification with": "Verification with", "View on": "View on", - "View public key": "View public key", + "View options": "View options", "Wallet Address": "Wallet Address", "Wallet created successfully!": "Wallet created successfully!", "Want to add another account?": "Want to add another account?", @@ -530,6 +536,7 @@ "You are attempting to sign an arbitrary message": { " Please use extreme caution and understand the implications of signing this data": "You are attempting to sign an arbitrary message. Please use extreme caution and understand the implications of signing this data." }, + "You are in fullscreen mode": "You are in fullscreen mode", "You are interacting with data that may be using untested and changing schemas": { " Proceed at your own risk": "You are interacting with data that may be using untested and changing schemas. Proceed at your own risk." }, diff --git a/extension/src/popup/locales/pt/translation.json b/extension/src/popup/locales/pt/translation.json index 4e985eb834..b3d8475520 100644 --- a/extension/src/popup/locales/pt/translation.json +++ b/extension/src/popup/locales/pt/translation.json @@ -6,6 +6,7 @@ "A Stellar wallet for every website": "A Stellar wallet for every website", "About": "About", "Account": "Account", + "Account details": "Account details", "Account ID": "Account ID", "Account Migration": "Account Migration", "Account minimum balance is too low": "Account minimum balance is too low", @@ -103,6 +104,7 @@ "Confirm": "Confirm", "Confirm Data": "Confirm Data", "Confirm delete": "Confirm delete", + "Confirm Entry": "Confirm Entry", "Confirm password": "Confirm password", "Confirm removing Network": "Confirm removing Network", "Confirm Send": "Confirm Send", @@ -113,6 +115,7 @@ "Connect anyway": "Connect anyway", "Connect device to computer": "Connect device to computer", "Connect to domain without SSL certificate": "Connect to domain without SSL certificate", + "Connected apps": "Connected apps", "CONNECTION ERROR": "CONNECTION ERROR", "Connection Request": "Connection Request", "Continue": "Continue", @@ -124,6 +127,7 @@ "Copy address": "Copy address", "Create a new Stellar address": "Create a new Stellar address", "Create a password": "Create a password", + "Create a wallet": "Create a wallet", "Create Wallet": "Create Wallet", "Custom": "Custom", "data entries": "data entries", @@ -167,6 +171,7 @@ "Error code": "Error code", "Executable Type": "Executable Type", "Executable Wasm Hash": "Executable Wasm Hash", + "Expand view": "Expand view", "Extend To": "Extend To", "Failed to fetch your account balances": "Failed to fetch your account balances.", "FEDERATION ADDRESS": "FEDERATION ADDRESS", @@ -274,8 +279,7 @@ "Make sure it is a funded Stellar account and try again": "Make sure it is a funded Stellar account and try again.", "Make sure you have your 12 word backup phrase": "Make sure you have your 12 word backup phrase", "Make sure you have your current 12 words backup phrase before continuing": "Make sure you have your current 12 words backup phrase before continuing.", - "Manage Assets": "Manage Assets", - "Manage connected apps": "Manage connected apps", + "Manage assets": "Manage assets", "Manage network settings": "Manage network settings", "Master Weight": "Master Weight", "Max Amount A": "Max Amount A", @@ -324,6 +328,9 @@ "Not funded": "Not funded", "Not migrated": "Not migrated", "Not on your lists": "Not on your lists", + "Note that you will need to reload this tab to load any account changes that happen outside this session": { + " For your own safety, please close this window when you are done": "Note that you will need to reload this tab to load any account changes that happen outside this session. For your own safety, please close this window when you are done." + }, "Now, let’s create a new mnemonic phrase": "Now, let’s create a new mnemonic phrase", "Number of seconds that can pass before this transaction can no longer be accepted by the network": "Number of seconds that can pass before this transaction can no longer be accepted by the network", "Offer ID": "Offer ID", @@ -478,7 +485,6 @@ "This invocation authorizes the following transfers, please review the invocation tree and confirm that you want to proceed": "This invocation authorizes the following transfers, please review the invocation tree and confirm that you want to proceed.", "This is a relatively new asset": "This is a relatively new asset.", "This new backup phrase will be used for your new accounts": "This new backup phrase will be used for your new accounts.", - "This site has been scanned and verified": "This site has been scanned and verified", "This site was flagged as malicious": "This site was flagged as malicious", "This transaction could not be completed": "This transaction could not be completed.", "This transaction is expected to fail": "This transaction is expected to fail", @@ -516,7 +522,7 @@ "Verification": "Verification", "Verification with": "Verification with", "View on": "View on", - "View public key": "View public key", + "View options": "View options", "Wallet Address": "Wallet Address", "Wallet created successfully!": "Wallet created successfully!", "Want to add another account?": "Want to add another account?", @@ -530,6 +536,7 @@ "You are attempting to sign an arbitrary message": { " Please use extreme caution and understand the implications of signing this data": "You are attempting to sign an arbitrary message. Please use extreme caution and understand the implications of signing this data." }, + "You are in fullscreen mode": "You are in fullscreen mode", "You are interacting with data that may be using untested and changing schemas": { " Proceed at your own risk": "You are interacting with data that may be using untested and changing schemas. Proceed at your own risk." }, diff --git a/extension/src/popup/views/UnlockAccount/index.tsx b/extension/src/popup/views/UnlockAccount/index.tsx index ab571d8812..1895fc98d3 100644 --- a/extension/src/popup/views/UnlockAccount/index.tsx +++ b/extension/src/popup/views/UnlockAccount/index.tsx @@ -108,6 +108,18 @@ export const UnlockAccount = () => { {t("Import using account seed phrase")}
+
+ {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} + { + openTab(newTabHref(ROUTES.accountCreator)); + }} + > + {t("Create a wallet")} + +
); From 507fad880d6671fe1ee8ce0651da633126d2f156 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Mon, 18 Nov 2024 14:16:27 -0500 Subject: [PATCH 05/11] Feature/maintain password 2 (#1677) * maintain password when switching between accounts * Added translations * add test and update e2e token --- extension/e2e-tests/helpers/test-token.ts | 2 +- extension/e2e-tests/loadAccount.test.ts | 14 ++++++++- extension/src/background/ducks/session.ts | 11 +++++-- .../messageListener/popupMessageListener.ts | 29 ++++++++++++++----- .../account/AccountHeader/index.tsx | 1 + 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/extension/e2e-tests/helpers/test-token.ts b/extension/e2e-tests/helpers/test-token.ts index ce274d84fa..5f5b4e03d5 100644 --- a/extension/e2e-tests/helpers/test-token.ts +++ b/extension/e2e-tests/helpers/test-token.ts @@ -1,2 +1,2 @@ export const TEST_TOKEN_ADDRESS = - "CA5F3Q3KQWGG5J4U6OBCJEFVG4B5JVMHFRGQGMLNZXTEMO7YEO6UYMMD"; + "CCP33I36MH3QCJZWLWQYSVSHISOGYDUDW5VFW3F3JW3SWWIAPMP6OWF5"; diff --git a/extension/e2e-tests/loadAccount.test.ts b/extension/e2e-tests/loadAccount.test.ts index f989b05a26..c14da690dc 100644 --- a/extension/e2e-tests/loadAccount.test.ts +++ b/extension/e2e-tests/loadAccount.test.ts @@ -1,5 +1,5 @@ import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures"; -import { loginToTestAccount } from "./helpers/login"; +import { loginToTestAccount, loginAndFund } from "./helpers/login"; test("Load accounts on standalone network", async ({ page, extensionId }) => { test.slow(); @@ -27,3 +27,15 @@ test("Load accounts on standalone network", async ({ page, extensionId }) => { }); await expect(page.getByTestId("account-assets")).toContainText("XLM"); }); +test("Switches account with password prompt", async ({ page, extensionId }) => { + test.slow(); + await loginToTestAccount({ page, extensionId }); + await expect(page.getByTestId("account-assets")).toContainText("XLM"); + await page.getByTestId("AccountHeader__icon-btn").click(); + await page.getByText("Account 2").click(); + + await page.getByTestId("account-options-dropdown").click(); + await page.getByText("Manage Assets").click({ force: true }); + + await expect(page.getByText("Your assets")).toBeVisible(); +}); diff --git a/extension/src/background/ducks/session.ts b/extension/src/background/ducks/session.ts index 74234688f0..b9d4c13b17 100644 --- a/extension/src/background/ducks/session.ts +++ b/extension/src/background/ducks/session.ts @@ -62,7 +62,7 @@ interface UiData { interface AppData { privateKey?: string; - password?: string; + password: string; } export const sessionSlice = createSlice({ @@ -72,11 +72,12 @@ export const sessionSlice = createSlice({ reset: () => initialState, logOut: () => initialState, setActivePrivateKey: (state, action: { payload: AppData }) => { - const { privateKey = "" } = action.payload; + const { privateKey = "", password = "" } = action.payload; return { ...state, privateKey, + password, }; }, setMigratedMnemonicPhrase: ( @@ -90,7 +91,11 @@ export const sessionSlice = createSlice({ migratedMnemonicPhrase, }; }, - timeoutAccountAccess: (state) => ({ ...state, privateKey: "" }), + timeoutAccountAccess: (state) => ({ + ...state, + privateKey: "", + password: "", + }), updateAllAccountsAccountName: ( state, action: { payload: { updatedAccountName: string } }, diff --git a/extension/src/background/messageListener/popupMessageListener.ts b/extension/src/background/messageListener/popupMessageListener.ts index 2bb9d890ca..89229352ee 100644 --- a/extension/src/background/messageListener/popupMessageListener.ts +++ b/extension/src/background/messageListener/popupMessageListener.ts @@ -201,6 +201,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { bipPath: string; }) => { const mnemonicPhrase = mnemonicPhraseSelector(sessionStore.getState()); + const password = passwordSelector(sessionStore.getState()) || ""; let allAccounts = allAccountsSelector(sessionStore.getState()); const keyId = `${HW_PREFIX}${publicKey}`; @@ -245,7 +246,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { ); // an active hw account should not have an active private key - sessionStore.dispatch(setActivePrivateKey({ privateKey: "" })); + sessionStore.dispatch(setActivePrivateKey({ privateKey: "", password })); }; const _storeAccount = async ({ @@ -442,7 +443,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { sessionTimer.startSession(); sessionStore.dispatch( - setActivePrivateKey({ privateKey: keyPair.privateKey }), + setActivePrivateKey({ privateKey: keyPair.privateKey, password }), ); const currentState = sessionStore.getState(); @@ -494,7 +495,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { sessionTimer.startSession(); sessionStore.dispatch( - setActivePrivateKey({ privateKey: keyPair.privateKey }), + setActivePrivateKey({ privateKey: keyPair.privateKey, password }), ); const currentState = sessionStore.getState(); @@ -540,7 +541,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { }); sessionTimer.startSession(); - sessionStore.dispatch(setActivePrivateKey({ privateKey })); + sessionStore.dispatch(setActivePrivateKey({ privateKey, password })); const currentState = sessionStore.getState(); @@ -572,7 +573,19 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { const { publicKey } = request; await _activatePublicKey({ publicKey }); - sessionStore.dispatch(timeoutAccountAccess()); + const password = passwordSelector(sessionStore.getState()) || ""; + const keyID = (await localStore.getItem(KEY_ID)) || ""; + + try { + const wallet = await _unlockKeystore({ keyID, password }); + const privateKey = wallet.privateKey; + + if (!(await getIsHardwareWalletActive())) { + sessionStore.dispatch(setActivePrivateKey({ privateKey, password })); + } + } catch (e) { + console.error(e); + } const currentState = sessionStore.getState(); @@ -850,7 +863,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { // start the timer now that we have active private key sessionTimer.startSession(); sessionStore.dispatch( - setActivePrivateKey({ privateKey: wallet.getSecret(0) }), + setActivePrivateKey({ privateKey: wallet.getSecret(0), password }), ); } @@ -999,7 +1012,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { sessionTimer.startSession(); if (!(await getIsHardwareWalletActive())) { sessionStore.dispatch( - setActivePrivateKey({ privateKey: activePrivateKey }), + setActivePrivateKey({ privateKey: activePrivateKey, password }), ); } @@ -1680,7 +1693,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { sessionTimer.startSession(); sessionStore.dispatch( - setActivePrivateKey({ privateKey: newWallet.getSecret(0) }), + setActivePrivateKey({ privateKey: newWallet.getSecret(0), password }), ); } diff --git a/extension/src/popup/components/account/AccountHeader/index.tsx b/extension/src/popup/components/account/AccountHeader/index.tsx index 17b9ef7a3a..f46ce3010d 100644 --- a/extension/src/popup/components/account/AccountHeader/index.tsx +++ b/extension/src/popup/components/account/AccountHeader/index.tsx @@ -65,6 +65,7 @@ export const AccountHeader = ({ leftContent={
setIsDropdownOpen(!isDropdownOpen)} > Date: Mon, 18 Nov 2024 16:26:59 -0500 Subject: [PATCH 06/11] enable e2e tests in CI and add cleanup tasks (#1684) * enable e2e tests in CI and add cleanup tasks * fix asset code change --- .github/workflows/runTests.yml | 6 +++--- .husky/e2eTests.sh | 3 --- .husky/pre-push | 3 +-- extension/e2e-tests/addAsset.test.ts | 22 +++++++++++++++++++++- extension/e2e-tests/sendPayment.test.ts | 20 ++++++++++++++++++++ 5 files changed, 45 insertions(+), 9 deletions(-) delete mode 100644 .husky/e2eTests.sh diff --git a/.github/workflows/runTests.yml b/.github/workflows/runTests.yml index 999fd0d2fd..a8d69b14c0 100644 --- a/.github/workflows/runTests.yml +++ b/.github/workflows/runTests.yml @@ -6,16 +6,16 @@ jobs: test-ci: name: test timeout-minutes: 20 - runs-on: ubuntu-latest - container: - image: mcr.microsoft.com/playwright:v1.45.3-jammy + runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 21 - run: npm install -g yarn && yarn + - run: npx playwright install --with-deps chromium - run: yarn setup - run: yarn build:freighter-api - run: yarn build:extension - run: yarn test:ci + - run: yarn test:e2e diff --git a/.husky/e2eTests.sh b/.husky/e2eTests.sh deleted file mode 100644 index 9b6892836d..0000000000 --- a/.husky/e2eTests.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -yarn test:e2e \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push index 03650a2769..0f0142aed4 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,5 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -bash ./.husky/addTranslations.sh -bash ./.husky/e2eTests.sh +bash ./.husky/addTranslations.sh \ No newline at end of file diff --git a/extension/e2e-tests/addAsset.test.ts b/extension/e2e-tests/addAsset.test.ts index 120f92f24b..e59f4a3ff1 100644 --- a/extension/e2e-tests/addAsset.test.ts +++ b/extension/e2e-tests/addAsset.test.ts @@ -19,7 +19,7 @@ test("Adding unverified Soroban token", async ({ page, extensionId }) => { await expect(page.getByTestId("asset-notification")).toHaveText( "Not on your listsFreighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", ); - await expect(page.getByTestId("ManageAssetCode")).toHaveText("E2E"); + await expect(page.getByTestId("ManageAssetCode")).toHaveText("E2E Token"); await expect(page.getByTestId("ManageAssetRowButton")).toHaveText("Add"); await page.getByTestId("ManageAssetRowButton").click({ force: true }); @@ -81,3 +81,23 @@ test("Adding Soroban verified token", async ({ page, extensionId }) => { timeout: 30000, }); }); +test.afterAll(async ({ page, extensionId }) => { + if ( + test.info().status !== test.info().expectedStatus && + test.info().title === "Adding Soroban verified token" + ) { + // remove trustline in cleanup if Adding Soroban verified token test failed + test.slow(); + await loginToTestAccount({ page, extensionId }); + + await page.getByText("Manage Assets").click({ force: true }); + await page.getByPlaceholder("Enter password").fill(PASSWORD); + await page.getByText("Log In").click({ force: true }); + + await page.getByTestId("ManageAssetRowButton__ellipsis-USDC").click(); + await page.getByText("Remove asset").click(); + await expect(page.getByTestId("account-view")).toBeVisible({ + timeout: 30000, + }); + } +}); diff --git a/extension/e2e-tests/sendPayment.test.ts b/extension/e2e-tests/sendPayment.test.ts index e7d4728cd6..db4ce605e2 100644 --- a/extension/e2e-tests/sendPayment.test.ts +++ b/extension/e2e-tests/sendPayment.test.ts @@ -282,3 +282,23 @@ test("Send token payment to C address", async ({ page, extensionId }) => { await expect(page.getByText("Sent E2E")).toBeVisible(); await expect(page.getByTestId("asset-amount")).toContainText(".001 E2E"); }); +test.afterAll(async ({ page, extensionId }) => { + if ( + test.info().status !== test.info().expectedStatus && + test.info().title === "Send SAC to C address" + ) { + // remove trustline in cleanup if Send SAC to C address test failed + test.slow(); + await loginToTestAccount({ page, extensionId }); + + await page.getByText("Manage Assets").click({ force: true }); + await page.getByPlaceholder("Enter password").fill(PASSWORD); + await page.getByText("Log In").click({ force: true }); + + await page.getByTestId("ManageAssetRowButton__ellipsis-USDC").click(); + await page.getByText("Remove asset").click(); + await expect(page.getByTestId("account-view")).toBeVisible({ + timeout: 30000, + }); + } +}); From 56293faf98c427e98634d6943d82e62d5e37fe15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Marcos=20Goulart?= <3228151+CassioMG@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:04:33 -0300 Subject: [PATCH 07/11] Redesign Connected apps (#1686) * Added translations * Add 'Disconnect all' button * Added translations * Display favicon --- extension/src/popup/assets/icon-x-remove.svg | 3 ++ .../basics/buttons/RemoveButton/index.tsx | 14 ++++++++ .../src/popup/locales/en/translation.json | 2 ++ .../src/popup/locales/pt/translation.json | 2 ++ .../popup/views/ManageConnectedApps/index.tsx | 36 +++++++++++++------ .../views/ManageConnectedApps/styles.scss | 11 ++++-- 6 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 extension/src/popup/assets/icon-x-remove.svg create mode 100644 extension/src/popup/basics/buttons/RemoveButton/index.tsx diff --git a/extension/src/popup/assets/icon-x-remove.svg b/extension/src/popup/assets/icon-x-remove.svg new file mode 100644 index 0000000000..f95cea6b4d --- /dev/null +++ b/extension/src/popup/assets/icon-x-remove.svg @@ -0,0 +1,3 @@ + + + diff --git a/extension/src/popup/basics/buttons/RemoveButton/index.tsx b/extension/src/popup/basics/buttons/RemoveButton/index.tsx new file mode 100644 index 0000000000..578828f7e6 --- /dev/null +++ b/extension/src/popup/basics/buttons/RemoveButton/index.tsx @@ -0,0 +1,14 @@ +import { Button } from "@stellar/design-system"; +import React from "react"; + +import IconXRemove from "popup/assets/icon-x-remove.svg"; + +interface RemoveButtonProps { + onClick: () => void; +} + +export const RemoveButton = ({ onClick }: RemoveButtonProps) => ( + +); diff --git a/extension/src/popup/locales/en/translation.json b/extension/src/popup/locales/en/translation.json index 08110bea3f..7989ccd078 100644 --- a/extension/src/popup/locales/en/translation.json +++ b/extension/src/popup/locales/en/translation.json @@ -145,6 +145,7 @@ "Different source and destination assets": "Different source and destination assets", "digits after the decimal allowed": "digits after the decimal allowed", "Disabled": "Disabled", + "Disconnect all": "Disconnect all", "Done": "Done", "Double check the domain name": { " If it is incorrect in any way, do not share your public key and contact the site administrator via a verified email or social media account to confirm that this domain is correct": "Double check the domain name. If it is incorrect in any way, do not share your public key and contact the site administrator via a verified email or social media account to confirm that this domain is correct." @@ -321,6 +322,7 @@ "New asset": "New asset", "New password": "New password", "Next": "Next", + "No connected apps found": "No connected apps found", "no path found": "no path found", "No transactions to show": "No transactions to show", "None": "None", diff --git a/extension/src/popup/locales/pt/translation.json b/extension/src/popup/locales/pt/translation.json index b3d8475520..b29b6be375 100644 --- a/extension/src/popup/locales/pt/translation.json +++ b/extension/src/popup/locales/pt/translation.json @@ -145,6 +145,7 @@ "Different source and destination assets": "Different source and destination assets", "digits after the decimal allowed": "digits after the decimal allowed", "Disabled": "Disabled", + "Disconnect all": "Disconnect all", "Done": "Done", "Double check the domain name": { " If it is incorrect in any way, do not share your public key and contact the site administrator via a verified email or social media account to confirm that this domain is correct": "Double check the domain name. If it is incorrect in any way, do not share your public key and contact the site administrator via a verified email or social media account to confirm that this domain is correct." @@ -321,6 +322,7 @@ "New asset": "New asset", "New password": "New password", "Next": "Next", + "No connected apps found": "No connected apps found", "no path found": "no path found", "No transactions to show": "No transactions to show", "None": "None", diff --git a/extension/src/popup/views/ManageConnectedApps/index.tsx b/extension/src/popup/views/ManageConnectedApps/index.tsx index e12f27ba99..511eb61a5c 100644 --- a/extension/src/popup/views/ManageConnectedApps/index.tsx +++ b/extension/src/popup/views/ManageConnectedApps/index.tsx @@ -1,18 +1,19 @@ +import { Button } from "@stellar/design-system"; import React from "react"; - -import { useSelector, useDispatch } from "react-redux"; import { useTranslation } from "react-i18next"; -import { PillButton } from "popup/basics/buttons/PillButton"; +import { useSelector, useDispatch } from "react-redux"; import { saveAllowList, settingsSelector } from "popup/ducks/settings"; import { SubviewHeader } from "popup/components/SubviewHeader"; +import { PunycodedDomain } from "popup/components/PunycodedDomain"; import { View } from "popup/basics/layout/View"; +import { RemoveButton } from "popup/basics/buttons/RemoveButton"; import "./styles.scss"; export const ManageConnectedApps = () => { - const { t } = useTranslation(); const dispatch = useDispatch(); + const { t } = useTranslation(); const { allowList } = useSelector(settingsSelector); const handleRemove = (domainToRemove: string) => { @@ -25,14 +26,18 @@ export const ManageConnectedApps = () => { ); }; + const handleRemoveAll = () => { + dispatch(saveAllowList({ allowList: [] })); + }; + return ( - +
{allowList.length ? (
-
+
{allowList.map( (allowedDomain) => allowedDomain && ( @@ -40,18 +45,27 @@ export const ManageConnectedApps = () => { className="ManageConnectedApps__row" key={allowedDomain} > -
{allowedDomain}
- handleRemove(allowedDomain)}> - {t("Remove")} - + + handleRemove(allowedDomain)} + />
), )}
+ +
) : (
- No connected apps found + {t("No connected apps found")}
)}
diff --git a/extension/src/popup/views/ManageConnectedApps/styles.scss b/extension/src/popup/views/ManageConnectedApps/styles.scss index e253dbdd00..84ef344c59 100644 --- a/extension/src/popup/views/ManageConnectedApps/styles.scss +++ b/extension/src/popup/views/ManageConnectedApps/styles.scss @@ -3,13 +3,20 @@ flex-direction: column; height: 100%; - &__content { + &__wrapper { display: flex; flex-direction: column; - gap: 1.5rem; + height: 100%; + gap: 2rem; justify-content: space-between; } + &__list { + display: flex; + flex-direction: column; + gap: 2rem; + } + &__row { display: flex; align-items: center; From 909b3588bcff477c60ac14236ef865b7b5fabd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Marcos=20Goulart?= <3228151+CassioMG@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:47:32 -0300 Subject: [PATCH 08/11] Skip password when creating a new address (#1685) * Skip password when creating new address * Better copy for Enter Password screen * Added translations * Allow user to input password again in case of error + remove spinner while redirecting --- @shared/api/internal.ts | 2 +- .../messageListener/popupMessageListener.ts | 8 ++- extension/src/popup/ducks/accountServices.ts | 2 +- .../src/popup/locales/en/translation.json | 2 +- .../src/popup/locales/pt/translation.json | 2 +- .../views/AddAccount/AddAccount/index.tsx | 65 +++++++++++++------ 6 files changed, 55 insertions(+), 26 deletions(-) diff --git a/@shared/api/internal.ts b/@shared/api/internal.ts index 1c62e58cf1..b244bb19cb 100644 --- a/@shared/api/internal.ts +++ b/@shared/api/internal.ts @@ -112,7 +112,7 @@ export const fundAccount = async (publicKey: string): Promise => { }; export const addAccount = async ( - password: string, + password: string = "", ): Promise<{ publicKey: string; allAccounts: Array; diff --git a/extension/src/background/messageListener/popupMessageListener.ts b/extension/src/background/messageListener/popupMessageListener.ts index 89229352ee..a5fda4e3cf 100644 --- a/extension/src/background/messageListener/popupMessageListener.ts +++ b/extension/src/background/messageListener/popupMessageListener.ts @@ -456,7 +456,13 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { }; const addAccount = async () => { - const { password } = request; + let password = request.password; + // in case a password is not provided, let's try using the value saved + // in current session store + if (!password) { + password = passwordSelector(sessionStore.getState()) || ""; + } + const mnemonicPhrase = mnemonicPhraseSelector(sessionStore.getState()); if (!mnemonicPhrase) { diff --git a/extension/src/popup/ducks/accountServices.ts b/extension/src/popup/ducks/accountServices.ts index d8a499b0a6..e89406ff11 100644 --- a/extension/src/popup/ducks/accountServices.ts +++ b/extension/src/popup/ducks/accountServices.ts @@ -77,7 +77,7 @@ export const addAccount = createAsyncThunk< { publicKey: string; allAccounts: Account[]; hasPrivateKey: boolean }, string, { rejectValue: ErrorMessage } ->("auth/addAccount", async (password, thunkApi) => { +>("auth/addAccount", async (password = "", thunkApi) => { let res = { publicKey: "", allAccounts: [] as Account[], diff --git a/extension/src/popup/locales/en/translation.json b/extension/src/popup/locales/en/translation.json index 7989ccd078..5b2454ff39 100644 --- a/extension/src/popup/locales/en/translation.json +++ b/extension/src/popup/locales/en/translation.json @@ -25,7 +25,6 @@ "Add list": "Add list", "Add manually": "Add manually", "Add network": "Add network", - "Add New Address": "Add New Address", "Add new list": "Add new list", "Address": "Address", "Addresses are uppercase and begin with letters “G“, “M“, or “C“": "Addresses are uppercase and begin with letters “G“, “M“, or “C“.", @@ -128,6 +127,7 @@ "Create a new Stellar address": "Create a new Stellar address", "Create a password": "Create a password", "Create a wallet": "Create a wallet", + "Create New Address": "Create New Address", "Create Wallet": "Create Wallet", "Custom": "Custom", "data entries": "data entries", diff --git a/extension/src/popup/locales/pt/translation.json b/extension/src/popup/locales/pt/translation.json index b29b6be375..34e947d9d6 100644 --- a/extension/src/popup/locales/pt/translation.json +++ b/extension/src/popup/locales/pt/translation.json @@ -25,7 +25,6 @@ "Add list": "Add list", "Add manually": "Add manually", "Add network": "Add network", - "Add New Address": "Add New Address", "Add new list": "Add new list", "Address": "Address", "Addresses are uppercase and begin with letters “G“, “M“, or “C“": "Addresses are uppercase and begin with letters “G“, “M“, or “C“.", @@ -128,6 +127,7 @@ "Create a new Stellar address": "Create a new Stellar address", "Create a password": "Create a password", "Create a wallet": "Create a wallet", + "Create New Address": "Create New Address", "Create Wallet": "Create Wallet", "Custom": "Custom", "data entries": "data entries", diff --git a/extension/src/popup/views/AddAccount/AddAccount/index.tsx b/extension/src/popup/views/AddAccount/AddAccount/index.tsx index 8d670bbef2..ab03603267 100644 --- a/extension/src/popup/views/AddAccount/AddAccount/index.tsx +++ b/extension/src/popup/views/AddAccount/AddAccount/index.tsx @@ -1,24 +1,23 @@ -import React, { useEffect } from "react"; -import { useDispatch, useSelector } from "react-redux"; import { Button, Input } from "@stellar/design-system"; import { Field, Form, Formik, FieldProps } from "formik"; +import React, { useCallback, useEffect } from "react"; import { useTranslation } from "react-i18next"; +import { useDispatch, useSelector } from "react-redux"; -import { ROUTES } from "popup/constants/routes"; -import { METRIC_NAMES } from "popup/constants/metricsNames"; -import { AppDispatch } from "popup/App"; -import { navigateTo } from "popup/helpers/navigate"; import { emitMetric } from "helpers/metrics"; +import { AppDispatch } from "popup/App"; import { FormRows } from "popup/basics/Forms"; import { View } from "popup/basics/layout/View"; - +import { ROUTES } from "popup/constants/routes"; +import { METRIC_NAMES } from "popup/constants/metricsNames"; +import { navigateTo } from "popup/helpers/navigate"; import { SubviewHeader } from "popup/components/SubviewHeader"; - import { addAccount, authErrorSelector, clearApiError, + hasPrivateKeySelector, } from "popup/ducks/accountServices"; import "./styles.scss"; @@ -35,30 +34,54 @@ export const AddAccount = () => { const { t } = useTranslation(); const dispatch: AppDispatch = useDispatch(); const authError = useSelector(authErrorSelector); + const hasPrivateKey = useSelector(hasPrivateKeySelector); - const handleSubmit = async (values: FormValues) => { - const { password } = values; + // In case a password is not provided here popupMessageListener/addAccount + // will try to use the existing password value saved in the session store + const handleAddAccount = useCallback( + async (password: string = "") => { + const res = await dispatch(addAccount(password)); - const res = await dispatch(addAccount(password)); + if (addAccount.fulfilled.match(res)) { + emitMetric(METRIC_NAMES.accountScreenAddAccount, { + // eslint-disable-next-line @typescript-eslint/naming-convention + number_of_accounts: res.payload.allAccounts.length, + }); + navigateTo(ROUTES.account); + } + }, + [dispatch], + ); - if (addAccount.fulfilled.match(res)) { - emitMetric(METRIC_NAMES.accountScreenAddAccount, { - // eslint-disable-next-line @typescript-eslint/naming-convention - number_of_accounts: res.payload.allAccounts.length, - }); - navigateTo(ROUTES.account); - } + const handleEnterPassword = async (values: FormValues) => { + await handleAddAccount(values.password); }; + // If we have a private key we can assume the user password is also saved in + // the current session store, so no need to ask for it again + useEffect(() => { + if (hasPrivateKey) { + handleAddAccount(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect( () => () => dispatch(clearApiError()) as unknown as void, [dispatch], ); + // No need to ask for password if it's already stored, so let's just briefly + // wait until user is navigated to the next screen + if (hasPrivateKey && !authError) { + return null; + } + + // Ask for user password in case it's not saved in current session store return ( - - + + {({ dirty, isSubmitting, isValid, errors, touched }) => (
@@ -92,7 +115,7 @@ export const AddAccount = () => { isLoading={isSubmitting} type="submit" > - {t("Add New Address")} + {t("Create New Address")} From 1f93869531b60692e8f462d9b1774b08d7a828f1 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Wed, 20 Nov 2024 12:31:31 -0500 Subject: [PATCH 09/11] limit how many instances of the test can run at a time to prevent collisions (#1690) --- .github/workflows/runTests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/runTests.yml b/.github/workflows/runTests.yml index a8d69b14c0..2f9dac4e0f 100644 --- a/.github/workflows/runTests.yml +++ b/.github/workflows/runTests.yml @@ -2,6 +2,8 @@ name: Run Tests env: INDEXER_URL: ${{ secrets.INDEXER_URL }} on: [pull_request] +concurrency: + group: ${{ github.workflow }} jobs: test-ci: name: test From aad0f96c125e8dc3f570a95faafb265d0452a8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Marcos=20Goulart?= <3228151+CassioMG@users.noreply.github.com> Date: Wed, 20 Nov 2024 19:17:35 -0300 Subject: [PATCH 10/11] Prevent infinity spinner when switching to same account (#1692) --- .../components/identicons/AccountListIdenticon/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extension/src/popup/components/identicons/AccountListIdenticon/index.tsx b/extension/src/popup/components/identicons/AccountListIdenticon/index.tsx index fad51c0119..e2a371ddb8 100644 --- a/extension/src/popup/components/identicons/AccountListIdenticon/index.tsx +++ b/extension/src/popup/components/identicons/AccountListIdenticon/index.tsx @@ -32,7 +32,9 @@ export const AccountListIdenticon = ({ const shortPublicKey = truncatedPublicKey(publicKey); const handleMakeAccountActive = () => { - if (setLoading) { + // If this account is already active (selected) we don't need to load any + // more stuff, so let's just collapse the dropdown in this case + if (!active && setLoading) { setLoading(true); } From 7e5655c03dbf4b589025cc7e42cc63e8e3b7e3dd Mon Sep 17 00:00:00 2001 From: aristides Date: Thu, 21 Nov 2024 11:17:55 -0700 Subject: [PATCH 11/11] splits buildAndSimulateSoroswapTx into flows for custom networks, uses simulate-tx API for other networks (#1694) --- @shared/api/internal.ts | 27 +++++++++++ extension/src/popup/helpers/sorobanSwap.ts | 54 ++++++++++++++-------- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/@shared/api/internal.ts b/@shared/api/internal.ts index b244bb19cb..038767a284 100644 --- a/@shared/api/internal.ts +++ b/@shared/api/internal.ts @@ -1496,6 +1496,33 @@ export const simulateTokenTransfer = async (args: { }; }; +export const simulateTransaction = async (args: { + xdr: string; + networkDetails: NetworkDetails; +}) => { + const { xdr, networkDetails } = args; + const options = { + method: "POST", + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + "Content-Type": "application/json", + }, + body: JSON.stringify({ + xdr, + // eslint-disable-next-line @typescript-eslint/naming-convention + network_url: networkDetails.sorobanRpcUrl, + // eslint-disable-next-line @typescript-eslint/naming-convention + network_passphrase: networkDetails.networkPassphrase, + }), + }; + const res = await fetch(`${INDEXER_URL}/simulate-tx`, options); + const response = await res.json(); + return { + ok: res.ok, + response, + }; +}; + export const saveIsBlockaidAnnounced = async ({ isBlockaidAnnounced, }: { diff --git a/extension/src/popup/helpers/sorobanSwap.ts b/extension/src/popup/helpers/sorobanSwap.ts index 65cc8db178..ce7b3a7708 100644 --- a/extension/src/popup/helpers/sorobanSwap.ts +++ b/extension/src/popup/helpers/sorobanSwap.ts @@ -10,9 +10,9 @@ import { import BigNumber from "bignumber.js"; import { NetworkDetails } from "@shared/constants/stellar"; -import { getSdk } from "@shared/helpers/stellar"; +import { getSdk, isCustomNetwork } from "@shared/helpers/stellar"; import { stellarSdkServer } from "@shared/api/helpers/stellarSdkServer"; -import { getTokenDetails } from "@shared/api/internal"; +import { getTokenDetails, simulateTransaction } from "@shared/api/internal"; import { SoroswapToken } from "@shared/api/types"; import { buildSorobanServer } from "@shared/helpers/soroban/server"; import { isTestnet, xlmToStroop } from "helpers/stellar"; @@ -290,26 +290,42 @@ export const buildAndSimulateSoroswapTx = async ({ } const builtTx = tx.build(); - // Now we can simulate and see if we have any issues - const simulationTransaction = await sorobanServer.simulateTransaction( - builtTx, - ); + if (isCustomNetwork(networkDetails)) { + // Now we can simulate and see if we have any issues + const simulationTransaction = await sorobanServer.simulateTransaction( + builtTx, + ); + + // If the simulation response is valid, we can prepare the transaction to be submitted to the network + // This is the transaction the user will sign and then submit to complete the swap + const preparedTransaction = Sdk.SorobanRpc.assembleTransaction( + builtTx, + simulationTransaction, + ) + .build() + .toXDR(); + + if (Sdk.SorobanRpc.Api.isSimulationError(simulationTransaction)) { + throw new Error(simulationTransaction.error); + } + + return { + simulationTransaction, + preparedTransaction, + }; + } + + const { ok, response } = await simulateTransaction({ + xdr: builtTx.toXDR(), + networkDetails, + }); - // If the simulation response is valid, we can prepare the transaction to be submitted to the network - // This is the transaction the user will sign and then submit to complete the swap - const preparedTransaction = Sdk.SorobanRpc.assembleTransaction( - builtTx, - simulationTransaction, - ) - .build() - .toXDR(); - - if (Sdk.SorobanRpc.Api.isSimulationError(simulationTransaction)) { - throw new Error(simulationTransaction.error); + if (!ok) { + throw new Error(response as string); } return { - simulationTransaction, - preparedTransaction, + preparedTransaction: response.preparedTransaction, + simulationTransaction: response.simulationResponse, }; };