From dec3b18af8087fbe1db1eb4daa8a4b8a0080e2b5 Mon Sep 17 00:00:00 2001 From: seaona <54408225+seaona@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:57:10 +0100 Subject: [PATCH 1/4] test: disable `it` failing block for ppom malicious signature (#21627) Disabling e2e test for malicious signatures, until fixed --- test/e2e/tests/ppom-blockaid-alert.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/tests/ppom-blockaid-alert.spec.js b/test/e2e/tests/ppom-blockaid-alert.spec.js index 1d7fbefda3a5..218c9728c105 100644 --- a/test/e2e/tests/ppom-blockaid-alert.spec.js +++ b/test/e2e/tests/ppom-blockaid-alert.spec.js @@ -213,7 +213,8 @@ describe('Confirmation Security Alert - Blockaid @no-mmi', function () { * 'malicious_domain'. Some other tests are found in other files: * e.g. test/e2e/flask/ppom-blockaid-alert-.spec.js */ - it('should show security alerts for malicious requests', async function () { + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('should show security alerts for malicious requests', async function () { await withFixtures( { dapp: true, From 51dbd52016d7932c4a2e6c892fbbe88d698c8bde Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 31 Oct 2023 20:59:54 +0100 Subject: [PATCH 2/4] Fix snaps website link pointing to the wrong URL (#21619) Fixes the snaps website link pointing to the wrong URL. Testing steps: 1. Install https://snaps.metamask.io/snap/npm/tezos-metamask-snap/ 2. Click website link in snap settings 3. See that you are navigated to https://metamask.tezos.com/ --- .../snaps/snap-authorship-expanded/snap-authorship-expanded.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/app/snaps/snap-authorship-expanded/snap-authorship-expanded.js b/ui/components/app/snaps/snap-authorship-expanded/snap-authorship-expanded.js index 8b7b6384ff71..45c31963d619 100644 --- a/ui/components/app/snaps/snap-authorship-expanded/snap-authorship-expanded.js +++ b/ui/components/app/snaps/snap-authorship-expanded/snap-authorship-expanded.js @@ -178,7 +178,7 @@ const SnapAuthorshipExpanded = ({ snapId, className, snap }) => { alignItems={AlignItems.flexEnd} > From d9bada9b5fb945c24faa67f5aa574061b3c4b614 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 31 Oct 2023 15:21:32 -0500 Subject: [PATCH 3/4] UX: Multichain: Send Flow: Recipient: Address Book (#21530) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR brings the address book and contact + recent transactions search to the recipient input. ## **Related issues** Fixes: https://github.com/MetaMask/MetaMask-planning/issues/1562 ## **Manual testing steps** ### 1. "Contacts" 1. Click any "Contacts" item 2. See the contact populated in the "To" field ### 2. "Contacts Search" 1. Search for a contact that lives in your address book 2. See that contact narrowed down in the contacts list ### 3. Domain Resolution / "Confusable" 1. In the "To" field, type `metamask.eth` 2. See on item display under "To", and it's the `metamask.eth` item with red `m`'s (`Confusable` component) ## **Screenshots/Recordings** ### **Before** (Wasn't there) ### **After** https://github.com/MetaMask/metamask-extension/assets/46655/cfcca367-4e52-4af9-a243-3d84cdaaed69 Note: I know that the Address Book item doesn't look the same as the Your Accounts item -- that will be fixed in https://github.com/MetaMask/MetaMask-planning/issues/1566 ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've clearly explained what problem this PR is solving and how it is solved. - [ ] I've linked related issues - [ ] I've included manual testing steps - [ ] I've included screenshots/recordings if applicable - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). - [ ] I’ve properly set the pull request status: - [ ] In case it's not yet "ready for review", I've set it to "draft". - [ ] In case it's "ready for review", I've changed it from "draft" to "non-draft". ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../send/__snapshots__/send.test.js.snap | 1053 +++++++++-------- .../pages/send/components/address-book.tsx | 115 ++ .../multichain/pages/send/components/index.ts | 2 + .../pages/send/components/recipient.tsx | 125 ++ ui/components/multichain/pages/send/send.js | 4 +- 5 files changed, 821 insertions(+), 478 deletions(-) create mode 100644 ui/components/multichain/pages/send/components/address-book.tsx create mode 100644 ui/components/multichain/pages/send/components/recipient.tsx diff --git a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap index aef3be1a0431..9878dd30d98f 100644 --- a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap +++ b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap @@ -173,619 +173,720 @@ exports[`SendPage render renders correctly 1`] = `
- -
-

- 0x0DCD5...3E7bc -

-
-
- - 966.988 - - - ETH - + + + + + +
-
- - - - - - - - - -
-
-

- 0xeB9e6...64823 -

-
-
- - 0 - - - ETH - + Imported +

-

- Imported -

-
- - - - + +
+
-

- 0xca8f1...Cf281 -

-
+ class="send__select-recipient-wrapper__recent-group-wrapper" + />
- - 0 - - +
+
+
+
+
+ + + + + +
+
+
+
- ETH - +

+ Address Book Account 1 +

+

+ 0xc42e...8813 +

+
diff --git a/ui/components/multichain/pages/send/components/address-book.tsx b/ui/components/multichain/pages/send/components/address-book.tsx new file mode 100644 index 000000000000..1b8ba9c8fca8 --- /dev/null +++ b/ui/components/multichain/pages/send/components/address-book.tsx @@ -0,0 +1,115 @@ +import React, { useContext } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import Fuse from 'fuse.js'; +import { Label } from '../../../../component-library'; +import { I18nContext } from '../../../../../contexts/i18n'; +import ContactList from '../../../../app/contact-list'; +import { + getAddressBook, + getCurrentNetworkTransactions, +} from '../../../../../selectors'; +import { + addHistoryEntry, + getRecipientUserInput, + updateRecipient, + updateRecipientUserInput, +} from '../../../../../ducks/send'; +import { SendPageRow } from '.'; + +export const SendPageAddressBook = () => { + const t = useContext(I18nContext); + const dispatch = useDispatch(); + + const addressBook = useSelector(getAddressBook); + const contacts = addressBook.filter(({ name }) => Boolean(name)); + const currentNetworkTransactions = useSelector(getCurrentNetworkTransactions); + + const txList = [...currentNetworkTransactions].reverse(); + const nonContacts = addressBook + .filter(({ name }) => !name) + .map((nonContact) => { + const nonContactTx = txList.find( + (transaction) => + transaction.txParams.to === nonContact.address.toLowerCase(), + ); + return { ...nonContact, timestamp: nonContactTx?.time }; + }); + + const userInput = useSelector(getRecipientUserInput); + const contactFuse = new Fuse(contacts, { + shouldSort: true, + threshold: 0.45, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: [ + { name: 'name', weight: 0.5 }, + { name: 'address', weight: 0.5 }, + ], + }); + + const recentFuse = new Fuse(nonContacts, { + shouldSort: true, + threshold: 0.45, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: [{ name: 'address', weight: 0.5 }], + }); + + const searchForContacts = () => { + if (userInput) { + contactFuse.setCollection(contacts); + return contactFuse.search(userInput); + } + + return contacts; + }; + + const searchForRecents = () => { + if (userInput) { + recentFuse.setCollection(nonContacts); + return recentFuse.search(userInput); + } + + return nonContacts; + }; + + const selectRecipient = ( + address = '', + nickname = '', + type = 'user input', + ) => { + dispatch( + addHistoryEntry( + `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`, + ), + ); + dispatch(updateRecipient({ address, nickname })); + dispatch(updateRecipientUserInput(address)); + }; + + return ( + + {addressBook.length ? ( + <> + + { + selectRecipient( + address, + name, + `${name ? 'contact' : 'recent'} list`, + ); + }} + /> + + ) : null} + + ); +}; diff --git a/ui/components/multichain/pages/send/components/index.ts b/ui/components/multichain/pages/send/components/index.ts index 7c3c3ffecb85..f7413bfde7c7 100644 --- a/ui/components/multichain/pages/send/components/index.ts +++ b/ui/components/multichain/pages/send/components/index.ts @@ -3,3 +3,5 @@ export { SendPageAccountPicker } from './account-picker'; export { SendPageNetworkPicker } from './network-picker'; export { SendPageYourAccount } from './your-accounts'; export { SendPageRecipientInput } from './recipient-input'; +export { SendPageAddressBook } from './address-book'; +export { SendPageRecipient } from './recipient'; diff --git a/ui/components/multichain/pages/send/components/recipient.tsx b/ui/components/multichain/pages/send/components/recipient.tsx new file mode 100644 index 000000000000..e00c25ddf7dd --- /dev/null +++ b/ui/components/multichain/pages/send/components/recipient.tsx @@ -0,0 +1,125 @@ +import React, { useContext } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { I18nContext } from '../../../../../contexts/i18n'; +import { + addHistoryEntry, + getRecipient, + getRecipientUserInput, + updateRecipient, + updateRecipientUserInput, +} from '../../../../../ducks/send'; +import { + getDomainError, + getDomainResolution, + getDomainWarning, +} from '../../../../../ducks/domains'; +import { + BannerAlert, + BannerAlertSeverity, + Box, +} from '../../../../component-library'; +import { getAddressBookEntry } from '../../../../../selectors'; +import Identicon from '../../../../ui/identicon'; +import Confusable from '../../../../ui/confusable'; +import { ellipsify } from '../../../../../pages/send/send.utils'; +import { SendPageAddressBook, SendPageRow, SendPageYourAccount } from '.'; + +const renderExplicitAddress = ( + address: string, + nickname: string, + type: string, + dispatch: any, +) => { + return ( +
{ + dispatch( + addHistoryEntry( + `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`, + ), + ); + dispatch(updateRecipient({ address, nickname })); + dispatch(updateRecipientUserInput(address)); + }} + > + +
+
+ {nickname ? : ellipsify(address)} +
+ {nickname && ( +
+ {ellipsify(address)} +
+ )} +
+
+ ); +}; + +export const SendPageRecipient = () => { + const t = useContext(I18nContext); + const dispatch = useDispatch(); + + const recipient = useSelector(getRecipient); + const userInput = useSelector(getRecipientUserInput); + + const domainResolution = useSelector(getDomainResolution); + const domainError = useSelector(getDomainError); + const domainWarning = useSelector(getDomainWarning); + + let addressBookEntryName = ''; + const entry = useSelector((state) => + getAddressBookEntry(state, domainResolution), + ); + if (domainResolution && entry?.name) { + addressBookEntryName = entry.name; + } + + const showErrorBanner = + domainError || (recipient.error && recipient.error !== 'required'); + const showWarningBanner = + !showErrorBanner && (domainWarning || recipient.warning); + + let contents; + if (recipient.address) { + contents = renderExplicitAddress( + recipient.address, + recipient.nickname, + 'validated user input', + dispatch, + ); + } else if (domainResolution && !recipient.error) { + contents = renderExplicitAddress( + domainResolution, + addressBookEntryName ?? userInput, + 'ENS resolution', + dispatch, + ); + } else { + contents = ( + <> + {userInput ? null : } + + + ); + } + + return ( + + {showErrorBanner ? ( + + {t(domainError ?? recipient.error)} + + ) : null} + {showWarningBanner ? ( + + {t(domainWarning ?? recipient.warning)} + + ) : null} + {contents} + + ); +}; diff --git a/ui/components/multichain/pages/send/send.js b/ui/components/multichain/pages/send/send.js index c1632a5b69b1..58f68b81b985 100644 --- a/ui/components/multichain/pages/send/send.js +++ b/ui/components/multichain/pages/send/send.js @@ -27,8 +27,8 @@ import { getMostRecentOverviewPage } from '../../../../ducks/history/history'; import { SendPageAccountPicker, SendPageRecipientInput, - SendPageYourAccount, SendPageNetworkPicker, + SendPageRecipient, } from './components'; export const SendPage = () => { @@ -116,7 +116,7 @@ export const SendPage = () => { - +