diff --git a/.circleci/config.yml b/.circleci/config.yml index 40d8bdb9082a..90b3e2da83a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -803,7 +803,7 @@ jobs: - run: name: Set branch parent commit env var command: | - echo "export PARENT_COMMIT=$(git rev-parse "$(git rev-list --topo-order --reverse HEAD ^origin/develop | head -1)"^)" >> $BASH_ENV + echo "export PARENT_COMMIT=$(git merge-base origin/HEAD HEAD)" >> $BASH_ENV source $BASH_ENV - run: name: build:announce diff --git a/.depcheckrc.yml b/.depcheckrc.yml index 8883375e7a36..aa1bffde4e63 100644 --- a/.depcheckrc.yml +++ b/.depcheckrc.yml @@ -4,6 +4,7 @@ ignores: # webapp deps # + - '@lavamoat/snow' - '@babel/runtime' - '@fortawesome/fontawesome-free' - 'punycode' diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c872d2d31243..23fbc9e5c578 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2716,6 +2716,14 @@ "message": "See address, account balance, activity and suggest transactions to approve", "description": "The description for the `eth_accounts` permission" }, + "permission_ethereumProvider": { + "message": "Access the Ethereum provider.", + "description": "The description for the `endowment:ethereum-provider` permission" + }, + "permission_getEntropy": { + "message": "Derive arbitrary keys unique to this snap.", + "description": "The description for the `snap_getEntropy` permission" + }, "permission_longRunning": { "message": "Run indefinitely.", "description": "The description for the `endowment:long-running` permission" diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 4b5cf3a06d4b..02235aa3f05f 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -1,10 +1,90 @@ { + "QRHardwareInvalidTransactionTitle": { + "message": "Errore" + }, + "QRHardwareMismatchedSignId": { + "message": "Dati incongruenti. Controlla i dati della transazione." + }, + "QRHardwarePubkeyAccountOutOfRange": { + "message": "Non ci sono altri account. Se desideri accedere a un altro account non elencato di seguito, ricollega il tuo portafoglio hardware e selezionalo." + }, + "QRHardwareScanInstructions": { + "message": "Posiziona il codice QR davanti alla tua fotocamera. Se lo schermo è sfocato non preoccuparti, non influirà sulla lettura." + }, "QRHardwareSignRequestCancel": { "message": "Annulla" }, + "QRHardwareSignRequestDescription": { + "message": "Dopo aver firmato con il tuo portafoglio, fai click su 'Ottieni firma' per ricevere la firma" + }, + "QRHardwareSignRequestGetSignature": { + "message": "Ottieni firma" + }, + "QRHardwareSignRequestSubtitle": { + "message": "Scansiona il QR code con il tuo portafoglio" + }, + "QRHardwareSignRequestTitle": { + "message": "Richiedi firma" + }, + "QRHardwareUnknownQRCodeTitle": { + "message": "Errore" + }, + "QRHardwareUnknownWalletQRCode": { + "message": "Codice QR non valido. Scansiona il codice QR di sincronizzazione del portafoglio hardware." + }, "QRHardwareWalletImporterTitle": { "message": "Scansiona Codice QR" }, + "QRHardwareWalletSteps1Description": { + "message": "Collega un portafoglio hardware air-gapped che comunica tramite codici QR. I portafogli hardware air-gapped supportati ufficialmente includono:" + }, + "QRHardwareWalletSteps1Title": { + "message": "Portafoglio HW basato su QR" + }, + "QRHardwareWalletSteps2Description": { + "message": "Ngrave (in arrivo)" + }, + "SIWEAddressInvalid": { + "message": "L'indirizzo nella richiesta di accesso non corrisponde all'indirizzo dell'account che stai utilizzando per accedere." + }, + "SIWEDomainWarningBody": { + "message": "Il sito Web ($1) ti chiede di accedere al dominio sbagliato. Potrebbe trattarsi di un attacco di phishing.", + "description": "$1 represents the website domain" + }, + "SIWELabelExpirationTime": { + "message": "Scade il:" + }, + "SIWELabelIssuedAt": { + "message": "Rilasciato a:" + }, + "SIWELabelMessage": { + "message": "Messaggio:" + }, + "SIWELabelNonce": { + "message": "Nonce:" + }, + "SIWELabelNotBefore": { + "message": "Non prima:" + }, + "SIWELabelResources": { + "message": "Risorse: $1", + "description": "$1 represents the number of resources" + }, + "SIWELabelVersion": { + "message": "Versione:" + }, + "SIWESiteRequestSubtitle": { + "message": "Questo sito richiede l'accesso con" + }, + "SIWESiteRequestTitle": { + "message": "Richiesta di accesso" + }, + "SIWEWarningSubtitle": { + "message": "Per confermare di aver compreso, controlla:" + }, + "SIWEWarningTitle": { + "message": "Sei sicuro?" + }, "about": { "message": "Informazioni" }, @@ -16,6 +96,17 @@ "message": "$1 può avere accesso e spendere al massimo", "description": "$1 is the url of the site requesting ability to spend" }, + "accessAndSpendNoticeNFT": { + "message": "$1 può accedere e spendere questa risorsa", + "description": "$1 is the url of the site requesting ability to spend" + }, + "accessYourWalletWithSRP": { + "message": "Accedi al tuo portafoglio con la tua frase di recupero segreta" + }, + "accessYourWalletWithSRPDescription": { + "message": "MetaMask non può recuperare la tua password. Useremo la tua frase di recupero segreta per assicurarci tu sia il proprietario, ripristinare il tuo portafoglio e impostare una nuova password. Innanzitutto, inserisci la frase di recupero segreta che ti è stata data in fase di creazione del tuo portafoglio. $1", + "description": "$1 is the words 'Learn More' from key 'learnMore', separated here so that it can be added as a link" + }, "accessingYourCamera": { "message": "Accesso alla fotocamera..." }, @@ -25,6 +116,10 @@ "accountName": { "message": "Nome Account" }, + "accountNameDuplicate": { + "message": "Questo nome è già in uso", + "description": "This is an error message shown when the user enters a new account name that matches an existing account name" + }, "accountOptions": { "message": "Opzioni Account" }, @@ -40,27 +135,109 @@ "activityLog": { "message": "log attività" }, + "add": { + "message": "Aggiungi" + }, + "addANetwork": { + "message": "Aggiungi una rete" + }, + "addANetworkManually": { + "message": "Aggungi manualmente una rete" + }, + "addANickname": { + "message": "Aggiungo un nickname" + }, "addAcquiredTokens": { "message": "Aggiungi i token che hai acquistato usando MetaMask" }, "addAlias": { "message": "Aggiungi alias" }, + "addBlockExplorer": { + "message": "Aggiungi un block explorer" + }, + "addContact": { + "message": "Aggiungi contatto" + }, + "addCustomToken": { + "message": "Aggiungi token personalizzato" + }, + "addCustomTokenByContractAddress": { + "message": "Non trovi un token? Puoi aggiungere qualsiasi token incollando il suo indirizzo. L'indirizzo del contratto del Token può essere trovato su $1.", + "description": "$1 is a blockchain explorer for a specific network, e.g. Etherscan for Ethereum" + }, + "addEthereumChainConfirmationDescription": { + "message": "Ciò consentirà a questa rete di essere utilizzata all'interno di MetaMask." + }, + "addEthereumChainConfirmationRisks": { + "message": "MetaMask non verifica le reti personalizzate." + }, + "addEthereumChainConfirmationRisksLearnMore": { + "message": "Maggiori informazioni su $1.", + "description": "$1 is a link with text that is provided by the 'addEthereumChainConfirmationRisksLearnMoreLink' key" + }, + "addEthereumChainConfirmationRisksLearnMoreLink": { + "message": "truffe e rischi per la sicurezza della rete", + "description": "Link text for the 'addEthereumChainConfirmationRisksLearnMore' translation key" + }, + "addEthereumChainConfirmationTitle": { + "message": "Consenti a questo sito di aggiungere una rete?" + }, + "addFriendsAndAddresses": { + "message": "Aggiungi amici e indirizzi di cui ti fidi" + }, + "addFromAListOfPopularNetworks": { + "message": "Aggiungi da un elenco di reti popolari o aggiungi una rete manualmente. Interagisci solo con le entità di cui ti fidi." + }, + "addMemo": { + "message": "Aggiungi memo" + }, + "addMoreNetworks": { + "message": "aggiungi più reti manualmente" + }, "addNetwork": { "message": "Aggiungi Rete" }, + "addNetworkTooltipWarning": { + "message": "Questarete si basa su terze parti. La connessione potrebbe essere meno affidabile o consentire a terzi di tracciare le tue attività. $1", + "description": "$1 is Learn more link" + }, "addSuggestedTokens": { "message": "Aggiungi Token Suggeriti" }, "addToken": { "message": "Aggiungi Token" }, + "address": { + "message": "Indirizzo" + }, + "addressBookIcon": { + "message": "Icona rubrica indirizzo" + }, "advanced": { "message": "Avanzate" }, + "advancedBaseGasFeeToolTip": { + "message": "Quando la tua transazione viene inclusa nel blocco, ogni differenza tra la tua offerta massima di gas e il gas effettivamente utilizzato viene restituita a te. Il totale viene calcolato come offerta massima di gas (in GEWI) * limite di gas." + }, + "advancedGasFeeDefaultOptIn": { + "message": "Salva queste $1 come mie preferite per \"Avanzate\"" + }, + "advancedGasFeeDefaultOptOut": { + "message": "Utilizzare sempre questi valori e l'impostazione avanzata come predefiniti." + }, + "advancedGasFeeModalTitle": { + "message": "Tariffa gas avanzata" + }, + "advancedGasPriceTitle": { + "message": "Prezzo gas" + }, "advancedOptions": { "message": "Opzioni Avanzate" }, + "advancedPriorityFeeToolTip": { + "message": "La commissione di priorità avanzata (nota anche come \"suggerimento del minatore\") va direttamente ai minatori e li incentiva a dare la priorità alla tua commissione di transazione." + }, "affirmAgree": { "message": "Acconsento" }, @@ -82,9 +259,17 @@ "alerts": { "message": "Avvisi" }, + "allOfYour": { + "message": "Tutti i tuoi $1", + "description": "$1 is the symbol or name of the token that the user is approving spending" + }, "allowExternalExtensionTo": { "message": "Permetti a questa estensione di:" }, + "allowSpendToken": { + "message": "Dai il permesso di spendere tuoi $1?", + "description": "$1 is the symbol of the token that are requesting to spend" + }, "allowThisSiteTo": { "message": "Permetti a questo sito di:" }, @@ -96,7 +281,7 @@ "message": "Importo" }, "appDescription": { - "message": "Ethereum Browser Extension", + "message": "Estensione Browser Ethereum", "description": "The description of the application" }, "appName": { @@ -114,6 +299,19 @@ "approve": { "message": "Approva" }, + "approveAllTokensTitle": { + "message": "Consenti l'accesso e il trasferimento di tutti i tuoi $1?", + "description": "$1 is the symbol of the token for which the user is granting approval" + }, + "approveAndInstall": { + "message": "Approva & installa" + }, + "approveAndUpdate": { + "message": "Approva & aggiorna" + }, + "approveButtonText": { + "message": "Approva" + }, "approveSpendLimit": { "message": "Approva limite di spesa per $1", "description": "The token symbol that is being approved" @@ -121,9 +319,28 @@ "approved": { "message": "Approvato" }, + "approvedAmountWithColon": { + "message": "Importo approvato:" + }, + "approvedAsset": { + "message": "Asset approvato" + }, + "approvedOn": { + "message": "Approvato il $1", + "description": "$1 is the approval date for a permission" + }, + "areYouSure": { + "message": "Sei sicuro?" + }, + "assetOptions": { + "message": "Opzioni asset" + }, "assets": { "message": "Patrimonio" }, + "attemptSendingAssets": { + "message": "Se si tenta di inviare risorse direttamente da una rete all'altra, ciò potrebbe comportare una perdita permanente della risorca coinvolta. Assicurati di usare un bridge." + }, "attemptToCancel": { "message": "Tentativo di Annullamento?" }, @@ -164,27 +381,98 @@ "message": "Esegui il backup ora" }, "balance": { - "message": "Bilancio:" + "message": "Bilancio" }, "balanceOutdated": { "message": "Il bilancio può essere non aggiornato" }, + "baseFee": { + "message": "Commissioni di base" + }, "basic": { "message": "Base" }, + "beCareful": { + "message": "Presta attenzione" + }, + "betaMetamaskDescription": { + "message": "Scelto da milioni di persone, MetaMask è un portafoglio sicuro che rende il mondo del web3 accessibile a tutti." + }, + "betaMetamaskDescriptionExplanation": { + "message": "Usa questa versione per testare le funzionalità imminenti prima che vengano rilasciate. Il tuo utilizzo e il tuo feedback ci aiutano a creare la migliore versione possibile di MetaMask. L'utilizzo di MetaMask Beta è soggetto al nostro standard $1 e al nostro $2. Come versione beta, potrebbe esserci un aumento del rischio di bug. Procedendo, accetti e riconosci questi rischi, così come i rischi che si trovano nei nostri Termini e Termini beta.", + "description": "$1 represents localization item betaMetamaskDescriptionExplanationTermsLinkText. $2 represents localization item betaMetamaskDescriptionExplanationBetaTermsLinkText" + }, + "betaMetamaskDescriptionExplanationBetaTermsLinkText": { + "message": "Termini beta supplementari" + }, + "betaMetamaskDescriptionExplanationTermsLinkText": { + "message": "Termini" + }, + "betaMetamaskVersion": { + "message": "MetaMask Versione Beta" + }, + "betaWelcome": { + "message": "Benvenuto in MetaMask Beta" + }, + "blockExplorerAccountAction": { + "message": "Profilo", + "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Account in Explorer" + }, "blockExplorerUrl": { - "message": "Block Explorer" + "message": "URL Block Explorer" + }, + "blockExplorerUrlDefinition": { + "message": "L'URL usato come Block Explorer per questa rete." }, "blockExplorerView": { "message": "Visualizza account su $1", "description": "$1 replaced by URL for custom block explorer" }, + "blockies": { + "message": "Blocchi" + }, "browserNotSupported": { "message": "Il tuo Browser non è supportato..." }, + "buildContactList": { + "message": "Costruisci la tua lista contatti" + }, + "builtAroundTheWorld": { + "message": "MetaMask è progettato e costruito in tutto il mondo." + }, + "busy": { + "message": "Occupato" + }, "buy": { "message": "Compra" }, + "buyAsset": { + "message": "Compra $1", + "description": "$1 is the ticker symbol of a an asset the user is being prompted to purchase" + }, + "buyCryptoWithCoinbasePay": { + "message": "Compra $1 con Coinbase Pay", + "description": "$1 represents the crypto symbol to be purchased" + }, + "buyCryptoWithCoinbasePayDescription": { + "message": "Puoi facilmente acquistare o trasferire criptovalute con il tuo account Coinbase.", + "description": "$1 represents the crypto symbol to be purchased" + }, + "buyCryptoWithMoonPay": { + "message": "Compra $1 con MoonPay", + "description": "$1 represents the cypto symbol to be purchased" + }, + "buyCryptoWithMoonPayDescription": { + "message": "MoonPay supporta metodi di pagamento popolari, incluso Visa, Mastercard, Apple / Google / Samsung Pay e bonifici bancari in 145+ paesi. I Token vengono depositati nel tuo account MetaMask." + }, + "buyCryptoWithTransak": { + "message": "Compra $1 con Transak", + "description": "$1 represents the cypto symbol to be purchased" + }, + "buyCryptoWithTransakDescription": { + "message": "Transak supporta carte di credito e debito, Apple Pay, MobiKwik e bonifici bancari (in base alla località) in 100+ paesi. Deposita $1 direttamente nel tuo account MetaMask.", + "description": "$1 represents the crypto symbol to be purchased" + }, "buyWithWyre": { "message": "Compra $1 con Wyre" }, @@ -197,24 +485,82 @@ "cancel": { "message": "Annulla" }, + "cancelEdit": { + "message": "Annulla modifica" + }, + "cancelPopoverTitle": { + "message": "Annulla transazione" + }, + "cancelSpeedUp": { + "message": "annulla o velocizza una transazione." + }, + "cancelSpeedUpLabel": { + "message": "Questa commissione di gas $1 l'originale.", + "description": "$1 is text 'replace' in bold" + }, + "cancelSpeedUpTransactionTooltip": { + "message": "Per $1 una transazione la commissione di gas deve crescere almeno del 10% per essere riconosciuto dalla rete.", + "description": "$1 is string 'cancel' or 'speed up'" + }, + "cancelSwapForFee": { + "message": "Annulla scambio per ~$1", + "description": "$1 could be e.g. $2.98, it is a cost for cancelling a Smart Transaction" + }, + "cancelSwapForFree": { + "message": "Annulla scambio gratuitamente" + }, "cancellationGasFee": { "message": "Costo di Annullamento in Gas" }, "cancelled": { "message": "Annullata" }, + "chainIdDefinition": { + "message": "Il Chain ID utilizzato per firmare le transazioni per questa rete." + }, + "chainIdExistsErrorMsg": { + "message": "Questo Chain ID al momento è usato dalla rete $1." + }, + "chainListReturnedDifferentTickerSymbol": { + "message": "La rete con chain ID $1 può utilizzare un simbolo diverso ($2) da quello che hai inserito. Si prega di verificare prima di continuare.", + "description": "$1 is the chain id currently entered in the network form and $2 is the return value of nativeCurrency.symbol from chainlist.network" + }, "chromeRequiredForHardwareWallets": { "message": "Devi usare MetaMask con Google Chrome per connettere il tuo Portafoglio Hardware" }, + "clickToConnectLedgerViaWebHID": { + "message": "Clicca qui per connettere il tuo Ledger tramite WebHID", + "description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid" + }, + "clickToManuallyAdd": { + "message": "Clicca qui per aggiungere manualmente i token." + }, "clickToRevealSeed": { "message": "Clicca qui per rivelare la tua frase segreta" }, "close": { "message": "Chiudi" }, + "collectibleAddFailedMessage": { + "message": "Non è possibile aggiungere questo NFT poiché i dettagli della proprietà non corrispondono. Assicurati di aver inserito le informazioni corrette." + }, + "collectibleAddressError": { + "message": "Questo token è un NFT. Aggiungilo su $1", + "description": "$1 is a clickable link with text defined by the 'importNFTPage' key" + }, "confirm": { "message": "Conferma" }, + "confirmPageDialogSetApprovalForAll": { + "message": "Stai concedendo l'accesso a $1, inclusi quelli che potresti possedere in futuro. L'ente a cui stai concedendo l'accesso potrà trasferire NFT dal tuo portafoglio in qualsiasi momento senza chiederti conferma fino a quando non revocherai questa approvazione. $2", + "description": "$1 and $2 are bolded translations 'confirmPageDialogSetApprovalForAllPlaceholder1' and 'confirmPageDialogSetApprovalForAllPlaceholder2'" + }, + "confirmPageDialogSetApprovalForAllPlaceholder1": { + "message": "tutti gli NFT in questo contratto" + }, + "confirmPageDialogSetApprovalForAllPlaceholder2": { + "message": "Procedi con cautela." + }, "confirmPassword": { "message": "Conferma Password" }, @@ -224,6 +570,15 @@ "confirmed": { "message": "Confermata" }, + "confusableUnicode": { + "message": "'$1' è simile a '$2'." + }, + "confusableZeroWidthUnicode": { + "message": "Trovato carattere a larghezza zero." + }, + "confusingEnsDomain": { + "message": "Abbiamo rilevato un carattere confondibile nel nome ENS. Controlla il nome ENS per evitare una potenziale truffa." + }, "congratulations": { "message": "Congratulazioni" }, @@ -283,6 +638,10 @@ "message": "$1 non è connesso ad alcun sito.", "description": "$1 is the account name" }, + "connectedSnapSites": { + "message": "$1 snap è collegato a questi siti. Hanno accesso alle autorizzazioni sopra elencate.", + "description": "$1 represents the name of the snap" + }, "connecting": { "message": "Connessione..." }, @@ -295,6 +654,9 @@ "connectingToMainnet": { "message": "Connessione alla Rete Ethereum Principale" }, + "connectingToSepolia": { + "message": "Connessione alla Rete di test Sepolia" + }, "contactUs": { "message": "Contattaci!" }, @@ -304,15 +666,51 @@ "continue": { "message": "Continua" }, + "continueToCoinbasePay": { + "message": "Continua su Coinbase Pay" + }, + "continueToMoonPay": { + "message": "Continua su MoonPay" + }, + "continueToTransak": { + "message": "Continua su Transak" + }, "continueToWyre": { "message": "Continua su Wyre" }, + "contract": { + "message": "Contratto" + }, + "contractAddress": { + "message": "Indirizzo contratto" + }, + "contractAddressError": { + "message": "Stai inviando i token all'indirizzo del contratto del token. Ciò potrebbe comportare la perdita di questi token." + }, "contractDeployment": { "message": "Distribuzione Contratto" }, + "contractDescription": { + "message": "Per proteggerti dai truffatori, prenditi un momento per verificare i dettagli del contratto." + }, "contractInteraction": { "message": "Interazione Contratto" }, + "contractRequestingSpendingCap": { + "message": "Il contratto richiede un limite di spesa" + }, + "contractTitle": { + "message": "Dettagli contratto" + }, + "contractToken": { + "message": "Token contratto" + }, + "convertTokenToNFTDescription": { + "message": "Abbiamo rilevato che questa risorsa è un NFT. MetaMask ora ha il supporto nativo completo per NFT. Vuoi rimuoverlo dal tuo elenco di token e aggiungerlo come NFT?" + }, + "convertTokenToNFTExistDescription": { + "message": "Abbiamo rilevato che questa risorsa è stata aggiunta come NFT. Vuoi rimuoverlo dal tuo elenco di token?" + }, "copiedExclamation": { "message": "Copiato!" }, @@ -322,6 +720,9 @@ "copyPrivateKey": { "message": "Questa è la tua chiave privata (clicca per copiare)" }, + "copyRawTransactionData": { + "message": "Copia i dati grezzi della transazione" + }, "copyToClipboard": { "message": "Copia negli appunti" }, @@ -337,12 +738,21 @@ "createAccount": { "message": "Crea Account" }, + "createNewWallet": { + "message": "Crea un nuovo portafoglio" + }, "createPassword": { "message": "Crea Password" }, "currencyConversion": { "message": "Conversione Moneta" }, + "currencySymbol": { + "message": "Simbolo moneta" + }, + "currencySymbolDefinition": { + "message": "Il simbolo del ticker visualizzato per la valuta di questa rete." + }, "currentAccountNotConnected": { "message": "Il tuo account corrente non è connesso" }, @@ -352,15 +762,72 @@ "currentLanguage": { "message": "Lingua Corrente" }, + "currentTitle": { + "message": "Corrente:" + }, + "currentlyUnavailable": { + "message": "Non disponibile su questa Rete" + }, + "curveHighGasEstimate": { + "message": "Grafico di stima del gas aggressivo" + }, + "curveLowGasEstimate": { + "message": "Grafico della stima del gas basso" + }, + "curveMediumGasEstimate": { + "message": "Grafico della stima del gas di mercato" + }, "custom": { "message": "Avanzate" }, + "customContentSearch": { + "message": "Cerca una rete aggiunta in precedenza" + }, + "customGasSettingToolTipMessage": { + "message": "Usa $1 per personalizzare il prezzo del gas. Questo può creare confusione se non hai familiarità. Interagisci a tuo rischio.", + "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold font-weight" + }, "customSpendLimit": { "message": "Limite Spesa Personalizzato" }, + "customSpendingCap": { + "message": "Custom spending cap" + }, "customToken": { "message": "Token Personalizzato" }, + "customTokenWarningInNonTokenDetectionNetwork": { + "message": "Il rilevamento dei token non è ancora disponibile su questa rete. Importa il token manualmente e assicurati che sia corretto. Ulteriori informazioni su $1" + }, + "customTokenWarningInTokenDetectionNetwork": { + "message": "Prima di importare manualmente un token, assicurati sia attendibile. Ulteriori informazioni su $ 1" + }, + "customTokenWarningInTokenDetectionNetworkWithTDOFF": { + "message": "Assicurati di fidarti di un token prima di importarlo. Scopri come evitare $1. Puoi anche abilitare il rilevamento dei token $2." + }, + "customerSupport": { + "message": "servizio clienti" + }, + "dappSuggested": { + "message": "Sito suggerito" + }, + "dappSuggestedGasSettingToolTipMessage": { + "message": "$1 ha suggerito questo prezzo.", + "description": "$1 is url for the dapp that has suggested gas settings" + }, + "dappSuggestedShortLabel": { + "message": "Sito" + }, + "dappSuggestedTooltip": { + "message": "$1 ha consigliato questo prezzo.", + "description": "$1 represents the Dapp's origin" + }, + "darkTheme": { + "message": "Scuro" + }, + "dataBackupSeemsCorrupt": { + "message": "Impossibile ripristinare i tuoi dati. Il file sembra essere corrotto." + }, "decimal": { "message": "Precisione Decimali" }, @@ -399,9 +866,32 @@ "deleteNetworkDescription": { "message": "Sei sicuro di voler eliminare questa rete?" }, + "depositCrypto": { + "message": "Deposita $1", + "description": "$1 represents the crypto symbol to be purchased" + }, + "deprecatedTestNetworksLink": { + "message": "Scopri di più" + }, + "deprecatedTestNetworksMsg": { + "message": "A causa delle modifiche al protocollo di Ethereum: le reti di test Rinkeby, Ropsten e Kovan potrebbero non funzionare in modo affidabile e saranno presto rimosse." + }, + "description": { + "message": "Descrizione" + }, "details": { "message": "Dettagli" }, + "directDepositCrypto": { + "message": "Deposito diretto $1" + }, + "directDepositCryptoExplainer": { + "message": "Se hai già $1, il modo più rapido per ottenere $1 nel tuo nuovo portafoglio tramite deposito diretto." + }, + "disabledGasOptionToolTipMessage": { + "message": "“$1” è disabilitato perché non soddisfa la maggiorazione minima del 10% rispetto al canone gas originario.", + "description": "$1 is gas estimate type which can be market or aggressive" + }, "disconnect": { "message": "Disconnetti" }, @@ -420,15 +910,30 @@ "dismiss": { "message": "Ignora" }, + "dismissReminderDescriptionField": { + "message": "Attiva questa opzione per ignorare il messaggio di promemoria del backup della frase di ripristino segreta. Ti consigliamo vivamente di eseguire il backup della tua frase di recupero segreta per evitare la perdita di fondi" + }, + "dismissReminderField": { + "message": "Ignora il promemoria di backup della frase di ripristino segreta" + }, + "domain": { + "message": "Dominio" + }, "done": { "message": "Finito" }, "dontShowThisAgain": { "message": "Non mostrare di nuovo" }, + "downArrow": { + "message": "Freccia in giù" + }, "downloadGoogleChrome": { "message": "Scarica Google Chrome" }, + "downloadNow": { + "message": "Scarica ora" + }, "downloadSecretBackup": { "message": "Scarica questa Frase di Backup Segreta e tienila al sicuro in un hard disk o supporto di memorizzazione esterno criptato." }, @@ -441,9 +946,64 @@ "edit": { "message": "Modifica" }, + "editANickname": { + "message": "Modifica nickname" + }, + "editAddressNickname": { + "message": "Modifica indirizzo nickname" + }, + "editCancellationGasFeeModalTitle": { + "message": "Modifica la tassa di cancellazione del gas" + }, "editContact": { "message": "Modifica contatto" }, + "editGasEducationButtonText": { + "message": "Come devo scegliere?" + }, + "editGasEducationHighExplanation": { + "message": "Questo è il migliore per le transazioni sensibili al tempo (come gli swap) in quanto aumenta la probabilità di una transazione riuscita. Se uno Swap impiega troppo tempo per l'elaborazione, potrebbe non riuscire e comportare la perdita di parte della tariffa del gas." + }, + "editGasEducationLowExplanation": { + "message": "Una tariffa gas inferiore dovrebbe essere utilizzata solo quando il tempo di elaborazione è meno importante. Commissioni più basse rendono difficile prevedere quando (o se) la tua transazione avrà esito positivo." + }, + "editGasEducationMediumExplanation": { + "message": "Una commissione gas media va bene per l'invio, il prelievo o altre transazioni non sensibili al fattore tempo. Molto spesso questa impostazione risulterà in una transazione riuscita." + }, + "editGasEducationModalIntro": { + "message": "La scelta della giusta commissione per il gas dipende dal tipo di transazione e da quanto è importante per te." + }, + "editGasEducationModalTitle": { + "message": "Come scegliere?" + }, + "editGasFeeModalTitle": { + "message": "Modificare la tariffa del gas" + }, + "editGasHigh": { + "message": "Alta" + }, + "editGasLimitOutOfBounds": { + "message": "Il limite di gas deve essere almeno $1" + }, + "editGasLimitOutOfBoundsV2": { + "message": "Il limite del gas deve essere maggiore di $1 e minore di $2", + "description": "$1 is the minimum limit for gas and $2 is the maximum limit" + }, + "editGasLimitTooltip": { + "message": "Il limite del gas è l'unità massima di gas che sei disposto a utilizzare. Le unità di gas sono un moltiplicatore per “Commissione massima priorità“ e “Commissione massima“." + }, + "editGasLow": { + "message": "Bassa" + }, + "editGasMaxBaseFeeGWEIImbalance": { + "message": "La tariffa base massima non può essere inferiore alla tariffa prioritaria" + }, + "editGasMaxBaseFeeHigh": { + "message": "La tariffa base massima è superiore al necessario" + }, + "editGasMaxBaseFeeLow": { + "message": "La tariffa base massima è bassa per le condizioni di rete attuali" + }, "editPermission": { "message": "Modifica Permessi" }, diff --git a/app/background.html b/app/background.html index 6a0150579912..eabb3bf3a704 100644 --- a/app/background.html +++ b/app/background.html @@ -4,6 +4,8 @@ + + {{@if(it.applyLavaMoat)}} diff --git a/app/home.html b/app/home.html index 4a4df3f4ea94..e4838a50e15d 100644 --- a/app/home.html +++ b/app/home.html @@ -13,6 +13,8 @@
+ + {{@if(it.applyLavaMoat)}} diff --git a/app/notification.html b/app/notification.html index 1f348f680f8b..209a642a38ad 100644 --- a/app/notification.html +++ b/app/notification.html @@ -33,6 +33,8 @@
+ + {{@if(it.applyLavaMoat)}} diff --git a/app/popup.html b/app/popup.html index 831fe6122370..978c237db181 100644 --- a/app/popup.html +++ b/app/popup.html @@ -13,6 +13,8 @@
+ + {{@if(it.applyLavaMoat)}} diff --git a/app/scripts/app-init.js b/app/scripts/app-init.js index 450c5959737e..0f61d9888ef1 100644 --- a/app/scripts/app-init.js +++ b/app/scripts/app-init.js @@ -127,6 +127,10 @@ chrome.runtime.onMessage.addListener(() => { return false; }); +chrome.runtime.onStartup.addListener(() => { + globalThis.isFirstTimeProfileLoaded = true; +}); + /* * This content script is injected programmatically because * MAIN world injection does not work properly via manifest diff --git a/app/scripts/background.js b/app/scripts/background.js index 736e60603202..0239eda85a17 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -491,11 +491,6 @@ function setupController(initState, initLangCode, remoteSourcePort) { controller.setupTrustedCommunication(portStream, remotePort.sender); if (isManifestV3) { - // Message below if captured by UI code in app/scripts/ui.js which will trigger UI initialisation - // This ensures that UI is initialised only after background is ready - // It fixes the issue of blank screen coming when extension is loaded, the issue is very frequent in MV3 - remotePort.postMessage({ name: EXTENSION_MESSAGES.CONNECTION_READY }); - // If we get a WORKER_KEEP_ALIVE message, we respond with an ACK remotePort.onMessage.addListener((message) => { if (message.name === WORKER_KEEP_ALIVE_MESSAGE) { diff --git a/app/scripts/controllers/ens/index.js b/app/scripts/controllers/ens/index.js index 85477ef7e5cf..47363a1562e6 100644 --- a/app/scripts/controllers/ens/index.js +++ b/app/scripts/controllers/ens/index.js @@ -27,6 +27,11 @@ export default class EnsController { } this.store = new ObservableStore(initState); + + this.resetState = () => { + this.store.updateState(initState); + }; + onNetworkDidChange(() => { this.store.putState(initState); const chainId = getCurrentChainId(); diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index 1c87c6181ba5..a18332624162 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -166,11 +166,9 @@ export default class MetaMetricsController { // Code below submits any pending segmentApiCalls to Segment if/when the controller is re-instantiated if (isManifestV3) { - Object.values(segmentApiCalls).forEach( - ({ eventType, payload, callback }) => { - this._submitSegmentAPICall(eventType, payload, callback); - }, - ); + Object.values(segmentApiCalls).forEach(({ eventType, payload }) => { + this._submitSegmentAPICall(eventType, payload); + }); } // Close out event fragments that were created but not progressed. An @@ -961,6 +959,11 @@ export default class MetaMetricsController { // Saving segmentApiCalls in controller store in MV3 ensures that events are tracked // even if service worker terminates before events are submiteed to segment. _submitSegmentAPICall(eventType, payload, callback) { + const { metaMetricsId, participateInMetaMetrics } = this.state; + if (!participateInMetaMetrics || !metaMetricsId) { + return; + } + const messageId = payload.messageId || generateRandomId(); let timestamp = new Date(); if (payload.timestamp) { @@ -979,7 +982,6 @@ export default class MetaMetricsController { ...modifiedPayload, timestamp: modifiedPayload.timestamp.toString(), }, - callback, }, }, }); diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index 297a219123ed..abe9949fe26b 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -362,7 +362,7 @@ describe('MetaMetricsController', function () { it('should track an event if user has not opted in, but isOptIn is true', function () { const mock = sinon.mock(segment); const metaMetricsController = getMetaMetricsController({ - participateInMetaMetrics: false, + participateInMetaMetrics: true, }); mock .expects('track') @@ -394,7 +394,7 @@ describe('MetaMetricsController', function () { it('should track an event during optin and allow for metaMetricsId override', function () { const mock = sinon.mock(segment); const metaMetricsController = getMetaMetricsController({ - participateInMetaMetrics: false, + participateInMetaMetrics: true, }); mock .expects('track') diff --git a/app/scripts/controllers/permissions/flask/snap-permissions.js b/app/scripts/controllers/permissions/flask/snap-permissions.js index 1a1d78c5d2d3..f8465ee1b07c 100644 --- a/app/scripts/controllers/permissions/flask/snap-permissions.js +++ b/app/scripts/controllers/permissions/flask/snap-permissions.js @@ -1,4 +1,4 @@ -import { endowmentPermissionBuilders } from '@metamask/snap-controllers'; +import { endowmentPermissionBuilders } from '@metamask/snaps-controllers'; import { restrictedMethodPermissionBuilders, selectHooks, diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 1e3fad4a4829..f717bcbc551f 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -1,6 +1,6 @@ import { constructPermission, PermissionType } from '@metamask/controllers'; ///: BEGIN:ONLY_INCLUDE_IN(flask) -import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications } from '@metamask/snap-controllers'; +import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications } from '@metamask/snaps-controllers'; import { caveatSpecifications as snapsCaveatsSpecifications } from '@metamask/rpc-methods'; ///: END:ONLY_INCLUDE_IN import { @@ -11,7 +11,7 @@ import { /** * This file contains the specifications of the permissions and caveats * that are recognized by our permission system. See the PermissionController - * README in @metamask/snap-controllers for details. + * README in @metamask/controllers for details. */ /** diff --git a/app/scripts/controllers/permissions/specifications.test.js b/app/scripts/controllers/permissions/specifications.test.js index f199e9122215..7e1dc24dff4e 100644 --- a/app/scripts/controllers/permissions/specifications.test.js +++ b/app/scripts/controllers/permissions/specifications.test.js @@ -16,7 +16,7 @@ describe('PermissionController specifications', () => { describe('caveat specifications', () => { it('getCaveatSpecifications returns the expected specifications object', () => { const caveatSpecifications = getCaveatSpecifications({}); - expect(Object.keys(caveatSpecifications)).toHaveLength(5); + expect(Object.keys(caveatSpecifications)).toHaveLength(6); expect( caveatSpecifications[CaveatTypes.restrictReturnedAccounts].type, ).toStrictEqual(CaveatTypes.restrictReturnedAccounts); @@ -33,6 +33,9 @@ describe('PermissionController specifications', () => { expect(caveatSpecifications.snapCronjob.type).toStrictEqual( SnapCaveatType.SnapCronjob, ); + expect(caveatSpecifications.transactionOrigin.type).toStrictEqual( + SnapCaveatType.TransactionOrigin, + ); }); describe('restrictReturnedAccounts', () => { diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index 8c7e2c2148d6..476d020ee1ac 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -115,6 +115,10 @@ export default class SwapsController { swapsState: { ...initialState.swapsState }, }); + this.resetState = () => { + this.store.updateState({ swapsState: { ...initialState.swapsState } }); + }; + this._fetchTradesInfo = fetchTradesInfo; this._getCurrentChainId = getCurrentChainId; this._getEIP1559GasFeeEstimates = getEIP1559GasFeeEstimates; diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 5becce48b40c..2b42ed3ff10f 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -151,6 +151,11 @@ export default class TransactionController extends EventEmitter { this.getTokenStandardAndDetails = opts.getTokenStandardAndDetails; this.memStore = new ObservableStore({}); + + this.resetState = () => { + this._updateMemstore(); + }; + this.query = new EthQuery(this.provider); this.txGasUtil = new TxGasUtil(this.provider); diff --git a/app/scripts/lib/account-tracker.js b/app/scripts/lib/account-tracker.js index 7e57a8ee9732..9f0600d65914 100644 --- a/app/scripts/lib/account-tracker.js +++ b/app/scripts/lib/account-tracker.js @@ -62,6 +62,10 @@ export default class AccountTracker { }; this.store = new ObservableStore(initState); + this.resetState = () => { + this.store.updateState(initState); + }; + this._provider = opts.provider; this._query = pify(new EthQuery(this._provider)); this._blockTracker = opts.blockTracker; diff --git a/app/scripts/lib/decrypt-message-manager.js b/app/scripts/lib/decrypt-message-manager.js index f5d221485cbb..4805ec398ca1 100644 --- a/app/scripts/lib/decrypt-message-manager.js +++ b/app/scripts/lib/decrypt-message-manager.js @@ -41,6 +41,14 @@ export default class DecryptMessageManager extends EventEmitter { unapprovedDecryptMsgs: {}, unapprovedDecryptMsgCount: 0, }); + + this.resetState = () => { + this.memStore.updateState({ + unapprovedDecryptMsgs: {}, + unapprovedDecryptMsgCount: 0, + }); + }; + this.messages = []; this.metricsEvent = opts.metricsEvent; } diff --git a/app/scripts/lib/encryption-public-key-manager.js b/app/scripts/lib/encryption-public-key-manager.js index 5f2b74993655..cf96c320a203 100644 --- a/app/scripts/lib/encryption-public-key-manager.js +++ b/app/scripts/lib/encryption-public-key-manager.js @@ -36,6 +36,14 @@ export default class EncryptionPublicKeyManager extends EventEmitter { unapprovedEncryptionPublicKeyMsgs: {}, unapprovedEncryptionPublicKeyMsgCount: 0, }); + + this.resetState = () => { + this.memStore.updateState({ + unapprovedEncryptionPublicKeyMsgs: {}, + unapprovedEncryptionPublicKeyMsgCount: 0, + }); + }; + this.messages = []; this.metricsEvent = opts.metricsEvent; } diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 6f80bb75b7b5..dfc4e3ec3aaf 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -36,6 +36,14 @@ export default class MessageManager extends EventEmitter { unapprovedMsgs: {}, unapprovedMsgCount: 0, }); + + this.resetState = () => { + this.memStore.updateState({ + unapprovedMsgs: {}, + unapprovedMsgCount: 0, + }); + }; + this.messages = []; this.metricsEvent = metricsEvent; } diff --git a/app/scripts/lib/personal-message-manager.js b/app/scripts/lib/personal-message-manager.js index 493288907a90..338458a66db0 100644 --- a/app/scripts/lib/personal-message-manager.js +++ b/app/scripts/lib/personal-message-manager.js @@ -43,6 +43,14 @@ export default class PersonalMessageManager extends EventEmitter { unapprovedPersonalMsgs: {}, unapprovedPersonalMsgCount: 0, }); + + this.resetState = () => { + this.memStore.updateState({ + unapprovedPersonalMsgs: {}, + unapprovedPersonalMsgCount: 0, + }); + }; + this.messages = []; this.metricsEvent = metricsEvent; } diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index 60356ba8a522..397e27b0c3c7 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -43,6 +43,14 @@ export default class TypedMessageManager extends EventEmitter { unapprovedTypedMessages: {}, unapprovedTypedMessagesCount: 0, }); + + this.resetState = () => { + this.memStore.updateState({ + unapprovedTypedMessages: {}, + unapprovedTypedMessagesCount: 0, + }); + }; + this.messages = []; this.metricsEvent = metricsEvent; } diff --git a/app/scripts/metamask-controller.actions.test.js b/app/scripts/metamask-controller.actions.test.js index b3eff23d7c6d..c8b335d9435e 100644 --- a/app/scripts/metamask-controller.actions.test.js +++ b/app/scripts/metamask-controller.actions.test.js @@ -23,6 +23,12 @@ const browserPolyfillMock = { }, getPlatformInfo: async () => 'mac', }, + storage: { + local: { + get: sinon.stub().resolves({}), + set: sinon.stub().resolves(), + }, + }, }; let loggerMiddlewareMock; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index fdf441cef079..ecbf520034cf 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -52,9 +52,10 @@ import { CronjobController, SnapController, IframeExecutionService, -} from '@metamask/snap-controllers'; +} from '@metamask/snaps-controllers'; ///: END:ONLY_INCLUDE_IN +import browser from 'webextension-polyfill'; import { ASSET_TYPES, TRANSACTION_STATUSES, @@ -100,6 +101,7 @@ import { getTokenValueParam, hexToDecimal, } from '../../shared/lib/metamask-controller-utils'; +import { isManifestV3 } from '../../shared/modules/mv3.utils'; import { onMessageReceived, checkForMultipleVersionsRunning, @@ -139,6 +141,7 @@ import seedPhraseVerifier from './lib/seed-phrase-verifier'; import MetaMetricsController from './controllers/metametrics'; import { segment } from './lib/segment'; import createMetaRPCHandler from './lib/createMetaRPCHandler'; + import { CaveatMutatorFactories, getCaveatSpecifications, @@ -417,6 +420,7 @@ export default class MetamaskController extends EventEmitter { : GAS_API_BASE_URL; this.gasFeeController = new GasFeeController({ + state: initState.GasFeeController, interval: 10000, messenger: gasFeeMessenger, clientId: SWAPS_CLIENT_ID, @@ -481,26 +485,30 @@ export default class MetamaskController extends EventEmitter { ); // token exchange rate tracker - this.tokenRatesController = new TokenRatesController({ - onTokensStateChange: (listener) => - this.tokensController.subscribe(listener), - onCurrencyRateStateChange: (listener) => - this.controllerMessenger.subscribe( - `${this.currencyRateController.name}:stateChange`, - listener, - ), - onNetworkStateChange: (cb) => - this.networkController.store.subscribe((networkState) => { - const modifiedNetworkState = { - ...networkState, - provider: { - ...networkState.provider, - chainId: hexToDecimal(networkState.provider.chainId), - }, - }; - return cb(modifiedNetworkState); - }), - }); + this.tokenRatesController = new TokenRatesController( + { + onTokensStateChange: (listener) => + this.tokensController.subscribe(listener), + onCurrencyRateStateChange: (listener) => + this.controllerMessenger.subscribe( + `${this.currencyRateController.name}:stateChange`, + listener, + ), + onNetworkStateChange: (cb) => + this.networkController.store.subscribe((networkState) => { + const modifiedNetworkState = { + ...networkState, + provider: { + ...networkState.provider, + chainId: hexToDecimal(networkState.provider.chainId), + }, + }; + return cb(modifiedNetworkState); + }), + }, + undefined, + initState.TokenRatesController, + ); this.ensController = new EnsController({ provider: this.provider, @@ -583,6 +591,7 @@ export default class MetamaskController extends EventEmitter { keyringTypes: additionalKeyrings, initState: initState.KeyringController, encryptor: opts.encryptor || undefined, + cacheEncryptionKey: isManifestV3, }); this.keyringController.memStore.subscribe((state) => this._onKeyringControllerUpdate(state), @@ -661,7 +670,7 @@ export default class MetamaskController extends EventEmitter { ///: BEGIN:ONLY_INCLUDE_IN(flask) this.snapExecutionService = new IframeExecutionService({ iframeUrl: new URL( - 'https://metamask.github.io/iframe-execution-environment/0.10.0', + 'https://metamask.github.io/iframe-execution-environment/0.11.0', ), messenger: this.controllerMessenger.getRestricted({ name: 'ExecutionService', @@ -719,6 +728,7 @@ export default class MetamaskController extends EventEmitter { }); this.rateLimitController = new RateLimitController({ + state: initState.RateLimitController, messenger: this.controllerMessenger.getRestricted({ name: 'RateLimitController', }), @@ -1029,6 +1039,24 @@ export default class MetamaskController extends EventEmitter { // ensure isClientOpenAndUnlocked is updated when memState updates this.on('update', (memState) => this._onStateUpdate(memState)); + /** + * All controllers in Memstore but not in store. They are not persisted. + * On chrome profile re-start, they will be re-initialized. + */ + const resetOnRestartStore = { + AccountTracker: this.accountTracker.store, + TxController: this.txController.memStore, + TokenRatesController: this.tokenRatesController, + MessageManager: this.messageManager.memStore, + PersonalMessageManager: this.personalMessageManager.memStore, + DecryptMessageManager: this.decryptMessageManager.memStore, + EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore, + TypesMessageManager: this.typedMessageManager.memStore, + SwapsController: this.swapsController.store, + EnsController: this.ensController.store, + ApprovalController: this.approvalController, + }; + this.store.updateStructure({ AppStateController: this.appStateController.store, TransactionController: this.txController.store, @@ -1057,21 +1085,14 @@ export default class MetamaskController extends EventEmitter { CronjobController: this.cronjobController, NotificationController: this.notificationController, ///: END:ONLY_INCLUDE_IN + ...resetOnRestartStore, }); this.memStore = new ComposableObservableStore({ config: { AppStateController: this.appStateController.store, NetworkController: this.networkController.store, - AccountTracker: this.accountTracker.store, - TxController: this.txController.memStore, CachedBalancesController: this.cachedBalancesController.store, - TokenRatesController: this.tokenRatesController, - MessageManager: this.messageManager.memStore, - PersonalMessageManager: this.personalMessageManager.memStore, - DecryptMessageManager: this.decryptMessageManager.memStore, - EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore, - TypesMessageManager: this.typedMessageManager.memStore, KeyringController: this.keyringController.memStore, PreferencesController: this.preferencesController.store, MetaMetricsController: this.metaMetricsController.store, @@ -1085,9 +1106,6 @@ export default class MetamaskController extends EventEmitter { PermissionLogController: this.permissionLogController.store, SubjectMetadataController: this.subjectMetadataController, BackupController: this.backupController, - SwapsController: this.swapsController.store, - EnsController: this.ensController.store, - ApprovalController: this.approvalController, AnnouncementController: this.announcementController, GasFeeController: this.gasFeeController, TokenListController: this.tokenListController, @@ -1099,18 +1117,43 @@ export default class MetamaskController extends EventEmitter { CronjobController: this.cronjobController, NotificationController: this.notificationController, ///: END:ONLY_INCLUDE_IN + ...resetOnRestartStore, }, controllerMessenger: this.controllerMessenger, }); - this.memStore.subscribe(this.sendUpdate.bind(this)); - const password = process.env.CONF?.PASSWORD; + // if this is the first time, clear the state of by calling these methods + const resetMethods = [ + this.accountTracker.resetState, + this.txController.resetState, + this.messageManager.resetState, + this.personalMessageManager.resetState, + this.decryptMessageManager.resetState, + this.encryptionPublicKeyManager.resetState, + this.typedMessageManager.resetState, + this.swapsController.resetState, + this.ensController.resetState, + this.approvalController.clear.bind(this.approvalController), + // WE SHOULD ADD TokenListController.resetState here too. But it's not implemented yet. + ]; + + if (isManifestV3) { + if (globalThis.isFirstTimeProfileLoaded === true) { + this.resetStates(resetMethods); + } + } else { + // it's always the first time in MV2 + this.resetStates(resetMethods); + } + + // Automatic login via config password or loginToken if ( - password && !this.isUnlocked() && this.onboardingController.store.getState().completedOnboarding ) { - this.submitPassword(password); + this._loginUser(); + } else { + this._startUISync(); } // Lazily update the store with the current extension environment @@ -1137,6 +1180,18 @@ export default class MetamaskController extends EventEmitter { checkForMultipleVersionsRunning(); } + resetStates(resetMethods) { + resetMethods.forEach((resetMethod) => { + try { + resetMethod(); + } catch (err) { + console.error(err); + } + }); + + globalThis.isFirstTimeProfileLoaded = false; + } + ///: BEGIN:ONLY_INCLUDE_IN(flask) /** * Constructor helper for getting Snap permission specifications. @@ -2110,6 +2165,8 @@ export default class MetamaskController extends EventEmitter { ///: BEGIN:ONLY_INCLUDE_IN(flask) // Clear snap state this.snapController.clearState(); + // Clear notification state + this.notificationController.clear(); ///: END:ONLY_INCLUDE_IN // clear accounts in accountTracker @@ -2334,6 +2391,68 @@ export default class MetamaskController extends EventEmitter { return this.keyringController.fullUpdate(); } + async _loginUser() { + try { + // Automatic login via config password + const password = process.env.CONF?.PASSWORD; + if (password) { + await this.submitPassword(password); + } + // Automatic login via storage encryption key + else if (isManifestV3) { + await this.submitEncryptionKey(); + } + // Updating accounts in this.accountTracker before starting UI syncing ensure that + // state has account balance before it is synced with UI + await this.accountTracker._updateAccounts(); + } finally { + this._startUISync(); + } + } + + _startUISync() { + // Message startUISync is used in MV3 to start syncing state with UI + // Sending this message after login is completed helps to ensure that incomplete state without + // account details are not flushed to UI. + this.emit('startUISync'); + this.startUISync = true; + this.memStore.subscribe(this.sendUpdate.bind(this)); + } + + /** + * Submits a user's encryption key to log the user in via login token + */ + async submitEncryptionKey() { + try { + const { loginToken, loginSalt } = await browser.storage.session.get([ + 'loginToken', + 'loginSalt', + ]); + if (loginToken && loginSalt) { + const { vault } = this.keyringController.store.getState(); + + if (vault.salt !== loginSalt) { + console.warn( + 'submitEncryptionKey: Stored salt and vault salt do not match', + ); + await this.clearLoginArtifacts(); + return; + } + + await this.keyringController.submitEncryptionKey(loginToken, loginSalt); + } + } catch (e) { + // If somehow this login token doesn't work properly, + // remove it and the user will get shown back to the unlock screen + await this.clearLoginArtifacts(); + throw e; + } + } + + async clearLoginArtifacts() { + await browser.storage.session.remove(['loginToken', 'loginSalt']); + } + /** * Submits a user's password to check its validity. * @@ -3472,6 +3591,23 @@ export default class MetamaskController extends EventEmitter { }); }; this.on('update', handleUpdate); + const startUISync = () => { + if (outStream._writableState.ended) { + return; + } + // send notification to client-side + outStream.write({ + jsonrpc: '2.0', + method: 'startUISync', + }); + }; + + if (this.startUISync) { + startUISync(); + } else { + this.once('startUISync', startUISync); + } + outStream.on('end', () => { this.activeControllerConnections -= 1; this.emit( @@ -3915,12 +4051,20 @@ export default class MetamaskController extends EventEmitter { * @private */ async _onKeyringControllerUpdate(state) { - const { keyrings } = state; + const { + keyrings, + encryptionKey: loginToken, + encryptionSalt: loginSalt, + } = state; const addresses = keyrings.reduce( (acc, { accounts }) => acc.concat(accounts), [], ); + if (isManifestV3) { + await browser.storage.session.set({ loginToken, loginSalt }); + } + if (!addresses.length) { return; } @@ -4312,6 +4456,10 @@ export default class MetamaskController extends EventEmitter { ); ledgerKeyring?.destroy?.(); + if (isManifestV3) { + this.clearLoginArtifacts(); + } + return this.keyringController.setLocked(); } diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 5fc7232ce4e9..b2aaf665f805 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -102,11 +102,14 @@ const CUSTOM_RPC_CHAIN_ID = '0x539'; describe('MetaMaskController', function () { let metamaskController; + const sandbox = sinon.createSandbox(); const noop = () => undefined; before(async function () { + globalThis.isFirstTimeProfileLoaded = true; await ganacheServer.start(); + sinon.spy(MetaMaskController.prototype, 'resetStates'); }); beforeEach(function () { @@ -160,6 +163,18 @@ describe('MetaMaskController', function () { await ganacheServer.quit(); }); + describe('should reset states on first time profile load', function () { + it('should reset state', function () { + assert(metamaskController.resetStates.calledOnce); + assert.equal(globalThis.isFirstTimeProfileLoaded, false); + }); + + it('should not reset states if already set', function () { + // global.isFirstTime should also remain false + assert.equal(globalThis.isFirstTimeProfileLoaded, false); + }); + }); + describe('#getAccounts', function () { it('returns first address when dapp calls web3.eth.getAccounts', async function () { const password = 'a-fake-password'; diff --git a/app/scripts/ui.js b/app/scripts/ui.js index c0f0bb7d1130..d335f60e544e 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -15,7 +15,6 @@ import launchMetaMaskUi, { updateBackgroundConnection } from '../../ui'; import { ENVIRONMENT_TYPE_FULLSCREEN, ENVIRONMENT_TYPE_POPUP, - EXTENSION_MESSAGES, PLATFORM_FIREFOX, } from '../../shared/constants/app'; import { isManifestV3 } from '../../shared/modules/mv3.utils'; @@ -117,11 +116,13 @@ async function start() { if (isManifestV3) { /* * In case of MV3 the issue of blank screen was very frequent, it is caused by UI initialising before background is ready to send state. - * Code below ensures that UI is rendered only after CONNECTION_READY message is received thus background is ready. + * Code below ensures that UI is rendered only after "CONNECTION_READY" or "startUISync" + * messages are received thus the background is ready, and ensures that streams and + * phishing warning page load only after the "startUISync" message is received. * In case the UI is already rendered, only update the streams. */ const messageListener = async (message) => { - if (message?.name === EXTENSION_MESSAGES.CONNECTION_READY) { + if (message?.data?.method === 'startUISync') { if (isUIInitialised) { // Currently when service worker is revived we create new streams // in later version we might try to improve it by reviving same streams. diff --git a/app/scripts/use-snow.js b/app/scripts/use-snow.js new file mode 100644 index 000000000000..11b01d78d1de --- /dev/null +++ b/app/scripts/use-snow.js @@ -0,0 +1,8 @@ +// eslint-disable-next-line import/unambiguous +(function () { + const log = console.log.bind(console); + const msg = 'SNOW INTERCEPTED NEW WINDOW CREATION IN METAMASK APP: '; + window.top.SNOW((win) => { + log(msg, win, win?.frameElement); + }); +})(); diff --git a/development/build/index.js b/development/build/index.js index a1a6874e68f6..eb93a6bde193 100755 --- a/development/build/index.js +++ b/development/build/index.js @@ -66,6 +66,7 @@ async function defineAndRunBuildTasks() { isLavaMoat, policyOnly, shouldIncludeLockdown, + shouldIncludeSnow, shouldLintFenceFiles, skipStats, version, @@ -81,6 +82,7 @@ async function defineAndRunBuildTasks() { livereload, browserPlatforms, shouldIncludeLockdown, + shouldIncludeSnow, buildType, }); @@ -230,6 +232,12 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`, 'Whether to include SES lockdown files in the extension bundle. Setting this to `false` can be useful during development if you want to handle lockdown errors later.', type: 'boolean', }) + .option('snow', { + default: true, + description: + 'Whether to include Snow files in the extension bundle. Setting this to `false` can be useful during development if you want to handle Snow errors later.', + type: 'boolean', + }) .option('policy-only', { default: false, description: @@ -263,6 +271,7 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`, buildVersion, lintFenceFiles, lockdown, + snow, policyOnly, skipStats, task, @@ -292,6 +301,7 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`, isLavaMoat: process.argv[0].includes('lavamoat'), policyOnly, shouldIncludeLockdown: lockdown, + shouldIncludeSnow: snow, shouldLintFenceFiles, skipStats, version, diff --git a/development/build/static.js b/development/build/static.js index 7e3ccf02fadb..af1b55c93671 100644 --- a/development/build/static.js +++ b/development/build/static.js @@ -15,11 +15,21 @@ module.exports = function createStaticAssetTasks({ livereload, browserPlatforms, shouldIncludeLockdown = true, + shouldIncludeSnow = true, buildType, }) { - const [copyTargetsProd, copyTargetsDev] = getCopyTargets( - shouldIncludeLockdown, - ); + const copyTargetsProds = {}; + const copyTargetsDevs = {}; + + browserPlatforms.forEach((browser) => { + const [copyTargetsProd, copyTargetsDev] = getCopyTargets( + shouldIncludeLockdown, + // Snow currently only works on Chromium based browsers + shouldIncludeSnow && browser === 'chrome', + ); + copyTargetsProds[browser] = copyTargetsProd; + copyTargetsDevs[browser] = copyTargetsDev; + }); const additionalBuildTargets = { [BuildType.beta]: [ @@ -37,60 +47,60 @@ module.exports = function createStaticAssetTasks({ }; if (Object.keys(additionalBuildTargets).includes(buildType)) { - copyTargetsProd.push(...additionalBuildTargets[buildType]); - copyTargetsDev.push(...additionalBuildTargets[buildType]); + Object.entries(copyTargetsProds).forEach(([_, copyTargetsProd]) => + copyTargetsProd.push(...additionalBuildTargets[buildType]), + ); + Object.entries(copyTargetsDevs).forEach(([_, copyTargetsDev]) => + copyTargetsDev.push(...additionalBuildTargets[buildType]), + ); } - const prod = createTask( - TASKS.STATIC_PROD, - composeSeries( - ...copyTargetsProd.map((target) => { - return async function copyStaticAssets() { - await performCopy(target); - }; - }), - ), - ); - const dev = createTask( - TASKS.STATIC_DEV, - composeSeries( - ...copyTargetsDev.map((target) => { - return async function copyStaticAssets() { - await setupLiveCopy(target); - }; - }), - ), - ); + const prodTasks = []; + Object.entries(copyTargetsProds).forEach(([browser, copyTargetsProd]) => { + copyTargetsProd.forEach((target) => { + prodTasks.push(async function copyStaticAssets() { + await performCopy(target, browser); + }); + }); + }); + + const devTasks = []; + Object.entries(copyTargetsDevs).forEach(([browser, copyTargetsDev]) => { + copyTargetsDev.forEach((target) => { + devTasks.push(async function copyStaticAssets() { + await setupLiveCopy(target, browser); + }); + }); + }); + + const prod = createTask(TASKS.STATIC_PROD, composeSeries(...prodTasks)); + const dev = createTask(TASKS.STATIC_DEV, composeSeries(...devTasks)); return { dev, prod }; - async function setupLiveCopy(target) { + async function setupLiveCopy(target, browser) { const pattern = target.pattern || '/**/*'; watch(target.src + pattern, (event) => { livereload.changed(event.path); - performCopy(target); + performCopy(target, browser); }); - await performCopy(target); + await performCopy(target, browser); } - async function performCopy(target) { - await Promise.all( - browserPlatforms.map(async (platform) => { - if (target.pattern) { - await copyGlob( - target.src, - `${target.src}${target.pattern}`, - `./dist/${platform}/${target.dest}`, - ); - } else { - await copyGlob( - target.src, - `${target.src}`, - `./dist/${platform}/${target.dest}`, - ); - } - }), - ); + async function performCopy(target, browser) { + if (target.pattern) { + await copyGlob( + target.src, + `${target.src}${target.pattern}`, + `./dist/${browser}/${target.dest}`, + ); + } else { + await copyGlob( + target.src, + `${target.src}`, + `./dist/${browser}/${target.dest}`, + ); + } } async function copyGlob(baseDir, srcGlob, dest) { @@ -104,7 +114,7 @@ module.exports = function createStaticAssetTasks({ } }; -function getCopyTargets(shouldIncludeLockdown) { +function getCopyTargets(shouldIncludeLockdown, shouldIncludeSnow) { const allCopyTargets = [ { src: `./app/_locales/`, @@ -147,6 +157,16 @@ function getCopyTargets(shouldIncludeLockdown) { src: `./node_modules/globalthis/dist/browser.js`, dest: `globalthis.js`, }, + { + src: shouldIncludeSnow + ? `./node_modules/@lavamoat/snow/snow.prod.js` + : EMPTY_JS_FILE, + dest: `snow.js`, + }, + { + src: shouldIncludeSnow ? `./app/scripts/use-snow.js` : EMPTY_JS_FILE, + dest: `use-snow.js`, + }, { src: shouldIncludeLockdown ? `./node_modules/ses/dist/lockdown.umd.min.js` diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index ac2f28d589c4..ec8653289217 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -490,6 +490,7 @@ "@metamask/controllers>abort-controller": true, "@metamask/controllers>async-mutex": true, "@metamask/controllers>eth-json-rpc-infura": true, + "@metamask/controllers>eth-keyring-controller": true, "@metamask/controllers>eth-method-registry": true, "@metamask/controllers>eth-phishing-detect": true, "@metamask/controllers>ethereumjs-wallet": true, @@ -504,7 +505,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -660,6 +660,37 @@ "safe-event-emitter": true } }, + "@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, + "@metamask/controllers>eth-keyring-controller>browser-passworder": { + "globals": { + "btoa": true, + "crypto.getRandomValues": true, + "crypto.subtle.decrypt": true, + "crypto.subtle.deriveKey": true, + "crypto.subtle.encrypt": true, + "crypto.subtle.importKey": true + }, + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder>browserify-unibabel": true + } + }, + "@metamask/controllers>eth-keyring-controller>browser-passworder>browserify-unibabel": { + "globals": { + "atob": true, + "btoa": true + } + }, "@metamask/controllers>eth-method-registry": { "packages": { "@metamask/controllers>eth-method-registry>ethjs": true @@ -1274,6 +1305,7 @@ "@metamask/controllers>web3": true, "@metamask/controllers>web3-provider-engine": true, "@metamask/metamask-eth-abis": true, + "@metamask/smart-transactions-controller>@metamask/controllers>eth-keyring-controller": true, "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry": true, "@metamask/smart-transactions-controller>@metamask/controllers>ethereumjs-wallet": true, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": true, @@ -1282,7 +1314,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -1296,6 +1327,18 @@ "uuid": true } }, + "@metamask/smart-transactions-controller>@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry>ethjs": true @@ -1396,7 +1439,7 @@ "setTimeout": true } }, - "@metamask/snap-controllers>nanoid": { + "@metamask/snaps-controllers>nanoid": { "globals": { "crypto.getRandomValues": true } @@ -2354,7 +2397,7 @@ "TextEncoder": true }, "packages": { - "@metamask/snap-utils>superstruct": true, + "@metamask/snaps-utils>superstruct": true, "browserify>buffer": true, "nock>debug": true } @@ -2465,8 +2508,8 @@ "browserify>buffer": true, "browserify>events": true, "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/browser-passworder": true, "eth-keyring-controller>@metamask/eth-hd-keyring": true, - "eth-keyring-controller>browser-passworder": true, "eth-keyring-controller>eth-simple-keyring": true, "eth-keyring-controller>obs-store": true, "eth-sig-util": true @@ -2480,6 +2523,15 @@ "ethereumjs-wallet>randombytes": true } }, + "eth-keyring-controller>@metamask/browser-passworder": { + "globals": { + "btoa": true, + "crypto": true + }, + "packages": { + "browserify>buffer": true + } + }, "eth-keyring-controller>@metamask/eth-hd-keyring": { "packages": { "browserify>buffer": true, @@ -2509,21 +2561,6 @@ "msCrypto": true } }, - "eth-keyring-controller>browser-passworder": { - "globals": { - "btoa": true, - "crypto": true - }, - "packages": { - "eth-keyring-controller>browser-passworder>browserify-unibabel": true - } - }, - "eth-keyring-controller>browser-passworder>browserify-unibabel": { - "globals": { - "atob": true, - "btoa": true - } - }, "eth-keyring-controller>eth-simple-keyring": { "packages": { "browserify>buffer": true, @@ -4947,6 +4984,7 @@ "Node": true }, "packages": { + "@lavamoat/snow>is-cross-origin>is-window": true, "proxyquire>fill-keys>is-object": true, "react-inspector>is-dom>is-window": true } @@ -5346,4 +5384,4 @@ } } } -} \ No newline at end of file +} diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index f2fa87fe8301..fcbf820d7a74 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -635,6 +635,7 @@ "@metamask/controllers>abort-controller": true, "@metamask/controllers>async-mutex": true, "@metamask/controllers>eth-json-rpc-infura": true, + "@metamask/controllers>eth-keyring-controller": true, "@metamask/controllers>eth-method-registry": true, "@metamask/controllers>eth-phishing-detect": true, "@metamask/controllers>ethereumjs-wallet": true, @@ -649,7 +650,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -805,6 +805,37 @@ "safe-event-emitter": true } }, + "@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, + "@metamask/controllers>eth-keyring-controller>browser-passworder": { + "globals": { + "btoa": true, + "crypto.getRandomValues": true, + "crypto.subtle.decrypt": true, + "crypto.subtle.deriveKey": true, + "crypto.subtle.encrypt": true, + "crypto.subtle.importKey": true + }, + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder>browserify-unibabel": true + } + }, + "@metamask/controllers>eth-keyring-controller>browser-passworder>browserify-unibabel": { + "globals": { + "atob": true, + "btoa": true + } + }, "@metamask/controllers>eth-method-registry": { "packages": { "@metamask/controllers>eth-method-registry>ethjs": true @@ -1428,8 +1459,9 @@ "@metamask/rpc-methods>@metamask/controllers": true, "@metamask/rpc-methods>@metamask/key-tree": true, "@metamask/rpc-methods>nanoid": true, - "@metamask/snap-utils": true, - "@metamask/snap-utils>superstruct": true, + "@metamask/snaps-utils": true, + "@metamask/snaps-utils>@noble/hashes": true, + "@metamask/snaps-utils>superstruct": true, "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true } @@ -1462,6 +1494,7 @@ "@metamask/controllers>web3": true, "@metamask/controllers>web3-provider-engine": true, "@metamask/metamask-eth-abis": true, + "@metamask/rpc-methods>@metamask/controllers>eth-keyring-controller": true, "@metamask/rpc-methods>@metamask/controllers>eth-method-registry": true, "@metamask/rpc-methods>@metamask/controllers>ethereumjs-wallet": true, "@metamask/rpc-methods>nanoid": true, @@ -1470,7 +1503,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -1484,6 +1516,18 @@ "uuid": true } }, + "@metamask/rpc-methods>@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, "@metamask/rpc-methods>@metamask/controllers>eth-method-registry": { "packages": { "@metamask/rpc-methods>@metamask/controllers>eth-method-registry>ethjs": true @@ -1570,8 +1614,8 @@ "@metamask/rpc-methods>@metamask/key-tree>@noble/ed25519": true, "@metamask/rpc-methods>@metamask/key-tree>@noble/secp256k1": true, "@metamask/rpc-methods>@metamask/key-tree>@scure/bip39": true, - "@metamask/snap-utils>@noble/hashes": true, - "@metamask/snap-utils>@scure/base": true, + "@metamask/snaps-utils>@noble/hashes": true, + "@metamask/snaps-utils>@scure/base": true, "eth-block-tracker>@metamask/utils": true } }, @@ -1593,8 +1637,8 @@ }, "@metamask/rpc-methods>@metamask/key-tree>@scure/bip39": { "packages": { - "@metamask/snap-utils>@noble/hashes": true, - "@metamask/snap-utils>@scure/base": true + "@metamask/snaps-utils>@noble/hashes": true, + "@metamask/snaps-utils>@scure/base": true } }, "@metamask/rpc-methods>nanoid": { @@ -1650,6 +1694,7 @@ "@metamask/controllers>web3": true, "@metamask/controllers>web3-provider-engine": true, "@metamask/metamask-eth-abis": true, + "@metamask/smart-transactions-controller>@metamask/controllers>eth-keyring-controller": true, "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry": true, "@metamask/smart-transactions-controller>@metamask/controllers>ethereumjs-wallet": true, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": true, @@ -1658,7 +1703,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -1672,6 +1716,18 @@ "uuid": true } }, + "@metamask/smart-transactions-controller>@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry>ethjs": true @@ -1772,7 +1828,7 @@ "setTimeout": true } }, - "@metamask/snap-controllers": { + "@metamask/snaps-controllers": { "globals": { "URL": true, "clearTimeout": true, @@ -1790,23 +1846,23 @@ "@metamask/post-message-stream": true, "@metamask/providers>@metamask/object-multiplex": true, "@metamask/rpc-methods": true, - "@metamask/snap-controllers>@metamask/browser-passworder": true, - "@metamask/snap-controllers>@metamask/controllers": true, - "@metamask/snap-controllers>@xstate/fsm": true, - "@metamask/snap-controllers>concat-stream": true, - "@metamask/snap-controllers>gunzip-maybe": true, - "@metamask/snap-controllers>json-rpc-middleware-stream": true, - "@metamask/snap-controllers>nanoid": true, - "@metamask/snap-controllers>readable-web-to-node-stream": true, - "@metamask/snap-controllers>tar-stream": true, - "@metamask/snap-utils": true, + "@metamask/snaps-controllers>@metamask/browser-passworder": true, + "@metamask/snaps-controllers>@metamask/controllers": true, + "@metamask/snaps-controllers>@xstate/fsm": true, + "@metamask/snaps-controllers>concat-stream": true, + "@metamask/snaps-controllers>gunzip-maybe": true, + "@metamask/snaps-controllers>json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>nanoid": true, + "@metamask/snaps-controllers>readable-web-to-node-stream": true, + "@metamask/snaps-controllers>tar-stream": true, + "@metamask/snaps-utils": true, "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true, "json-rpc-engine": true, "pump": true } }, - "@metamask/snap-controllers>@metamask/browser-passworder": { + "@metamask/snaps-controllers>@metamask/browser-passworder": { "globals": { "btoa": true, "crypto.getRandomValues": true, @@ -1819,7 +1875,7 @@ "browserify>buffer": true } }, - "@metamask/snap-controllers>@metamask/controllers": { + "@metamask/snaps-controllers>@metamask/controllers": { "globals": { "Headers": true, "URL": true, @@ -1847,15 +1903,15 @@ "@metamask/controllers>web3": true, "@metamask/controllers>web3-provider-engine": true, "@metamask/metamask-eth-abis": true, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry": true, - "@metamask/snap-controllers>@metamask/controllers>ethereumjs-wallet": true, - "@metamask/snap-controllers>nanoid": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-keyring-controller": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry": true, + "@metamask/snaps-controllers>@metamask/controllers>ethereumjs-wallet": true, + "@metamask/snaps-controllers>nanoid": true, "browserify>buffer": true, "browserify>events": true, "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -1869,21 +1925,33 @@ "uuid": true } }, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry": { + "@metamask/snaps-controllers>@metamask/controllers>eth-keyring-controller": { "packages": { - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs": true + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true } }, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs": { + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry": { + "packages": { + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs": true + } + }, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs": { "globals": { "clearInterval": true, "setInterval": true }, "packages": { - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>bn.js": true, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-abi": true, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract": true, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-query": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>bn.js": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-abi": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-query": true, "browserify>buffer": true, "ethjs>ethjs-filter": true, "ethjs>ethjs-provider-http": true, @@ -1893,17 +1961,17 @@ "ethjs>number-to-bn": true } }, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-abi": { + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-abi": { "packages": { - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>bn.js": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>bn.js": true, "browserify>buffer": true, "ethjs>js-sha3": true, "ethjs>number-to-bn": true } }, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract": { + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract": { "packages": { - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract>ethjs-abi": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract>ethjs-abi": true, "ethjs-query>babel-runtime": true, "ethjs>ethjs-filter": true, "ethjs>ethjs-util": true, @@ -1911,15 +1979,15 @@ "promise-to-callback": true } }, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract>ethjs-abi": { + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-contract>ethjs-abi": { "packages": { - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>bn.js": true, + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>bn.js": true, "browserify>buffer": true, "ethjs>js-sha3": true, "ethjs>number-to-bn": true } }, - "@metamask/snap-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-query": { + "@metamask/snaps-controllers>@metamask/controllers>eth-method-registry>ethjs>ethjs-query": { "globals": { "console": true }, @@ -1930,9 +1998,9 @@ "promise-to-callback": true } }, - "@metamask/snap-controllers>@metamask/controllers>ethereumjs-wallet": { + "@metamask/snaps-controllers>@metamask/controllers>ethereumjs-wallet": { "packages": { - "@metamask/snap-controllers>@metamask/controllers>ethereumjs-wallet>uuid": true, + "@metamask/snaps-controllers>@metamask/controllers>ethereumjs-wallet>uuid": true, "@truffle/codec>utf8": true, "browserify>buffer": true, "browserify>crypto-browserify": true, @@ -1944,20 +2012,20 @@ "ethers>@ethersproject/json-wallets>scrypt-js": true } }, - "@metamask/snap-controllers>@metamask/controllers>ethereumjs-wallet>uuid": { + "@metamask/snaps-controllers>@metamask/controllers>ethereumjs-wallet>uuid": { "globals": { "crypto": true, "msCrypto": true } }, - "@metamask/snap-controllers>concat-stream": { + "@metamask/snaps-controllers>concat-stream": { "packages": { - "@metamask/snap-controllers>concat-stream>readable-stream": true, + "@metamask/snaps-controllers>concat-stream>readable-stream": true, "browserify>buffer": true, "pumpify>inherits": true } }, - "@metamask/snap-controllers>concat-stream>readable-stream": { + "@metamask/snaps-controllers>concat-stream>readable-stream": { "packages": { "@storybook/api>util-deprecate": true, "browserify>browser-resolve": true, @@ -1968,19 +2036,19 @@ "pumpify>inherits": true } }, - "@metamask/snap-controllers>gunzip-maybe": { + "@metamask/snaps-controllers>gunzip-maybe": { "packages": { - "@metamask/snap-controllers>gunzip-maybe>browserify-zlib": true, - "@metamask/snap-controllers>gunzip-maybe>is-deflate": true, - "@metamask/snap-controllers>gunzip-maybe>is-gzip": true, - "@metamask/snap-controllers>gunzip-maybe>peek-stream": true, - "@metamask/snap-controllers>gunzip-maybe>pumpify": true, - "@metamask/snap-controllers>gunzip-maybe>through2": true + "@metamask/snaps-controllers>gunzip-maybe>browserify-zlib": true, + "@metamask/snaps-controllers>gunzip-maybe>is-deflate": true, + "@metamask/snaps-controllers>gunzip-maybe>is-gzip": true, + "@metamask/snaps-controllers>gunzip-maybe>peek-stream": true, + "@metamask/snaps-controllers>gunzip-maybe>pumpify": true, + "@metamask/snaps-controllers>gunzip-maybe>through2": true } }, - "@metamask/snap-controllers>gunzip-maybe>browserify-zlib": { + "@metamask/snaps-controllers>gunzip-maybe>browserify-zlib": { "packages": { - "@metamask/snap-controllers>gunzip-maybe>browserify-zlib>pako": true, + "@metamask/snaps-controllers>gunzip-maybe>browserify-zlib>pako": true, "browserify>assert": true, "browserify>buffer": true, "browserify>process": true, @@ -1988,15 +2056,15 @@ "readable-stream": true } }, - "@metamask/snap-controllers>gunzip-maybe>peek-stream": { + "@metamask/snaps-controllers>gunzip-maybe>peek-stream": { "packages": { - "@metamask/snap-controllers>gunzip-maybe>peek-stream>duplexify": true, - "@metamask/snap-controllers>gunzip-maybe>peek-stream>through2": true, + "@metamask/snaps-controllers>gunzip-maybe>peek-stream>duplexify": true, + "@metamask/snaps-controllers>gunzip-maybe>peek-stream>through2": true, "browserify>buffer": true, "terser>source-map-support>buffer-from": true } }, - "@metamask/snap-controllers>gunzip-maybe>peek-stream>duplexify": { + "@metamask/snaps-controllers>gunzip-maybe>peek-stream>duplexify": { "packages": { "browserify>buffer": true, "browserify>process": true, @@ -2006,7 +2074,7 @@ "readable-stream": true } }, - "@metamask/snap-controllers>gunzip-maybe>peek-stream>through2": { + "@metamask/snaps-controllers>gunzip-maybe>peek-stream>through2": { "packages": { "browserify>process": true, "browserify>util": true, @@ -2014,14 +2082,14 @@ "watchify>xtend": true } }, - "@metamask/snap-controllers>gunzip-maybe>pumpify": { + "@metamask/snaps-controllers>gunzip-maybe>pumpify": { "packages": { - "@metamask/snap-controllers>gunzip-maybe>pumpify>duplexify": true, - "@metamask/snap-controllers>gunzip-maybe>pumpify>pump": true, + "@metamask/snaps-controllers>gunzip-maybe>pumpify>duplexify": true, + "@metamask/snaps-controllers>gunzip-maybe>pumpify>pump": true, "pumpify>inherits": true } }, - "@metamask/snap-controllers>gunzip-maybe>pumpify>duplexify": { + "@metamask/snaps-controllers>gunzip-maybe>pumpify>duplexify": { "packages": { "browserify>buffer": true, "browserify>process": true, @@ -2031,14 +2099,14 @@ "readable-stream": true } }, - "@metamask/snap-controllers>gunzip-maybe>pumpify>pump": { + "@metamask/snaps-controllers>gunzip-maybe>pumpify>pump": { "packages": { "browserify>browser-resolve": true, "end-of-stream": true, "pump>once": true } }, - "@metamask/snap-controllers>gunzip-maybe>through2": { + "@metamask/snaps-controllers>gunzip-maybe>through2": { "packages": { "browserify>process": true, "browserify>util": true, @@ -2046,8 +2114,9 @@ "watchify>xtend": true } }, - "@metamask/snap-controllers>json-rpc-middleware-stream": { + "@metamask/snaps-controllers>json-rpc-middleware-stream": { "globals": { + "console.warn": true, "setTimeout": true }, "packages": { @@ -2055,17 +2124,17 @@ "readable-stream": true } }, - "@metamask/snap-controllers>nanoid": { + "@metamask/snaps-controllers>nanoid": { "globals": { "crypto.getRandomValues": true } }, - "@metamask/snap-controllers>readable-web-to-node-stream": { + "@metamask/snaps-controllers>readable-web-to-node-stream": { "packages": { - "@metamask/snap-controllers>readable-web-to-node-stream>readable-stream": true + "@metamask/snaps-controllers>readable-web-to-node-stream>readable-stream": true } }, - "@metamask/snap-controllers>readable-web-to-node-stream>readable-stream": { + "@metamask/snaps-controllers>readable-web-to-node-stream>readable-stream": { "packages": { "@storybook/api>util-deprecate": true, "browserify>browser-resolve": true, @@ -2076,10 +2145,10 @@ "pumpify>inherits": true } }, - "@metamask/snap-controllers>tar-stream": { + "@metamask/snaps-controllers>tar-stream": { "packages": { - "@metamask/snap-controllers>tar-stream>fs-constants": true, - "@metamask/snap-controllers>tar-stream>readable-stream": true, + "@metamask/snaps-controllers>tar-stream>fs-constants": true, + "@metamask/snaps-controllers>tar-stream>readable-stream": true, "browserify>buffer": true, "browserify>process": true, "browserify>string_decoder": true, @@ -2089,12 +2158,12 @@ "pumpify>inherits": true } }, - "@metamask/snap-controllers>tar-stream>fs-constants": { + "@metamask/snaps-controllers>tar-stream>fs-constants": { "packages": { "browserify>constants-browserify": true } }, - "@metamask/snap-controllers>tar-stream>readable-stream": { + "@metamask/snaps-controllers>tar-stream>readable-stream": { "packages": { "@storybook/api>util-deprecate": true, "browserify>browser-resolve": true, @@ -2105,18 +2174,18 @@ "pumpify>inherits": true } }, - "@metamask/snap-utils": { + "@metamask/snaps-utils": { "globals": { "URL": true }, "packages": { "@babel/core": true, "@babel/core>@babel/types": true, - "@metamask/snap-utils>@noble/hashes": true, - "@metamask/snap-utils>@scure/base": true, - "@metamask/snap-utils>cron-parser": true, - "@metamask/snap-utils>rfdc": true, - "@metamask/snap-utils>superstruct": true, + "@metamask/snaps-utils>@noble/hashes": true, + "@metamask/snaps-utils>@scure/base": true, + "@metamask/snaps-utils>cron-parser": true, + "@metamask/snaps-utils>rfdc": true, + "@metamask/snaps-utils>superstruct": true, "browserify": true, "browserify>buffer": true, "browserify>crypto-browserify": true, @@ -2127,25 +2196,25 @@ "semver": true } }, - "@metamask/snap-utils>@noble/hashes": { + "@metamask/snaps-utils>@noble/hashes": { "globals": { "TextEncoder": true, "crypto": true } }, - "@metamask/snap-utils>@scure/base": { + "@metamask/snaps-utils>@scure/base": { "globals": { "TextDecoder": true, "TextEncoder": true } }, - "@metamask/snap-utils>cron-parser": { + "@metamask/snaps-utils>cron-parser": { "packages": { "browserify>browser-resolve": true, "luxon": true } }, - "@metamask/snap-utils>rfdc": { + "@metamask/snaps-utils>rfdc": { "packages": { "browserify>buffer": true } @@ -3137,7 +3206,7 @@ "TextEncoder": true }, "packages": { - "@metamask/snap-utils>superstruct": true, + "@metamask/snaps-utils>superstruct": true, "browserify>buffer": true, "nock>debug": true } @@ -3248,8 +3317,8 @@ "browserify>buffer": true, "browserify>events": true, "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/browser-passworder": true, "eth-keyring-controller>@metamask/eth-hd-keyring": true, - "eth-keyring-controller>browser-passworder": true, "eth-keyring-controller>eth-simple-keyring": true, "eth-keyring-controller>obs-store": true, "eth-sig-util": true @@ -3263,6 +3332,15 @@ "ethereumjs-wallet>randombytes": true } }, + "eth-keyring-controller>@metamask/browser-passworder": { + "globals": { + "btoa": true, + "crypto": true + }, + "packages": { + "browserify>buffer": true + } + }, "eth-keyring-controller>@metamask/eth-hd-keyring": { "packages": { "browserify>buffer": true, @@ -3292,21 +3370,6 @@ "msCrypto": true } }, - "eth-keyring-controller>browser-passworder": { - "globals": { - "btoa": true, - "crypto": true - }, - "packages": { - "eth-keyring-controller>browser-passworder>browserify-unibabel": true - } - }, - "eth-keyring-controller>browser-passworder>browserify-unibabel": { - "globals": { - "atob": true, - "btoa": true - } - }, "eth-keyring-controller>eth-simple-keyring": { "packages": { "browserify>buffer": true, @@ -5780,6 +5843,7 @@ "Node": true }, "packages": { + "@lavamoat/snow>is-cross-origin>is-window": true, "proxyquire>fill-keys>is-object": true, "react-inspector>is-dom>is-window": true } @@ -6236,4 +6300,4 @@ } } } -} \ No newline at end of file +} diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index ac2f28d589c4..ec8653289217 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -490,6 +490,7 @@ "@metamask/controllers>abort-controller": true, "@metamask/controllers>async-mutex": true, "@metamask/controllers>eth-json-rpc-infura": true, + "@metamask/controllers>eth-keyring-controller": true, "@metamask/controllers>eth-method-registry": true, "@metamask/controllers>eth-phishing-detect": true, "@metamask/controllers>ethereumjs-wallet": true, @@ -504,7 +505,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -660,6 +660,37 @@ "safe-event-emitter": true } }, + "@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, + "@metamask/controllers>eth-keyring-controller>browser-passworder": { + "globals": { + "btoa": true, + "crypto.getRandomValues": true, + "crypto.subtle.decrypt": true, + "crypto.subtle.deriveKey": true, + "crypto.subtle.encrypt": true, + "crypto.subtle.importKey": true + }, + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder>browserify-unibabel": true + } + }, + "@metamask/controllers>eth-keyring-controller>browser-passworder>browserify-unibabel": { + "globals": { + "atob": true, + "btoa": true + } + }, "@metamask/controllers>eth-method-registry": { "packages": { "@metamask/controllers>eth-method-registry>ethjs": true @@ -1274,6 +1305,7 @@ "@metamask/controllers>web3": true, "@metamask/controllers>web3-provider-engine": true, "@metamask/metamask-eth-abis": true, + "@metamask/smart-transactions-controller>@metamask/controllers>eth-keyring-controller": true, "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry": true, "@metamask/smart-transactions-controller>@metamask/controllers>ethereumjs-wallet": true, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": true, @@ -1282,7 +1314,6 @@ "deep-freeze-strict": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true, - "eth-keyring-controller": true, "eth-query": true, "eth-rpc-errors": true, "eth-sig-util": true, @@ -1296,6 +1327,18 @@ "uuid": true } }, + "@metamask/smart-transactions-controller>@metamask/controllers>eth-keyring-controller": { + "packages": { + "@metamask/controllers>eth-keyring-controller>browser-passworder": true, + "browserify>buffer": true, + "browserify>events": true, + "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/eth-hd-keyring": true, + "eth-keyring-controller>eth-simple-keyring": true, + "eth-keyring-controller>obs-store": true, + "eth-sig-util": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry": { "packages": { "@metamask/smart-transactions-controller>@metamask/controllers>eth-method-registry>ethjs": true @@ -1396,7 +1439,7 @@ "setTimeout": true } }, - "@metamask/snap-controllers>nanoid": { + "@metamask/snaps-controllers>nanoid": { "globals": { "crypto.getRandomValues": true } @@ -2354,7 +2397,7 @@ "TextEncoder": true }, "packages": { - "@metamask/snap-utils>superstruct": true, + "@metamask/snaps-utils>superstruct": true, "browserify>buffer": true, "nock>debug": true } @@ -2465,8 +2508,8 @@ "browserify>buffer": true, "browserify>events": true, "eth-keyring-controller>@metamask/bip39": true, + "eth-keyring-controller>@metamask/browser-passworder": true, "eth-keyring-controller>@metamask/eth-hd-keyring": true, - "eth-keyring-controller>browser-passworder": true, "eth-keyring-controller>eth-simple-keyring": true, "eth-keyring-controller>obs-store": true, "eth-sig-util": true @@ -2480,6 +2523,15 @@ "ethereumjs-wallet>randombytes": true } }, + "eth-keyring-controller>@metamask/browser-passworder": { + "globals": { + "btoa": true, + "crypto": true + }, + "packages": { + "browserify>buffer": true + } + }, "eth-keyring-controller>@metamask/eth-hd-keyring": { "packages": { "browserify>buffer": true, @@ -2509,21 +2561,6 @@ "msCrypto": true } }, - "eth-keyring-controller>browser-passworder": { - "globals": { - "btoa": true, - "crypto": true - }, - "packages": { - "eth-keyring-controller>browser-passworder>browserify-unibabel": true - } - }, - "eth-keyring-controller>browser-passworder>browserify-unibabel": { - "globals": { - "atob": true, - "btoa": true - } - }, "eth-keyring-controller>eth-simple-keyring": { "packages": { "browserify>buffer": true, @@ -4947,6 +4984,7 @@ "Node": true }, "packages": { + "@lavamoat/snow>is-cross-origin>is-window": true, "proxyquire>fill-keys>is-object": true, "react-inspector>is-dom>is-window": true } @@ -5346,4 +5384,4 @@ } } } -} \ No newline at end of file +} diff --git a/lavamoat/browserify/policy-override.json b/lavamoat/browserify/policy-override.json index 9bc3387269c9..b8c26285a525 100644 --- a/lavamoat/browserify/policy-override.json +++ b/lavamoat/browserify/policy-override.json @@ -38,7 +38,7 @@ "crypto.getRandomValues": true } }, - "eth-keyring-controller>browser-passworder": { + "eth-keyring-controller>@metamask/browser-passworder": { "globals": { "crypto": true } @@ -87,7 +87,7 @@ "crypto.getRandomValues": true } }, - "@metamask/snap-controllers>nanoid": { + "@metamask/snaps-controllers>nanoid": { "globals": { "crypto.getRandomValues": true } diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index de3161d63beb..c080ec25a38c 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -1813,6 +1813,7 @@ }, "packages": { "chokidar>braces": true, + "chokidar>fsevents": true, "chokidar>glob-parent": true, "chokidar>is-binary-path": true, "chokidar>normalize-path": true, @@ -1839,6 +1840,12 @@ "chokidar>braces>fill-range>to-regex-range>is-number": true } }, + "chokidar>fsevents": { + "globals": { + "process.platform": true + }, + "native": true + }, "chokidar>glob-parent": { "builtin": { "os.platform": true, @@ -4184,6 +4191,7 @@ "gulp-watch>chokidar>anymatch": true, "gulp-watch>chokidar>async-each": true, "gulp-watch>chokidar>braces": true, + "gulp-watch>chokidar>fsevents": true, "gulp-watch>chokidar>is-binary-path": true, "gulp-watch>chokidar>normalize-path": true, "gulp-watch>chokidar>readdirp": true, @@ -4332,552 +4340,1319 @@ "webpack>micromatch>braces>fill-range>repeat-string": true } }, - "gulp-watch>chokidar>is-binary-path": { - "builtin": { - "path.extname": true - }, - "packages": { - "gulp-watch>chokidar>is-binary-path>binary-extensions": true - } - }, - "gulp-watch>chokidar>readdirp": { + "gulp-watch>chokidar>fsevents": { "builtin": { + "events.EventEmitter": true, + "fs.stat": true, "path.join": true, - "path.relative": true, "util.inherits": true }, "globals": { + "__dirname": true, + "process.nextTick": true, + "process.platform": true, "setImmediate": true }, "packages": { - "fs-extra>graceful-fs": true, - "gulp-watch>chokidar>readdirp>micromatch": true, - "readable-stream": true + "gulp-watch>chokidar>fsevents>node-pre-gyp": true } }, - "gulp-watch>chokidar>readdirp>micromatch": { + "gulp-watch>chokidar>fsevents>node-pre-gyp": { "builtin": { - "path.basename": true, - "path.sep": true, - "util.inspect": true + "events.EventEmitter": true, + "fs.existsSync": true, + "fs.readFileSync": true, + "fs.renameSync": true, + "path.dirname": true, + "path.existsSync": true, + "path.join": true, + "path.resolve": true, + "url.parse": true, + "url.resolve": true, + "util.inherits": true }, "globals": { - "process.platform": true + "__dirname": true, + "console.log": true, + "process.arch": true, + "process.cwd": true, + "process.env": true, + "process.platform": true, + "process.version.substr": true, + "process.versions": true }, "packages": { - "gulp-watch>chokidar>braces": true, - "gulp-watch>chokidar>readdirp>micromatch>arr-diff": true, - "gulp-watch>chokidar>readdirp>micromatch>array-unique": true, - "gulp-watch>chokidar>readdirp>micromatch>define-property": true, - "gulp-watch>chokidar>readdirp>micromatch>extend-shallow": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob": true, - "gulp-watch>chokidar>readdirp>micromatch>kind-of": true, - "webpack>micromatch>fragment-cache": true, - "webpack>micromatch>nanomatch": true, - "webpack>micromatch>object.pick": true, - "webpack>micromatch>regex-not": true, - "webpack>micromatch>snapdragon": true, - "webpack>micromatch>to-regex": true - } - }, - "gulp-watch>chokidar>readdirp>micromatch>define-property": { - "packages": { - "gulp>gulp-cli>isobject": true, - "webpack>micromatch>define-property>is-descriptor": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>detect-libc": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>semver": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extend-shallow": { - "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extend-shallow>is-extendable": true, - "webpack>micromatch>extend-shallow>assign-symbols": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>detect-libc": { + "builtin": { + "child_process.spawnSync": true, + "fs.readdirSync": true, + "os.platform": true + }, + "globals": { + "process.env": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extend-shallow>is-extendable": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt": { + "builtin": { + "path": true, + "stream.Stream": true, + "url": true + }, + "globals": { + "console": true, + "process.argv": true, + "process.env.DEBUG_NOPT": true, + "process.env.NOPT_DEBUG": true, + "process.platform": true + }, "packages": { - "@babel/register>clone-deep>is-plain-object": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>abbrev": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv": { + "builtin": { + "child_process.exec": true, + "path": true + }, + "globals": { + "process.env.COMPUTERNAME": true, + "process.env.ComSpec": true, + "process.env.EDITOR": true, + "process.env.HOSTNAME": true, + "process.env.PATH": true, + "process.env.PROMPT": true, + "process.env.PS1": true, + "process.env.Path": true, + "process.env.SHELL": true, + "process.env.USER": true, + "process.env.USERDOMAIN": true, + "process.env.USERNAME": true, + "process.env.VISUAL": true, + "process.env.path": true, + "process.nextTick": true, + "process.platform": true + }, "packages": { - "gulp-watch>chokidar>readdirp>micromatch>array-unique": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>define-property": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>extend-shallow": true, - "webpack>micromatch>fragment-cache": true, - "webpack>micromatch>regex-not": true, - "webpack>micromatch>snapdragon": true, - "webpack>micromatch>to-regex": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-homedir": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>define-property": { - "packages": { - "webpack>micromatch>define-property>is-descriptor": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-homedir": { + "builtin": { + "os.homedir": true + }, + "globals": { + "process.env": true, + "process.getuid": true, + "process.platform": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": { "globals": { - "__filename": true - }, - "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>debug": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>extend-shallow": true, - "webpack>micromatch>extglob>expand-brackets>posix-character-classes": true, - "webpack>micromatch>regex-not": true, - "webpack>micromatch>snapdragon": true, - "webpack>micromatch>to-regex": true + "process.env.SystemRoot": true, + "process.env.TEMP": true, + "process.env.TMP": true, + "process.env.TMPDIR": true, + "process.env.windir": true, + "process.platform": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>debug": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog": { "builtin": { - "fs.SyncWriteStream": true, - "net.Socket": true, - "tty.WriteStream": true, - "tty.isatty": true, + "events.EventEmitter": true, "util": true }, "globals": { - "chrome": true, - "console": true, - "document": true, - "localStorage": true, - "navigator": true, - "process": true + "process.nextTick": true, + "process.stderr": true }, "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>debug>ms": true - } - }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property": { - "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>console-control-strings": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>set-blocking": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet": { + "builtin": { + "events.EventEmitter": true, + "util.inherits": true + }, "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": true, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>kind-of": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>delegates": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream": { + "builtin": { + "events.EventEmitter": true, + "stream": true, + "util": true + }, + "globals": { + "process.browser": true, + "process.env.READABLE_STREAM": true, + "process.stderr": true, + "process.stdout": true, + "process.version.slice": true, + "setImmediate": true + }, "packages": { - "gulp-watch>anymatch>micromatch>kind-of": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>core-util-is": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>isarray": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>process-nextick-args": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>string_decoder": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>util-deprecate": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>inherits": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>tar>safe-buffer": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": { - "packages": { - "gulp-watch>anymatch>micromatch>kind-of": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>core-util-is": { + "globals": { + "Buffer.isBuffer": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>extend-shallow": { - "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>extend-shallow>is-extendable": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>process-nextick-args": { + "globals": { + "process": true } }, - "gulp-watch>chokidar>readdirp>micromatch>extglob>extend-shallow": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>string_decoder": { "packages": { - "gulp-watch>chokidar>readdirp>micromatch>extglob>extend-shallow>is-extendable": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>tar>safe-buffer": true } }, - "gulp-watch>chokidar>upath": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>util-deprecate": { "builtin": { - "path": true + "util.deprecate": true } }, - "gulp-watch>fancy-log": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge": { + "builtin": { + "util.format": true + }, "globals": { - "console": true, - "process.argv.indexOf": true, - "process.stderr.write": true, - "process.stdout.write": true + "clearInterval": true, + "process": true, + "setImmediate": true, + "setInterval": true }, "packages": { - "fancy-log>ansi-gray": true, - "fancy-log>color-support": true, - "fancy-log>time-stamp": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>console-control-strings": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>aproba": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>has-unicode": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>object-assign": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>signal-exit": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>wide-align": true } }, - "gulp-watch>glob-parent": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>has-unicode": { "builtin": { - "os.platform": true, - "path": true + "os.type": true }, - "packages": { - "gulp-watch>glob-parent>is-glob": true, - "gulp-watch>glob-parent>path-dirname": true - } - }, - "gulp-watch>glob-parent>is-glob": { - "packages": { - "gulp-watch>glob-parent>is-glob>is-extglob": true + "globals": { + "process.env.LANG": true, + "process.env.LC_ALL": true, + "process.env.LC_CTYPE": true } }, - "gulp-watch>glob-parent>path-dirname": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>signal-exit": { "builtin": { - "path": true, - "util.inspect": true + "assert.equal": true, + "events": true }, "globals": { - "process.platform": true + "process": true } }, - "gulp-watch>path-is-absolute": { - "globals": { - "process.platform": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width": { + "packages": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>code-point-at": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>is-fullwidth-code-point": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi": true } }, - "gulp-watch>vinyl-file": { - "builtin": { - "path.resolve": true - }, - "globals": { - "process.cwd": true - }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>is-fullwidth-code-point": { "packages": { - "del>globby>pinkie-promise": true, - "fs-extra>graceful-fs": true, - "gulp-watch>vinyl-file>pify": true, - "gulp-watch>vinyl-file>strip-bom": true, - "gulp-watch>vinyl-file>strip-bom-stream": true, - "gulp-watch>vinyl-file>vinyl": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>is-fullwidth-code-point>number-is-nan": true } }, - "gulp-watch>vinyl-file>strip-bom": { - "globals": { - "Buffer.isBuffer": true - }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi": { "packages": { - "gulp>vinyl-fs>remove-bom-buffer>is-utf8": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi>ansi-regex": true } }, - "gulp-watch>vinyl-file>strip-bom-stream": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>wide-align": { "packages": { - "gulp-watch>vinyl-file>strip-bom": true, - "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width": true } }, - "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>npmlog>set-blocking": { + "globals": { + "process.stderr": true, + "process.stdout": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf": { "builtin": { - "util.inherits": true + "assert": true, + "fs": true, + "path.join": true }, "globals": { - "Buffer.concat": true, - "setImmediate": true + "process.platform": true, + "setTimeout": true }, "packages": { - "readable-stream": true + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob": true } }, - "gulp-watch>vinyl-file>vinyl": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob": { + "builtin": { + "assert": true, + "events.EventEmitter": true, + "fs.lstat": true, + "fs.lstatSync": true, + "fs.readdir": true, + "fs.readdirSync": true, + "fs.stat": true, + "fs.statSync": true, + "path.join": true, + "path.resolve": true, + "util": true + }, + "globals": { + "console.error": true, + "process.cwd": true, + "process.nextTick": true, + "process.platform": true + }, + "packages": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>fs.realpath": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>inflight": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>inherits": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>once": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>path-is-absolute": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>fs.realpath": { + "builtin": { + "fs.lstat": true, + "fs.lstatSync": true, + "fs.readlink": true, + "fs.readlinkSync": true, + "fs.realpath": true, + "fs.realpathSync": true, + "fs.stat": true, + "fs.statSync": true, + "path.normalize": true, + "path.resolve": true + }, + "globals": { + "console.error": true, + "console.trace": true, + "process.env.NODE_DEBUG": true, + "process.nextTick": true, + "process.noDeprecation": true, + "process.platform": true, + "process.throwDeprecation": true, + "process.traceDeprecation": true, + "process.version": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>inflight": { + "globals": { + "process.nextTick": true + }, + "packages": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>once": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>once>wrappy": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>inherits": { + "builtin": { + "util.inherits": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch": { + "builtin": { + "path": true + }, + "globals": { + "console.error": true + }, + "packages": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion": { + "packages": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion>balanced-match": true, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion>concat-map": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>once": { + "packages": { + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>once>wrappy": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf>glob>path-is-absolute": { + "globals": { + "process.platform": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>semver": { + "globals": { + "console": true, + "process": true + } + }, + "gulp-watch>chokidar>fsevents>node-pre-gyp>tar>safe-buffer": { + "builtin": { + "buffer": true + } + }, + "gulp-watch>chokidar>is-binary-path": { + "builtin": { + "path.extname": true + }, + "packages": { + "gulp-watch>chokidar>is-binary-path>binary-extensions": true + } + }, + "gulp-watch>chokidar>readdirp": { + "builtin": { + "path.join": true, + "path.relative": true, + "util.inherits": true + }, + "globals": { + "setImmediate": true + }, + "packages": { + "fs-extra>graceful-fs": true, + "gulp-watch>chokidar>readdirp>micromatch": true, + "readable-stream": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch": { + "builtin": { + "path.basename": true, + "path.sep": true, + "util.inspect": true + }, + "globals": { + "process.platform": true + }, + "packages": { + "gulp-watch>chokidar>braces": true, + "gulp-watch>chokidar>readdirp>micromatch>arr-diff": true, + "gulp-watch>chokidar>readdirp>micromatch>array-unique": true, + "gulp-watch>chokidar>readdirp>micromatch>define-property": true, + "gulp-watch>chokidar>readdirp>micromatch>extend-shallow": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob": true, + "gulp-watch>chokidar>readdirp>micromatch>kind-of": true, + "webpack>micromatch>fragment-cache": true, + "webpack>micromatch>nanomatch": true, + "webpack>micromatch>object.pick": true, + "webpack>micromatch>regex-not": true, + "webpack>micromatch>snapdragon": true, + "webpack>micromatch>to-regex": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>define-property": { + "packages": { + "gulp>gulp-cli>isobject": true, + "webpack>micromatch>define-property>is-descriptor": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extend-shallow": { + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extend-shallow>is-extendable": true, + "webpack>micromatch>extend-shallow>assign-symbols": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extend-shallow>is-extendable": { + "packages": { + "@babel/register>clone-deep>is-plain-object": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob": { + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>array-unique": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>define-property": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>extend-shallow": true, + "webpack>micromatch>fragment-cache": true, + "webpack>micromatch>regex-not": true, + "webpack>micromatch>snapdragon": true, + "webpack>micromatch>to-regex": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>define-property": { + "packages": { + "webpack>micromatch>define-property>is-descriptor": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets": { + "globals": { + "__filename": true + }, + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>debug": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>extend-shallow": true, + "webpack>micromatch>extglob>expand-brackets>posix-character-classes": true, + "webpack>micromatch>regex-not": true, + "webpack>micromatch>snapdragon": true, + "webpack>micromatch>to-regex": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>debug": { + "builtin": { + "fs.SyncWriteStream": true, + "net.Socket": true, + "tty.WriteStream": true, + "tty.isatty": true, + "util": true + }, + "globals": { + "chrome": true, + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>debug>ms": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property": { + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor": { + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": true, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>kind-of": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": { + "packages": { + "gulp-watch>anymatch>micromatch>kind-of": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": { + "packages": { + "gulp-watch>anymatch>micromatch>kind-of": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>extend-shallow": { + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extglob>expand-brackets>extend-shallow>is-extendable": true + } + }, + "gulp-watch>chokidar>readdirp>micromatch>extglob>extend-shallow": { + "packages": { + "gulp-watch>chokidar>readdirp>micromatch>extglob>extend-shallow>is-extendable": true + } + }, + "gulp-watch>chokidar>upath": { + "builtin": { + "path": true + } + }, + "gulp-watch>fancy-log": { + "globals": { + "console": true, + "process.argv.indexOf": true, + "process.stderr.write": true, + "process.stdout.write": true + }, + "packages": { + "fancy-log>ansi-gray": true, + "fancy-log>color-support": true, + "fancy-log>time-stamp": true + } + }, + "gulp-watch>glob-parent": { + "builtin": { + "os.platform": true, + "path": true + }, + "packages": { + "gulp-watch>glob-parent>is-glob": true, + "gulp-watch>glob-parent>path-dirname": true + } + }, + "gulp-watch>glob-parent>is-glob": { + "packages": { + "gulp-watch>glob-parent>is-glob>is-extglob": true + } + }, + "gulp-watch>glob-parent>path-dirname": { + "builtin": { + "path": true, + "util.inspect": true + }, + "globals": { + "process.platform": true + } + }, + "gulp-watch>path-is-absolute": { + "globals": { + "process.platform": true + } + }, + "gulp-watch>vinyl-file": { + "builtin": { + "path.resolve": true + }, + "globals": { + "process.cwd": true + }, + "packages": { + "del>globby>pinkie-promise": true, + "fs-extra>graceful-fs": true, + "gulp-watch>vinyl-file>pify": true, + "gulp-watch>vinyl-file>strip-bom": true, + "gulp-watch>vinyl-file>strip-bom-stream": true, + "gulp-watch>vinyl-file>vinyl": true + } + }, + "gulp-watch>vinyl-file>strip-bom": { + "globals": { + "Buffer.isBuffer": true + }, + "packages": { + "gulp>vinyl-fs>remove-bom-buffer>is-utf8": true + } + }, + "gulp-watch>vinyl-file>strip-bom-stream": { + "packages": { + "gulp-watch>vinyl-file>strip-bom": true, + "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream": true + } + }, + "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream": { + "builtin": { + "util.inherits": true + }, + "globals": { + "Buffer.concat": true, + "setImmediate": true + }, + "packages": { + "readable-stream": true + } + }, + "gulp-watch>vinyl-file>vinyl": { + "builtin": { + "buffer.Buffer": true, + "path.basename": true, + "path.dirname": true, + "path.extname": true, + "path.join": true, + "path.relative": true, + "stream.PassThrough": true, + "stream.Stream": true + }, + "globals": { + "process.cwd": true + }, + "packages": { + "gulp-watch>vinyl-file>vinyl>clone": true, + "gulp-watch>vinyl-file>vinyl>clone-stats": true, + "gulp-watch>vinyl-file>vinyl>replace-ext": true + } + }, + "gulp-watch>vinyl-file>vinyl>clone": { + "globals": { + "Buffer": true + } + }, + "gulp-watch>vinyl-file>vinyl>clone-stats": { + "builtin": { + "fs.Stats": true + } + }, + "gulp-watch>vinyl-file>vinyl>replace-ext": { + "builtin": { + "path.basename": true, + "path.dirname": true, + "path.extname": true, + "path.join": true + } + }, + "gulp-zip": { + "builtin": { + "buffer.constants.MAX_LENGTH": true, + "path.join": true + }, + "packages": { + "gulp-zip>get-stream": true, + "gulp-zip>plugin-error": true, + "gulp-zip>through2": true, + "gulp-zip>yazl": true, + "vinyl": true + } + }, + "gulp-zip>get-stream": { + "builtin": { + "buffer.constants.MAX_LENGTH": true, + "stream.PassThrough": true + }, + "globals": { + "Buffer.concat": true + }, + "packages": { + "pump": true + } + }, + "gulp-zip>plugin-error": { + "builtin": { + "util.inherits": true + }, + "packages": { + "gulp-watch>ansi-colors": true, + "gulp-zip>plugin-error>arr-union": true, + "gulp-zip>plugin-error>extend-shallow": true, + "webpack>micromatch>arr-diff": true + } + }, + "gulp-zip>plugin-error>extend-shallow": { + "packages": { + "gulp-zip>plugin-error>extend-shallow>is-extendable": true, + "webpack>micromatch>extend-shallow>assign-symbols": true + } + }, + "gulp-zip>plugin-error>extend-shallow>is-extendable": { + "packages": { + "@babel/register>clone-deep>is-plain-object": true + } + }, + "gulp-zip>through2": { + "builtin": { + "util.inherits": true + }, + "globals": { + "process.nextTick": true + }, + "packages": { + "gulp-zip>through2>readable-stream": true + } + }, + "gulp-zip>through2>readable-stream": { "builtin": { "buffer.Buffer": true, + "events.EventEmitter": true, + "stream": true, + "util": true + }, + "globals": { + "process.env.READABLE_STREAM": true, + "process.nextTick": true, + "process.stderr": true, + "process.stdout": true + }, + "packages": { + "@storybook/api>util-deprecate": true, + "browserify>string_decoder": true, + "pumpify>inherits": true + } + }, + "gulp-zip>yazl": { + "builtin": { + "events.EventEmitter": true, + "fs.createReadStream": true, + "fs.stat": true, + "stream.PassThrough": true, + "stream.Transform": true, + "util.inherits": true, + "zlib.DeflateRaw": true, + "zlib.deflateRaw": true + }, + "globals": { + "Buffer": true, + "setImmediate": true, + "utf8FileName.length": true + }, + "packages": { + "gulp-zip>yazl>buffer-crc32": true + } + }, + "gulp-zip>yazl>buffer-crc32": { + "builtin": { + "buffer.Buffer": true + } + }, + "gulp>glob-watcher": { + "packages": { + "gulp>glob-watcher>anymatch": true, + "gulp>glob-watcher>async-done": true, + "gulp>glob-watcher>chokidar": true, + "gulp>glob-watcher>is-negated-glob": true, + "gulp>glob-watcher>just-debounce": true, + "gulp>undertaker>object.defaults": true + } + }, + "gulp>glob-watcher>anymatch": { + "builtin": { + "path.sep": true + }, + "packages": { + "gulp>glob-watcher>anymatch>micromatch": true, + "gulp>glob-watcher>anymatch>normalize-path": true + } + }, + "gulp>glob-watcher>anymatch>micromatch": { + "builtin": { + "path.basename": true, + "path.sep": true, + "util.inspect": true + }, + "globals": { + "process.platform": true + }, + "packages": { + "gulp>glob-watcher>anymatch>micromatch>define-property": true, + "gulp>glob-watcher>anymatch>micromatch>extend-shallow": true, + "gulp>glob-watcher>chokidar>braces": true, + "webpack>micromatch>arr-diff": true, + "webpack>micromatch>array-unique": true, + "webpack>micromatch>extglob": true, + "webpack>micromatch>fragment-cache": true, + "webpack>micromatch>kind-of": true, + "webpack>micromatch>nanomatch": true, + "webpack>micromatch>object.pick": true, + "webpack>micromatch>regex-not": true, + "webpack>micromatch>snapdragon": true, + "webpack>micromatch>to-regex": true + } + }, + "gulp>glob-watcher>anymatch>micromatch>define-property": { + "packages": { + "gulp>gulp-cli>isobject": true, + "webpack>micromatch>define-property>is-descriptor": true + } + }, + "gulp>glob-watcher>anymatch>micromatch>extend-shallow": { + "packages": { + "gulp>glob-watcher>anymatch>micromatch>extend-shallow>is-extendable": true, + "webpack>micromatch>extend-shallow>assign-symbols": true + } + }, + "gulp>glob-watcher>anymatch>micromatch>extend-shallow>is-extendable": { + "packages": { + "@babel/register>clone-deep>is-plain-object": true + } + }, + "gulp>glob-watcher>anymatch>normalize-path": { + "packages": { + "vinyl>remove-trailing-separator": true + } + }, + "gulp>glob-watcher>async-done": { + "builtin": { + "domain.create": true + }, + "globals": { + "process.nextTick": true + }, + "packages": { + "end-of-stream": true, + "gulp>glob-watcher>async-done>process-nextick-args": true, + "gulp>glob-watcher>async-done>stream-exhaust": true, + "pump>once": true + } + }, + "gulp>glob-watcher>async-done>process-nextick-args": { + "globals": { + "process": true + } + }, + "gulp>glob-watcher>async-done>stream-exhaust": { + "builtin": { + "stream.Writable": true, + "util.inherits": true + }, + "globals": { + "setImmediate": true + } + }, + "gulp>glob-watcher>chokidar": { + "builtin": { + "events.EventEmitter": true, + "fs": true, "path.basename": true, "path.dirname": true, "path.extname": true, "path.join": true, "path.relative": true, - "stream.PassThrough": true, - "stream.Stream": true + "path.resolve": true, + "path.sep": true }, "globals": { - "process.cwd": true + "clearTimeout": true, + "console.error": true, + "process.env.CHOKIDAR_INTERVAL": true, + "process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR": true, + "process.env.CHOKIDAR_USEPOLLING": true, + "process.nextTick": true, + "process.platform": true, + "setTimeout": true }, "packages": { - "gulp-watch>vinyl-file>vinyl>clone": true, - "gulp-watch>vinyl-file>vinyl>clone-stats": true, - "gulp-watch>vinyl-file>vinyl>replace-ext": true + "eslint>is-glob": true, + "gulp-watch>chokidar>async-each": true, + "gulp-watch>glob-parent": true, + "gulp-watch>path-is-absolute": true, + "gulp>glob-watcher>anymatch": true, + "gulp>glob-watcher>chokidar>braces": true, + "gulp>glob-watcher>chokidar>fsevents": true, + "gulp>glob-watcher>chokidar>is-binary-path": true, + "gulp>glob-watcher>chokidar>normalize-path": true, + "gulp>glob-watcher>chokidar>readdirp": true, + "gulp>glob-watcher>chokidar>upath": true, + "pumpify>inherits": true } }, - "gulp-watch>vinyl-file>vinyl>clone": { - "globals": { - "Buffer": true + "gulp>glob-watcher>chokidar>braces": { + "packages": { + "gulp>glob-watcher>chokidar>braces>fill-range": true, + "gulp>gulp-cli>isobject": true, + "gulp>undertaker>arr-flatten": true, + "webpack>micromatch>array-unique": true, + "webpack>micromatch>braces>repeat-element": true, + "webpack>micromatch>braces>snapdragon-node": true, + "webpack>micromatch>braces>split-string": true, + "webpack>micromatch>extglob>extend-shallow": true, + "webpack>micromatch>snapdragon": true, + "webpack>micromatch>to-regex": true } }, - "gulp-watch>vinyl-file>vinyl>clone-stats": { + "gulp>glob-watcher>chokidar>braces>fill-range": { "builtin": { - "fs.Stats": true + "util.inspect": true + }, + "packages": { + "gulp>glob-watcher>chokidar>braces>fill-range>is-number": true, + "gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": true, + "webpack>micromatch>braces>fill-range>repeat-string": true, + "webpack>micromatch>extglob>extend-shallow": true } }, - "gulp-watch>vinyl-file>vinyl>replace-ext": { + "gulp>glob-watcher>chokidar>braces>fill-range>is-number": { + "packages": { + "gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": true + } + }, + "gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": { + "packages": { + "browserify>insert-module-globals>is-buffer": true + } + }, + "gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": { + "packages": { + "gulp>glob-watcher>chokidar>braces>fill-range>is-number": true, + "webpack>micromatch>braces>fill-range>repeat-string": true + } + }, + "gulp>glob-watcher>chokidar>fsevents": { "builtin": { - "path.basename": true, + "events.EventEmitter": true, + "fs.stat": true, + "path.join": true, + "util.inherits": true + }, + "globals": { + "__dirname": true, + "process.nextTick": true, + "process.platform": true, + "setImmediate": true + }, + "packages": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp": { + "builtin": { + "events.EventEmitter": true, + "fs.existsSync": true, + "fs.readFileSync": true, + "fs.renameSync": true, "path.dirname": true, - "path.extname": true, - "path.join": true + "path.existsSync": true, + "path.join": true, + "path.resolve": true, + "url.parse": true, + "url.resolve": true, + "util.inherits": true + }, + "globals": { + "__dirname": true, + "console.log": true, + "process.arch": true, + "process.cwd": true, + "process.env": true, + "process.platform": true, + "process.version.substr": true, + "process.versions": true + }, + "packages": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>detect-libc": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>semver": true } }, - "gulp-zip": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>detect-libc": { "builtin": { - "buffer.constants.MAX_LENGTH": true, - "path.join": true + "child_process.spawnSync": true, + "fs.readdirSync": true, + "os.platform": true + }, + "globals": { + "process.env": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt": { + "builtin": { + "path": true, + "stream.Stream": true, + "url": true + }, + "globals": { + "console": true, + "process.argv": true, + "process.env.DEBUG_NOPT": true, + "process.env.NOPT_DEBUG": true, + "process.platform": true }, "packages": { - "gulp-zip>get-stream": true, - "gulp-zip>plugin-error": true, - "gulp-zip>through2": true, - "gulp-zip>yazl": true, - "vinyl": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>abbrev": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>osenv": true } }, - "gulp-zip>get-stream": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>osenv": { "builtin": { - "buffer.constants.MAX_LENGTH": true, - "stream.PassThrough": true + "child_process.exec": true, + "path": true }, "globals": { - "Buffer.concat": true + "process.env.COMPUTERNAME": true, + "process.env.ComSpec": true, + "process.env.EDITOR": true, + "process.env.HOSTNAME": true, + "process.env.PATH": true, + "process.env.PROMPT": true, + "process.env.PS1": true, + "process.env.Path": true, + "process.env.SHELL": true, + "process.env.USER": true, + "process.env.USERDOMAIN": true, + "process.env.USERNAME": true, + "process.env.VISUAL": true, + "process.env.path": true, + "process.nextTick": true, + "process.platform": true }, "packages": { - "pump": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-homedir": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": true } }, - "gulp-zip>plugin-error": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-homedir": { "builtin": { - "util.inherits": true + "os.homedir": true }, - "packages": { - "gulp-watch>ansi-colors": true, - "gulp-zip>plugin-error>arr-union": true, - "gulp-zip>plugin-error>extend-shallow": true, - "webpack>micromatch>arr-diff": true + "globals": { + "process.env": true, + "process.getuid": true, + "process.platform": true } }, - "gulp-zip>plugin-error>extend-shallow": { - "packages": { - "gulp-zip>plugin-error>extend-shallow>is-extendable": true, - "webpack>micromatch>extend-shallow>assign-symbols": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": { + "globals": { + "process.env.SystemRoot": true, + "process.env.TEMP": true, + "process.env.TMP": true, + "process.env.TMPDIR": true, + "process.env.windir": true, + "process.platform": true } }, - "gulp-zip>plugin-error>extend-shallow>is-extendable": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog": { + "builtin": { + "events.EventEmitter": true, + "util": true + }, + "globals": { + "process.nextTick": true, + "process.stderr": true + }, "packages": { - "@babel/register>clone-deep>is-plain-object": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>console-control-strings": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>set-blocking": true } }, - "gulp-zip>through2": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet": { "builtin": { + "events.EventEmitter": true, "util.inherits": true }, - "globals": { - "process.nextTick": true - }, "packages": { - "gulp-zip>through2>readable-stream": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>delegates": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream": true } }, - "gulp-zip>through2>readable-stream": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream": { "builtin": { - "buffer.Buffer": true, "events.EventEmitter": true, "stream": true, "util": true }, "globals": { + "process.browser": true, "process.env.READABLE_STREAM": true, - "process.nextTick": true, "process.stderr": true, - "process.stdout": true + "process.stdout": true, + "process.version.slice": true, + "setImmediate": true }, "packages": { - "@storybook/api>util-deprecate": true, - "browserify>string_decoder": true, - "pumpify>inherits": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>core-util-is": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>isarray": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>process-nextick-args": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>string_decoder": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>util-deprecate": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>inherits": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>tar>safe-buffer": true } }, - "gulp-zip>yazl": { - "builtin": { - "events.EventEmitter": true, - "fs.createReadStream": true, - "fs.stat": true, - "stream.PassThrough": true, - "stream.Transform": true, - "util.inherits": true, - "zlib.DeflateRaw": true, - "zlib.deflateRaw": true - }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>core-util-is": { "globals": { - "Buffer": true, - "setImmediate": true, - "utf8FileName.length": true - }, - "packages": { - "gulp-zip>yazl>buffer-crc32": true + "Buffer.isBuffer": true } }, - "gulp-zip>yazl>buffer-crc32": { - "builtin": { - "buffer.Buffer": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>process-nextick-args": { + "globals": { + "process": true } }, - "gulp>glob-watcher": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>string_decoder": { "packages": { - "gulp>glob-watcher>anymatch": true, - "gulp>glob-watcher>async-done": true, - "gulp>glob-watcher>chokidar": true, - "gulp>glob-watcher>is-negated-glob": true, - "gulp>glob-watcher>just-debounce": true, - "gulp>undertaker>object.defaults": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>tar>safe-buffer": true } }, - "gulp>glob-watcher>anymatch": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>are-we-there-yet>readable-stream>util-deprecate": { "builtin": { - "path.sep": true - }, - "packages": { - "gulp>glob-watcher>anymatch>micromatch": true, - "gulp>glob-watcher>anymatch>normalize-path": true + "util.deprecate": true } }, - "gulp>glob-watcher>anymatch>micromatch": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge": { "builtin": { - "path.basename": true, - "path.sep": true, - "util.inspect": true + "util.format": true }, "globals": { - "process.platform": true + "clearInterval": true, + "process": true, + "setImmediate": true, + "setInterval": true }, "packages": { - "gulp>glob-watcher>anymatch>micromatch>define-property": true, - "gulp>glob-watcher>anymatch>micromatch>extend-shallow": true, - "gulp>glob-watcher>chokidar>braces": true, - "webpack>micromatch>arr-diff": true, - "webpack>micromatch>array-unique": true, - "webpack>micromatch>extglob": true, - "webpack>micromatch>fragment-cache": true, - "webpack>micromatch>kind-of": true, - "webpack>micromatch>nanomatch": true, - "webpack>micromatch>object.pick": true, - "webpack>micromatch>regex-not": true, - "webpack>micromatch>snapdragon": true, - "webpack>micromatch>to-regex": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>console-control-strings": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>aproba": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>has-unicode": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>object-assign": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>signal-exit": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>wide-align": true } }, - "gulp>glob-watcher>anymatch>micromatch>define-property": { - "packages": { - "gulp>gulp-cli>isobject": true, - "webpack>micromatch>define-property>is-descriptor": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>has-unicode": { + "builtin": { + "os.type": true + }, + "globals": { + "process.env.LANG": true, + "process.env.LC_ALL": true, + "process.env.LC_CTYPE": true } }, - "gulp>glob-watcher>anymatch>micromatch>extend-shallow": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>signal-exit": { + "builtin": { + "assert.equal": true, + "events": true + }, + "globals": { + "process": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width": { "packages": { - "gulp>glob-watcher>anymatch>micromatch>extend-shallow>is-extendable": true, - "webpack>micromatch>extend-shallow>assign-symbols": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>code-point-at": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>is-fullwidth-code-point": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi": true } }, - "gulp>glob-watcher>anymatch>micromatch>extend-shallow>is-extendable": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>is-fullwidth-code-point": { "packages": { - "@babel/register>clone-deep>is-plain-object": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width>is-fullwidth-code-point>number-is-nan": true } }, - "gulp>glob-watcher>anymatch>normalize-path": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi": { "packages": { - "vinyl>remove-trailing-separator": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>strip-ansi>ansi-regex": true } }, - "gulp>glob-watcher>async-done": { - "builtin": { - "domain.create": true - }, - "globals": { - "process.nextTick": true - }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>wide-align": { "packages": { - "end-of-stream": true, - "gulp>glob-watcher>async-done>process-nextick-args": true, - "gulp>glob-watcher>async-done>stream-exhaust": true, - "pump>once": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>gauge>string-width": true } }, - "gulp>glob-watcher>async-done>process-nextick-args": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>npmlog>set-blocking": { "globals": { - "process": true + "process.stderr": true, + "process.stdout": true } }, - "gulp>glob-watcher>async-done>stream-exhaust": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf": { "builtin": { - "stream.Writable": true, - "util.inherits": true + "assert": true, + "fs": true, + "path.join": true }, "globals": { - "setImmediate": true + "process.platform": true, + "setTimeout": true + }, + "packages": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob": true } }, - "gulp>glob-watcher>chokidar": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob": { "builtin": { + "assert": true, "events.EventEmitter": true, - "fs": true, - "path.basename": true, - "path.dirname": true, - "path.extname": true, + "fs.lstat": true, + "fs.lstatSync": true, + "fs.readdir": true, + "fs.readdirSync": true, + "fs.stat": true, + "fs.statSync": true, "path.join": true, - "path.relative": true, "path.resolve": true, - "path.sep": true + "util": true }, "globals": { - "clearTimeout": true, "console.error": true, - "process.env.CHOKIDAR_INTERVAL": true, - "process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR": true, - "process.env.CHOKIDAR_USEPOLLING": true, + "process.cwd": true, "process.nextTick": true, - "process.platform": true, - "setTimeout": true + "process.platform": true }, "packages": { - "eslint>is-glob": true, - "gulp-watch>chokidar>async-each": true, - "gulp-watch>glob-parent": true, - "gulp-watch>path-is-absolute": true, - "gulp>glob-watcher>anymatch": true, - "gulp>glob-watcher>chokidar>braces": true, - "gulp>glob-watcher>chokidar>is-binary-path": true, - "gulp>glob-watcher>chokidar>normalize-path": true, - "gulp>glob-watcher>chokidar>readdirp": true, - "gulp>glob-watcher>chokidar>upath": true, - "pumpify>inherits": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>fs.realpath": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>inflight": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>inherits": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>once": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>path-is-absolute": true } }, - "gulp>glob-watcher>chokidar>braces": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>fs.realpath": { + "builtin": { + "fs.lstat": true, + "fs.lstatSync": true, + "fs.readlink": true, + "fs.readlinkSync": true, + "fs.realpath": true, + "fs.realpathSync": true, + "fs.stat": true, + "fs.statSync": true, + "path.normalize": true, + "path.resolve": true + }, + "globals": { + "console.error": true, + "console.trace": true, + "process.env.NODE_DEBUG": true, + "process.nextTick": true, + "process.noDeprecation": true, + "process.platform": true, + "process.throwDeprecation": true, + "process.traceDeprecation": true, + "process.version": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>inflight": { + "globals": { + "process.nextTick": true + }, "packages": { - "gulp>glob-watcher>chokidar>braces>fill-range": true, - "gulp>gulp-cli>isobject": true, - "gulp>undertaker>arr-flatten": true, - "webpack>micromatch>array-unique": true, - "webpack>micromatch>braces>repeat-element": true, - "webpack>micromatch>braces>snapdragon-node": true, - "webpack>micromatch>braces>split-string": true, - "webpack>micromatch>extglob>extend-shallow": true, - "webpack>micromatch>snapdragon": true, - "webpack>micromatch>to-regex": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>once": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>once>wrappy": true } }, - "gulp>glob-watcher>chokidar>braces>fill-range": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>inherits": { "builtin": { - "util.inspect": true + "util.inherits": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch": { + "builtin": { + "path": true + }, + "globals": { + "console.error": true }, "packages": { - "gulp>glob-watcher>chokidar>braces>fill-range>is-number": true, - "gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": true, - "webpack>micromatch>braces>fill-range>repeat-string": true, - "webpack>micromatch>extglob>extend-shallow": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion": true } }, - "gulp>glob-watcher>chokidar>braces>fill-range>is-number": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion": { "packages": { - "gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion>balanced-match": true, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>minimatch>brace-expansion>concat-map": true } }, - "gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": { + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>once": { "packages": { - "browserify>insert-module-globals>is-buffer": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>once>wrappy": true } }, - "gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": { - "packages": { - "gulp>glob-watcher>chokidar>braces>fill-range>is-number": true, - "webpack>micromatch>braces>fill-range>repeat-string": true + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>rimraf>glob>path-is-absolute": { + "globals": { + "process.platform": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>semver": { + "globals": { + "console": true, + "process": true + } + }, + "gulp>glob-watcher>chokidar>fsevents>node-pre-gyp>tar>safe-buffer": { + "builtin": { + "buffer": true } }, "gulp>glob-watcher>chokidar>is-binary-path": { diff --git a/package.json b/package.json index 8588c254c5ab..a952c5b17418 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "setup": "yarn install && yarn setup:postinstall", "setup:postinstall": "yarn patch-package && yarn allow-scripts", - "start": "yarn build:dev dev --apply-lavamoat=false", + "start": "yarn build:dev dev --apply-lavamoat=false --snow=false", "start:lavamoat": "yarn build:dev dev --apply-lavamoat=true", "start:mv3": "ENABLE_MV3=true yarn build:dev dev --apply-lavamoat=false", "dist": "yarn build dist", @@ -122,11 +122,11 @@ "@metamask/obs-store": "^5.0.0", "@metamask/post-message-stream": "^6.0.0", "@metamask/providers": "^10.2.1", - "@metamask/rpc-methods": "^0.23.0", + "@metamask/rpc-methods": "^0.24.1", "@metamask/slip44": "^2.1.0", "@metamask/smart-transactions-controller": "^3.0.0", - "@metamask/snap-controllers": "^0.23.0", - "@metamask/snap-utils": "^0.23.0", + "@metamask/snaps-controllers": "^0.24.1", + "@metamask/snaps-utils": "^0.24.1", "@ngraveio/bc-ur": "^1.1.6", "@popperjs/core": "^2.4.0", "@reduxjs/toolkit": "^1.6.2", @@ -138,6 +138,7 @@ "@spruceid/siwe-parser": "^1.1.3", "@truffle/codec": "^0.11.18", "@truffle/decoder": "^5.1.0", + "@lavamoat/snow": "^1.2.1", "@zxing/browser": "^0.0.10", "@zxing/library": "0.8.0", "await-semaphore": "^0.1.1", @@ -155,7 +156,7 @@ "eth-ens-namehash": "^2.0.8", "eth-json-rpc-filters": "^4.2.1", "eth-json-rpc-middleware": "^9.0.1", - "eth-keyring-controller": "^7.0.2", + "eth-keyring-controller": "^8.0.1", "eth-lattice-keyring": "^0.12.3", "eth-method-registry": "^2.0.0", "eth-query": "^2.1.2", diff --git a/shared/constants/permissions.test.js b/shared/constants/permissions.test.js index 433d6629c3b5..e12b6ab93721 100644 --- a/shared/constants/permissions.test.js +++ b/shared/constants/permissions.test.js @@ -1,4 +1,4 @@ -import { endowmentPermissionBuilders } from '@metamask/snap-controllers'; +import { endowmentPermissionBuilders } from '@metamask/snaps-controllers'; import { restrictedMethodPermissionBuilders } from '@metamask/rpc-methods'; import { EndowmentPermissions, diff --git a/shared/constants/permissions.ts b/shared/constants/permissions.ts index dbd39b51e69b..a21d345394c8 100644 --- a/shared/constants/permissions.ts +++ b/shared/constants/permissions.ts @@ -11,6 +11,7 @@ export const RestrictedMethods = Object.freeze({ snap_getBip32PublicKey: 'snap_getBip32PublicKey', snap_getBip32Entropy: 'snap_getBip32Entropy', snap_getBip44Entropy: 'snap_getBip44Entropy', + snap_getEntropy: 'snap_getEntropy', 'wallet_snap_*': 'wallet_snap_*', ///: END:ONLY_INCLUDE_IN } as const); @@ -25,6 +26,7 @@ export const EndowmentPermissions = Object.freeze({ 'endowment:long-running': 'endowment:long-running', 'endowment:transaction-insight': 'endowment:transaction-insight', 'endowment:cronjob': 'endowment:cronjob', + 'endowment:ethereum-provider': 'endowment:ethereum-provider', } as const); // Methods / permissions in external packages that we are temporarily excluding. diff --git a/test/e2e/run-all.js b/test/e2e/run-all.js index 27b754750297..5ef37e4331d1 100644 --- a/test/e2e/run-all.js +++ b/test/e2e/run-all.js @@ -13,10 +13,13 @@ const getTestPathsForTestDir = async (testDir) => { return testPaths; }; -function chunk(array, chunkSize) { +// Heavily inspired by: https://stackoverflow.com/a/51514813 +// Splits the array into totalChunks chunks with a decent spread of items in each chunk +function chunk(array, totalChunks) { + const copyArray = [...array]; const result = []; - for (let i = 0; i < array.length; i += chunkSize) { - result.push(array.slice(i, i + chunkSize)); + for (let chunkIndex = totalChunks; chunkIndex > 0; chunkIndex--) { + result.push(copyArray.splice(0, Math.ceil(copyArray.length / chunkIndex))); } return result; } @@ -77,8 +80,7 @@ async function main() { // For running E2Es in parallel in CI const currentChunkIndex = process.env.CIRCLE_NODE_INDEX ?? 0; const totalChunks = process.env.CIRCLE_NODE_TOTAL ?? 1; - const chunkSize = Math.ceil(testPaths.length / totalChunks); - const chunks = chunk(testPaths, chunkSize); + const chunks = chunk(testPaths, totalChunks); const currentChunk = chunks[currentChunkIndex]; for (const testPath of currentChunk) { diff --git a/test/e2e/snaps/enums.js b/test/e2e/snaps/enums.js index a940e21b92f3..1cc1906b0b3a 100644 --- a/test/e2e/snaps/enums.js +++ b/test/e2e/snaps/enums.js @@ -1,3 +1,3 @@ module.exports = { - TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/test-snaps/4.0.2/', + TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/test-snaps/4.1.1/', }; diff --git a/test/e2e/snaps/test-snap-update.spec.js b/test/e2e/snaps/test-snap-update.spec.js index bc78e10cfb25..332aac1e0ae3 100644 --- a/test/e2e/snaps/test-snap-update.spec.js +++ b/test/e2e/snaps/test-snap-update.spec.js @@ -29,7 +29,7 @@ describe('Test Snap update', function () { await driver.press('#password', driver.Key.ENTER); // open a new tab and navigate to test snaps page and connect - await driver.openNewPage(TEST_SNAPS_WEBSITE_URL); + await driver.driver.get(TEST_SNAPS_WEBSITE_URL); // find and scroll to the correct card and click first const snapButton = await driver.findElement('#connectUpdateNew'); @@ -41,7 +41,7 @@ describe('Test Snap update', function () { // switch to metamask extension and click connect let windowHandles = await driver.waitUntilXWindowHandles( - 3, + 2, 1000, 10000, ); @@ -59,7 +59,7 @@ describe('Test Snap update', function () { await driver.delay(2000); // approve install of snap - windowHandles = await driver.waitUntilXWindowHandles(3, 1000, 10000); + windowHandles = await driver.waitUntilXWindowHandles(2, 1000, 10000); await driver.switchToWindowWithTitle( 'MetaMask Notification', windowHandles, @@ -69,8 +69,17 @@ describe('Test Snap update', function () { tag: 'button', }); + // wait for permissions popover, click checkboxes and confirm + await driver.delay(1000); + await driver.clickElement('#key-access-bip32-m-44h-0h-secp256k1-0'); + await driver.clickElement('#key-access-bip32-m-44h-0h-ed25519-0'); + await driver.clickElement({ + text: 'Confirm', + tag: 'button', + }); + // navigate to test snap page - windowHandles = await driver.waitUntilXWindowHandles(2, 1000, 10000); + windowHandles = await driver.waitUntilXWindowHandles(1, 1000, 10000); await driver.switchToWindowWithTitle('Test Snaps', windowHandles); await driver.delay(1000); @@ -81,7 +90,7 @@ describe('Test Snap update', function () { await driver.clickElement('#connectUpdateNew'); // switch to metamask extension and click connect - await driver.waitUntilXWindowHandles(3, 1000, 10000); + await driver.waitUntilXWindowHandles(2, 1000, 10000); await driver.delay(1000); // approve update of snap @@ -96,13 +105,13 @@ describe('Test Snap update', function () { }); // navigate to test snap page - windowHandles = await driver.waitUntilXWindowHandles(2, 1000, 10000); + windowHandles = await driver.waitUntilXWindowHandles(1, 1000, 10000); await driver.switchToWindowWithTitle('Test Snaps', windowHandles); // look for the correct version text const versionResult = await driver.findElement('#updateSnapVersion'); await driver.delay(1000); - assert.equal(await versionResult.getText(), '"2.0.0"'); + assert.equal(await versionResult.getText(), '"4.0.2"'); }, ); }); diff --git a/test/e2e/tests/from-import-ui.spec.js b/test/e2e/tests/from-import-ui.spec.js index 9c87f6c6690f..e09da607bea2 100644 --- a/test/e2e/tests/from-import-ui.spec.js +++ b/test/e2e/tests/from-import-ui.spec.js @@ -397,7 +397,7 @@ describe('MetaMask Import UI', function () { // error should occur await driver.waitForSelector({ css: '.error', - text: "The account you're are trying to import is a duplicate", + text: 'The account you are trying to import is a duplicate', }); }, ); diff --git a/test/lib/mock-encryptor.js b/test/lib/mock-encryptor.js index 75a8488a8a6d..1d2add75d6bd 100644 --- a/test/lib/mock-encryptor.js +++ b/test/lib/mock-encryptor.js @@ -12,6 +12,12 @@ const mockEncryptor = { return Promise.resolve(cacheVal || {}); }, + encryptWithDetail(_, dataObj) { + cacheVal = dataObj; + + return Promise.resolve({ vault: mockHex, exportedKeyString: '' }); + }, + encryptWithKey(key, dataObj) { return this.encrypt(key, dataObj); }, diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js index 27ac3f80e643..f407392baed5 100644 --- a/test/lib/render-helpers.js +++ b/test/lib/render-helpers.js @@ -137,3 +137,12 @@ export function renderControlledInput(InputComponent, props) { }; return { user: userEvent.setup(), ...render() }; } + +// userEvent setup function as per testing-library docs +// https://testing-library.com/docs/user-event/intr +export function renderWithUserEvent(jsx) { + return { + user: userEvent.setup(), + ...render(jsx), + }; +} diff --git a/ui/components/app/loading-network-screen/loading-network-screen.container.js b/ui/components/app/loading-network-screen/loading-network-screen.container.js index 657ff3265aa2..bb5a5d138588 100644 --- a/ui/components/app/loading-network-screen/loading-network-screen.container.js +++ b/ui/components/app/loading-network-screen/loading-network-screen.container.js @@ -19,7 +19,8 @@ const mapStateToProps = (state) => { const providerChainId = provider?.chainId; const isDeprecatedNetwork = DEPRECATED_TEST_NET_CHAINIDS.includes(providerChainId); - const isInfuraRpcUrl = provider?.rpcUrl?.match('infura'); + const isInfuraRpcUrl = + provider?.rpcUrl && new URL(provider.rpcUrl).host.endsWith('.infura.io'); const showDeprecatedRpcUrlWarning = isDeprecatedNetwork && isInfuraRpcUrl; return { diff --git a/ui/components/app/permissions-connect-permission-list/index.scss b/ui/components/app/permissions-connect-permission-list/index.scss index 1a7169bfde6b..5d61c83b2b49 100644 --- a/ui/components/app/permissions-connect-permission-list/index.scss +++ b/ui/components/app/permissions-connect-permission-list/index.scss @@ -19,6 +19,7 @@ min-height: 16px; color: var(--color-icon-alternative); font-size: 1rem; + text-align: center; } } diff --git a/ui/components/component-library/avatar-account/avatar-account.js b/ui/components/component-library/avatar-account/avatar-account.js index 032703718fdf..516aec9197be 100644 --- a/ui/components/component-library/avatar-account/avatar-account.js +++ b/ui/components/component-library/avatar-account/avatar-account.js @@ -3,14 +3,14 @@ import classnames from 'classnames'; import PropTypes from 'prop-types'; import Jazzicon from '../../ui/jazzicon/jazzicon.component'; import BlockieIdenticon from '../../ui/identicon/blockieIdenticon/blockieIdenticon.component'; -import { BaseAvatar } from '../base-avatar'; +import { AvatarBase } from '../avatar-base'; import { SIZES } from '../../../helpers/constants/design-system'; import { DIAMETERS, TYPES } from './avatar-account.constants'; export const AvatarAccount = ({ size, address, className, type, ...props }) => { return ( - { borderRadius="50%" /> )} - + ); }; diff --git a/ui/components/component-library/avatar-base/README.mdx b/ui/components/component-library/avatar-base/README.mdx new file mode 100644 index 000000000000..018cf831140a --- /dev/null +++ b/ui/components/component-library/avatar-base/README.mdx @@ -0,0 +1,97 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; + +import { AvatarBase } from './avatar-base'; + +### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components + +# AvatarBase + +The `AvatarBase` is a wrapper component responsible for enforcing dimensions and border radius for Avatar components + + + + + +## Props + +The `AvatarBase` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props. + + + +### Size + +Use the `size` prop to set the size of the `AvatarBase`. + +Possible sizes include: + +- `xs` 16px +- `sm` 24px +- `md` 32px +- `lg` 40px +- `xl` 48px + +Defaults to `md` + + + + + +```jsx +import { AVATAR_BASE_SIZES } from '../ui/component-library/avatar-base'; +import { AvatarBase } from '../../component-library'; + + + + + +``` + +### Children + +The `AvatarBase` component can contain images, icons or text + + + + + +```jsx +import { AvatarBase } from '../../component-library'; + + + + + + + + + +A +``` + +### Color, Background Color And Border Color + +Use the `color`, `backgroundColor` and `borderColor` props to set the text color, background-color and border-color of the `AvatarBase`. + + + + + +```jsx +import { COLORS } from '../../../helpers/constants/design-system'; +import { AvatarBase } from '../../component-library'; +B + + G + + + S + +``` diff --git a/ui/components/component-library/avatar-base/__snapshots__/avatar-base.test.js.snap b/ui/components/component-library/avatar-base/__snapshots__/avatar-base.test.js.snap new file mode 100644 index 000000000000..004fa7eadb13 --- /dev/null +++ b/ui/components/component-library/avatar-base/__snapshots__/avatar-base.test.js.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AvatarBase should render correctly 1`] = ` +
+
+
+`; diff --git a/ui/components/component-library/avatar-base/avatar-base.constants.js b/ui/components/component-library/avatar-base/avatar-base.constants.js new file mode 100644 index 000000000000..690b84363264 --- /dev/null +++ b/ui/components/component-library/avatar-base/avatar-base.constants.js @@ -0,0 +1,9 @@ +import { SIZES } from '../../../helpers/constants/design-system'; + +export const AVATAR_BASE_SIZES = { + XS: SIZES.XS, + SM: SIZES.SM, + MD: SIZES.MD, + LG: SIZES.LG, + XL: SIZES.XL, +}; diff --git a/ui/components/component-library/base-avatar/base-avatar.js b/ui/components/component-library/avatar-base/avatar-base.js similarity index 54% rename from ui/components/component-library/base-avatar/base-avatar.js rename to ui/components/component-library/avatar-base/avatar-base.js index a412f474d13b..927181a5812b 100644 --- a/ui/components/component-library/base-avatar/base-avatar.js +++ b/ui/components/component-library/avatar-base/avatar-base.js @@ -3,10 +3,11 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import Box from '../../ui/box/box'; -import { COLORS, SIZES } from '../../../helpers/constants/design-system'; +import { COLORS } from '../../../helpers/constants/design-system'; +import { AVATAR_BASE_SIZES } from './avatar-base.constants'; -export const BaseAvatar = ({ - size = SIZES.MD, +export const AvatarBase = ({ + size = AVATAR_BASE_SIZES.MD, children, backgroundColor = COLORS.BACKGROUND_ALTERNATIVE, borderColor = COLORS.BORDER_DEFAULT, @@ -16,8 +17,8 @@ export const BaseAvatar = ({ }) => ( ); -BaseAvatar.propTypes = { +AvatarBase.propTypes = { /** - * The size of the BaseAvatar. - * Possible values could be 'SIZES.XS', 'SIZES.SM', 'SIZES.MD', 'SIZES.LG', 'SIZES.XL' - * Defaults to SIZES.MD + * The size of the AvatarBase. + * Possible values could be 'AVATAR_BASE_SIZES.XS'(16px), 'AVATAR_BASE_SIZES.SM'(24px), 'AVATAR_BASE_SIZES.MD'(32px), 'AVATAR_BASE_SIZES.LG'(40px), 'AVATAR_BASE_SIZES.XL'(48px) + * Defaults to AVATAR_BASE_SIZES.MD */ - size: PropTypes.oneOf(Object.values(SIZES)), + size: PropTypes.oneOf(Object.values(AVATAR_BASE_SIZES)), /** - * The children to be rendered inside the BaseAvatar + * The children to be rendered inside the AvatarBase */ children: PropTypes.node, /** - * The background color of the BaseAvatar + * The background color of the AvatarBase * Defaults to COLORS.BACKGROUND_ALTERNATIVE */ backgroundColor: Box.propTypes.backgroundColor, /** - * The background color of the BaseAvatar + * The background color of the AvatarBase * Defaults to COLORS.BORDER_DEFAULT */ borderColor: Box.propTypes.borderColor, /** - * The color of the text inside the BaseAvatar + * The color of the text inside the AvatarBase * Defaults to COLORS.TEXT_DEFAULT */ color: Box.propTypes.color, @@ -57,7 +58,7 @@ BaseAvatar.propTypes = { */ className: PropTypes.string, /** - * BaseAvatar also accepts all Box props including but not limited to + * AvatarBase also accepts all Box props including but not limited to * className, as(change root element of HTML element) and margin props */ ...Box.propTypes, diff --git a/ui/components/component-library/base-avatar/base-avatar.scss b/ui/components/component-library/avatar-base/avatar-base.scss similarity index 96% rename from ui/components/component-library/base-avatar/base-avatar.scss rename to ui/components/component-library/avatar-base/avatar-base.scss index cf558da6f67c..257a897c77c5 100644 --- a/ui/components/component-library/base-avatar/base-avatar.scss +++ b/ui/components/component-library/avatar-base/avatar-base.scss @@ -1,4 +1,4 @@ -.base-avatar { +.mm-avatar-base { --avatar-size: var(--size, 16px); &--size-xs { diff --git a/ui/components/component-library/base-avatar/base-avatar.stories.js b/ui/components/component-library/avatar-base/avatar-base.stories.js similarity index 71% rename from ui/components/component-library/base-avatar/base-avatar.stories.js rename to ui/components/component-library/avatar-base/avatar-base.stories.js index defdb9fa9f07..4fe763551873 100644 --- a/ui/components/component-library/base-avatar/base-avatar.stories.js +++ b/ui/components/component-library/avatar-base/avatar-base.stories.js @@ -3,7 +3,6 @@ import { ALIGN_ITEMS, COLORS, DISPLAY, - SIZES, TEXT_COLORS, BACKGROUND_COLORS, BORDER_COLORS, @@ -12,7 +11,8 @@ import { import Box from '../../ui/box/box'; import README from './README.mdx'; -import { BaseAvatar } from './base-avatar'; +import { AvatarBase } from './avatar-base'; +import { AVATAR_BASE_SIZES } from './avatar-base.constants'; const marginSizeKnobOptions = [ 0, @@ -32,9 +32,9 @@ const marginSizeKnobOptions = [ ]; export default { - title: 'Components/ComponentLibrary/BaseAvatar', + title: 'Components/ComponentLibrary/AvatarBase', id: __filename, - component: BaseAvatar, + component: AvatarBase, parameters: { docs: { page: README, @@ -43,7 +43,7 @@ export default { argTypes: { size: { control: 'select', - options: Object.values(SIZES), + options: Object.values(AVATAR_BASE_SIZES), }, color: { options: Object.values(TEXT_COLORS), @@ -85,40 +85,40 @@ export default { }, }, args: { - size: SIZES.MD, + size: AVATAR_BASE_SIZES.MD, color: COLORS.TEXT_DEFAULT, backgroundColor: COLORS.BACKGROUND_ALTERNATIVE, borderColor: COLORS.BORDER_DEFAULT, }, }; -export const DefaultStory = (args) => B; +export const DefaultStory = (args) => B; DefaultStory.storyName = 'Default'; export const Size = (args) => ( - - - - - + + + + + ); export const Children = (args) => ( - + - - + + - - + + - - A - + A + ( className="fa fa-user" style={{ color: 'var(--color-info-default)' }} /> - + ); export const ColorBackgroundColorAndBorderColor = (args) => ( - B - B + G - - + S - + ); diff --git a/ui/components/component-library/avatar-base/avatar-base.test.js b/ui/components/component-library/avatar-base/avatar-base.test.js new file mode 100644 index 000000000000..8f14599d77e7 --- /dev/null +++ b/ui/components/component-library/avatar-base/avatar-base.test.js @@ -0,0 +1,124 @@ +/* eslint-disable jest/require-top-level-describe */ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { COLORS } from '../../../helpers/constants/design-system'; + +import { AvatarBase } from './avatar-base'; + +describe('AvatarBase', () => { + it('should render correctly', () => { + const { getByTestId, container } = render( + , + ); + expect(getByTestId('avatar-base')).toBeDefined(); + expect(container).toMatchSnapshot(); + }); + it('should render with different size classes', () => { + const { getByTestId } = render( + <> + + + + + + , + ); + expect(getByTestId('avatar-base-xs')).toHaveClass( + 'mm-avatar-base--size-xs', + ); + expect(getByTestId('avatar-base-sm')).toHaveClass( + 'mm-avatar-base--size-sm', + ); + expect(getByTestId('avatar-base-md')).toHaveClass( + 'mm-avatar-base--size-md', + ); + expect(getByTestId('avatar-base-lg')).toHaveClass( + 'mm-avatar-base--size-lg', + ); + expect(getByTestId('avatar-base-xl')).toHaveClass( + 'mm-avatar-base--size-xl', + ); + }); + // className + it('should render with custom className', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('avatar-base')).toHaveClass('test-class'); + }); + // children + it('should render children', () => { + render( + + + , + ); + const image = screen.getByRole('img'); + expect(image).toBeDefined(); + expect(image).toHaveAttribute('src', './images/arbitrum.svg'); + }); + // color + it('should render with different colors', () => { + const { getByTestId } = render( + <> + + + , + ); + expect(getByTestId(COLORS.SUCCESS_DEFAULT)).toHaveClass( + `box--color-${COLORS.SUCCESS_DEFAULT}`, + ); + expect(getByTestId(COLORS.ERROR_DEFAULT)).toHaveClass( + `box--color-${COLORS.ERROR_DEFAULT}`, + ); + }); + // background color + it('should render with different background colors', () => { + const { getByTestId } = render( + <> + + + , + ); + expect(getByTestId(COLORS.SUCCESS_DEFAULT)).toHaveClass( + `box--background-color-${COLORS.SUCCESS_DEFAULT}`, + ); + expect(getByTestId(COLORS.ERROR_DEFAULT)).toHaveClass( + `box--background-color-${COLORS.ERROR_DEFAULT}`, + ); + }); + // border color + it('should render with different border colors', () => { + const { getByTestId } = render( + <> + + + , + ); + expect(getByTestId(COLORS.SUCCESS_DEFAULT)).toHaveClass( + `box--border-color-${COLORS.SUCCESS_DEFAULT}`, + ); + expect(getByTestId(COLORS.ERROR_DEFAULT)).toHaveClass( + `box--border-color-${COLORS.ERROR_DEFAULT}`, + ); + }); +}); diff --git a/ui/components/component-library/avatar-base/index.js b/ui/components/component-library/avatar-base/index.js new file mode 100644 index 000000000000..5cd6109d9e85 --- /dev/null +++ b/ui/components/component-library/avatar-base/index.js @@ -0,0 +1,2 @@ +export { AvatarBase } from './avatar-base'; +export { AVATAR_BASE_SIZES } from './avatar-base.constants'; diff --git a/ui/components/component-library/avatar-favicon/avatar-favicon.js b/ui/components/component-library/avatar-favicon/avatar-favicon.js index 2f460685f4ba..d04a4d66bfcd 100644 --- a/ui/components/component-library/avatar-favicon/avatar-favicon.js +++ b/ui/components/component-library/avatar-favicon/avatar-favicon.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { BaseAvatar } from '../base-avatar'; +import { AvatarBase } from '../avatar-base'; import Box from '../../ui/box/box'; import { ICON_NAMES, Icon } from '../icon'; import { @@ -23,7 +23,7 @@ export const AvatarFavicon = ({ ...props }) => { return ( - )} - + ); }; diff --git a/ui/components/component-library/avatar-favicon/avatar-favicon.test.js b/ui/components/component-library/avatar-favicon/avatar-favicon.test.js index f23bef224ec4..3b508b41139f 100644 --- a/ui/components/component-library/avatar-favicon/avatar-favicon.test.js +++ b/ui/components/component-library/avatar-favicon/avatar-favicon.test.js @@ -27,7 +27,7 @@ describe('AvatarFavicon', () => { const { container } = render( , ); - expect(container.getElementsByClassName('icon')).toHaveLength(1); + expect(container.getElementsByClassName('mm-icon')).toHaveLength(1); }); it('should render fallback image with custom fallbackIconProps if no ImageSource is provided', () => { diff --git a/ui/components/component-library/avatar-network/avatar-network.js b/ui/components/component-library/avatar-network/avatar-network.js index e90013913240..d133b3f01870 100644 --- a/ui/components/component-library/avatar-network/avatar-network.js +++ b/ui/components/component-library/avatar-network/avatar-network.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { BaseAvatar } from '../base-avatar'; +import { AvatarBase } from '../avatar-base'; import Box from '../../ui/box/box'; import { @@ -36,7 +36,7 @@ export const AvatarNetwork = ({ }; return ( - )} - + ); }; diff --git a/ui/components/component-library/avatar-network/avatar-network.stories.js b/ui/components/component-library/avatar-network/avatar-network.stories.js index cbb96e63f05f..9d53ccfa9307 100644 --- a/ui/components/component-library/avatar-network/avatar-network.stories.js +++ b/ui/components/component-library/avatar-network/avatar-network.stories.js @@ -94,14 +94,14 @@ export const ColorBackgroundColorAndBorderColor = (args) => ( backgroundColor={COLORS.GOERLI} borderColor={COLORS.GOERLI} networkName="G" - color={COLORS.PRIMARY_INVERSE} // This will have to be added to the BaseAvatar component as a prop so we can change the color of the text and to the base avatar + color={COLORS.PRIMARY_INVERSE} // This will have to be added to the AvatarBase component as a prop so we can change the color of the text and to the base avatar /> ); diff --git a/ui/components/component-library/avatar-token/avatar-token.js b/ui/components/component-library/avatar-token/avatar-token.js index c29430abcbfb..214d40e23152 100644 --- a/ui/components/component-library/avatar-token/avatar-token.js +++ b/ui/components/component-library/avatar-token/avatar-token.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import classnames from 'classnames'; import PropTypes from 'prop-types'; import Box from '../../ui/box/box'; -import { BaseAvatar } from '../base-avatar'; +import { AvatarBase } from '../avatar-base'; import { COLORS, @@ -36,7 +36,7 @@ export const AvatarToken = ({ const fallbackString = tokenName && tokenName[0] ? tokenName[0] : '?'; return ( - )} - + ); }; diff --git a/ui/components/component-library/avatar-token/avatar-token.stories.js b/ui/components/component-library/avatar-token/avatar-token.stories.js index 5c097599c0c7..7fbac2b4cfe6 100644 --- a/ui/components/component-library/avatar-token/avatar-token.stories.js +++ b/ui/components/component-library/avatar-token/avatar-token.stories.js @@ -94,14 +94,14 @@ export const ColorBackgroundColorAndBorderColor = (args) => ( backgroundColor={COLORS.GOERLI} borderColor={COLORS.GOERLI} tokenName="G" - color={COLORS.PRIMARY_INVERSE} // TODO: This will have to be added to the BaseAvatar component as a prop so we can change the color of the text and to the base avatar + color={COLORS.PRIMARY_INVERSE} // TODO: This will have to be added to the AvatarBase component as a prop so we can change the color of the text and to the base avatar /> ); diff --git a/ui/components/component-library/base-avatar/README.mdx b/ui/components/component-library/base-avatar/README.mdx deleted file mode 100644 index c30f0ab08635..000000000000 --- a/ui/components/component-library/base-avatar/README.mdx +++ /dev/null @@ -1,53 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import { BaseAvatar } from './base-avatar'; - -### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components - -# BaseAvatar - -The `BaseAvatar` is a wrapper component responsible for enforcing dimensions and border radius for Avatar components - - - - - -## Props - -The `BaseAvatar` accepts all props below as well as all [Box](/ui-components-ui-box-box-stories-js--default-story) component props. - - - -### Size - -Use the `size` prop to set the size of the `BaseAvatar`. - -Possible sizes include: - -- `xs` 16px -- `sm` 24px -- `md` 32px -- `lg` 40px -- `xl` 48px - -Defaults to `md` - - - - - -### Children - -The `BaseAvatar` component can contain images, icons or text - - - - - -### Color, Background Color And Border Color - -Use the `color`, `backgroundColor` and `borderColor` props to set the text color, background-color and border-color of the s of the `BaseAvatar`. - - - - diff --git a/ui/components/component-library/base-avatar/base-avatar.test.js b/ui/components/component-library/base-avatar/base-avatar.test.js deleted file mode 100644 index 5c37030f628f..000000000000 --- a/ui/components/component-library/base-avatar/base-avatar.test.js +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable jest/require-top-level-describe */ -import { render } from '@testing-library/react'; -import React from 'react'; - -import { BaseAvatar } from './base-avatar'; - -describe('BaseAvatar', () => { - it('should render correctly', () => { - const { getByTestId } = render(); - expect(getByTestId('base-avatar')).toBeDefined(); - }); - it('should render with different size classes', () => { - const { getByTestId } = render( - <> - - - - - - , - ); - expect(getByTestId('base-avatar-xs')).toHaveClass('base-avatar--size-xs'); - expect(getByTestId('base-avatar-sm')).toHaveClass('base-avatar--size-sm'); - expect(getByTestId('base-avatar-md')).toHaveClass('base-avatar--size-md'); - expect(getByTestId('base-avatar-lg')).toHaveClass('base-avatar--size-lg'); - expect(getByTestId('base-avatar-xl')).toHaveClass('base-avatar--size-xl'); - }); -}); diff --git a/ui/components/component-library/base-avatar/index.js b/ui/components/component-library/base-avatar/index.js deleted file mode 100644 index b37a9d558018..000000000000 --- a/ui/components/component-library/base-avatar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { BaseAvatar } from './base-avatar'; diff --git a/ui/components/component-library/button-base/button-base.js b/ui/components/component-library/button-base/button-base.js index 426b65a311e7..ed701d9dbd98 100644 --- a/ui/components/component-library/button-base/button-base.js +++ b/ui/components/component-library/button-base/button-base.js @@ -35,6 +35,7 @@ export const ButtonBase = ({ return ( { expect(anchor).toBe(1); }); - it('should render anchor element correctly by href only being passed', () => { + it('should render anchor element correctly by href only being passed and href exists', () => { const { getByTestId, container } = render( - , + + Button Base + , ); expect(getByTestId('button-base')).toHaveClass('mm-button'); + expect(getByTestId('button-base')).toHaveAttribute( + 'href', + 'https://www.test.com/', + ); const anchor = container.getElementsByTagName('a').length; expect(anchor).toBe(1); }); diff --git a/ui/components/component-library/button-icon/__snapshots__/button-icon.test.js.snap b/ui/components/component-library/button-icon/__snapshots__/button-icon.test.js.snap index ce55c094a735..6100807d7332 100644 --- a/ui/components/component-library/button-icon/__snapshots__/button-icon.test.js.snap +++ b/ui/components/component-library/button-icon/__snapshots__/button-icon.test.js.snap @@ -8,8 +8,8 @@ exports[`ButtonIcon should render button element correctly 1`] = ` data-testid="button-icon" >
diff --git a/ui/components/component-library/button-link/button-link.test.js b/ui/components/component-library/button-link/button-link.test.js index be6deb2b4c91..01d4abb4776c 100644 --- a/ui/components/component-library/button-link/button-link.test.js +++ b/ui/components/component-library/button-link/button-link.test.js @@ -79,7 +79,7 @@ describe('ButtonLink', () => { , ); - const icons = container.getElementsByClassName('icon').length; + const icons = container.getElementsByClassName('mm-icon').length; expect(icons).toBe(1); }); }); diff --git a/ui/components/component-library/button-primary/button-primary.test.js b/ui/components/component-library/button-primary/button-primary.test.js index 83892c345a0e..cb9125c8e65e 100644 --- a/ui/components/component-library/button-primary/button-primary.test.js +++ b/ui/components/component-library/button-primary/button-primary.test.js @@ -92,7 +92,7 @@ describe('ButtonPrimary', () => { , ); - const icons = container.getElementsByClassName('icon').length; + const icons = container.getElementsByClassName('mm-icon').length; expect(icons).toBe(1); }); }); diff --git a/ui/components/component-library/button-secondary/button-secondary.test.js b/ui/components/component-library/button-secondary/button-secondary.test.js index 8b2b14896261..7552ebabb506 100644 --- a/ui/components/component-library/button-secondary/button-secondary.test.js +++ b/ui/components/component-library/button-secondary/button-secondary.test.js @@ -96,7 +96,7 @@ describe('ButtonSecondary', () => { , ); - const icons = container.getElementsByClassName('icon').length; + const icons = container.getElementsByClassName('mm-icon').length; expect(icons).toBe(1); }); }); diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss index 898d3945d141..02e900189b02 100644 --- a/ui/components/component-library/component-library-components.scss +++ b/ui/components/component-library/component-library-components.scss @@ -9,7 +9,7 @@ @import 'icon/icon'; @import 'label/label'; @import 'tag/tag'; -@import 'base-avatar/base-avatar'; +@import 'avatar-base/avatar-base'; @import 'avatar-account/avatar-account'; @import 'avatar-favicon/avatar-favicon'; @import 'avatar-network/avatar-network'; @@ -26,3 +26,4 @@ @import 'text-field/text-field'; @import 'text-field-base/text-field-base'; @import 'text-field-search/text-field-search'; +@import 'form-text-field/form-text-field'; diff --git a/ui/components/component-library/form-text-field/README.mdx b/ui/components/component-library/form-text-field/README.mdx new file mode 100644 index 000000000000..25b00104346d --- /dev/null +++ b/ui/components/component-library/form-text-field/README.mdx @@ -0,0 +1,336 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; + +import { TextField, TextFieldBase } from '../'; +import { FormTextField } from './form-text-field'; + +# FormTextField + +The `FormTextField` is an input component to create forms. It bundles the [TextField](/docs/ui-components-component-library-text-field-text-field-stories-js--default-story), [Label](/docs/ui-components-component-library-label-label-stories-js--default-story) and [HelpText](/docs/ui-components-component-library-help-text-help-text-stories-js--default-story) components together. + + + + + +## Props + +The `FormTextField` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props + + + +`FormTextField` accepts all [TextField](/docs/ui-components-component-library-text-field-text-field-stories-js--default-story#props) +component props + + + +`FormTextField` accepts all [TextFieldBase](/docs/ui-components-component-library-text-field-base-text-field-base-stories-js--default-story#props) +component props + + + +### Id + +Use the `id` prop to set the `id` of the `FormTextField` component. This is required for accessibility when the `label` prop is set. It is also used internally to link the `label` and `input` elements using `htmlFor`, so clicking on the `label` will focus the `input`. + + + + + +```jsx +import { FormTextField } from '../../component-library'; + +; +``` + +### Label + +Use the `label` prop to add a label to the `FormTextField` component. Uses the [Label](/docs/ui-components-component-library-label-label-stories-js--default-story) component. Use the `labelProps` prop to pass props to the `Label` component. To use a custom label component see the [Custom Label or HelpText](#custom-label-or-helptext) story example. + + + + + +```jsx +import { FormTextField } from '../../component-library'; + +; +``` + +### HelpText + +Use the `helpText` prop to add help text to the `FormTextField` component. Uses the [HelpText](/docs/ui-components-component-library-helpText-helpText-stories-js--default-story) component. Use the `helpTextProps` prop to pass props to the `HelpText` component. To use a custom help text component see the [Custom Label or HelpText](#custom-helpText-or-helptext) story example. When `error` is true the `helpText` will be rendered as an error message. + + + + + +```jsx +import { FormTextField } from '../../component-library'; + +; +; +``` + +### Form Example + +An example of a form using the `FormTextField` component. + + + + + +```jsx +import React, { useState, useEffect } from 'react'; +import { + DISPLAY, + COLORS, + ALIGN_ITEMS, + TEXT, +} from '../../../helpers/constants/design-system'; + +import Box from '../../ui/box/box'; + +import { + ButtonPrimary, + ButtonSecondary, + FormTextField, + ICON_NAMES, + Text, +} from '../../component-library'; + +const FORM_STATE = { + DEFAULT: 'default', + SUCCESS: 'success', + ERROR: 'error', +}; + +const VALIDATED_VALUES = { + NETWORK_NAME: 'network name', + NEW_RPC_URL: 'new rpc url', + CHAIN_ID: 'chain id', +}; + +const ERROR_MESSAGES = { + NETWORK_NAME: `Please enter "${VALIDATED_VALUES.NETWORK_NAME}"`, + NEW_RPC_URL: `Please enter "${VALIDATED_VALUES.NEW_RPC_URL}"`, + CHAIN_ID: `Please enter "${VALIDATED_VALUES.CHAIN_ID}"`, +}; + +const [submitted, setSubmitted] = useState(FORM_STATE.DEFAULT); + +const [values, setValues] = useState({ + networkName: '', + newRpcUrl: '', + chainId: '', +}); + +const [errors, setErrors] = useState({ + networkName: '', + newRpcUrl: '', + chainId: '', +}); + +useEffect(() => { + setErrors({ + networkName: + values.networkName && + values.networkName.toLowerCase() !== VALIDATED_VALUES.NETWORK_NAME + ? ERROR_MESSAGES.NETWORK_NAME + : '', + newRpcUrl: + values.newRpcUrl && + values.newRpcUrl.toLowerCase() !== VALIDATED_VALUES.NEW_RPC_URL + ? ERROR_MESSAGES.NEW_RPC_URL + : '', + chainId: + values.chainId && + values.chainId.toLowerCase() !== VALIDATED_VALUES.CHAIN_ID + ? ERROR_MESSAGES.CHAIN_ID + : '', + }); +}, [values]); + +const handleClearForm = () => { + setValues({ networkName: '', newRpcUrl: '', chainId: '' }); + setErrors({ networkName: '', newRpcUrl: '', chainId: '' }); + setSubmitted(FORM_STATE.DEFAULT); +}; + +const handleOnChange = (e) => { + if (submitted === FORM_STATE.ERROR) { + setErrors({ networkName: '', newRpcUrl: '', chainId: '' }); + setSubmitted(FORM_STATE.DEFAULT); + } + setValues({ + ...values, + [e.target.name]: e.target.value, + }); +}; + +const handleOnSubmit = (e) => { + e.preventDefault(); + if (errors.networkName || errors.newRpcUrl || errors.chainId) { + setSubmitted(FORM_STATE.ERROR); + } else { + setSubmitted(FORM_STATE.SUCCESS); + } +}; + +return ( + <> + + + + + + Submit + + + + Clear form + + {submitted === FORM_STATE.SUCCESS && ( + + Form successfully submitted! + + )} + +); +``` + +### Custom Label or HelpText + +There will be times when you will want to use a custom `Label` or `HelpText`. This can be done by simply not providing `label` or `helpText` props to the `FormTextField` component. You can then use the `Label` and `HelpText` components to create your own custom label or help text. + + + + + +```jsx +import { + SIZES, + DISPLAY, + COLORS, + ALIGN_ITEMS, + JUSTIFY_CONTENT, +} from '../../../helpers/constants/design-system'; + +import Box from '../../ui/box/box'; + +import { + ButtonLink, + FormTextField, + HelpText, + ICON_NAMES, + Icon, + Label, + TEXT_FIELD_TYPES, + Text, +} from '../../component-library'; + + + Examples of how one might customize the Label or HelpText within the + FormTextField component + + + + {/** + * If you need a custom label + * or require adding some form of customization + * import the Label component separately + */} + + + + Use default + +Max} + marginBottom={4} + type={TEXT_FIELD_TYPES.NUMBER} +/> + + + {/** + * If you need a custom help text + * or require adding some form of customization + * import the HelpText component separately and handle the error + * logic yourself + */} + + Only enter a number that you're comfortable with the contract accessing + now or in the future. You can always increase the token limit later. + + + Max + + +``` diff --git a/ui/components/component-library/form-text-field/__snapshots__/form-text-field.test.js.snap b/ui/components/component-library/form-text-field/__snapshots__/form-text-field.test.js.snap new file mode 100644 index 000000000000..6332f658aaad --- /dev/null +++ b/ui/components/component-library/form-text-field/__snapshots__/form-text-field.test.js.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FormTextField should render correctly 1`] = ` +
+
+
+ +
+
+
+`; diff --git a/ui/components/component-library/form-text-field/form-text-field.js b/ui/components/component-library/form-text-field/form-text-field.js new file mode 100644 index 000000000000..5c46f5bc3454 --- /dev/null +++ b/ui/components/component-library/form-text-field/form-text-field.js @@ -0,0 +1,167 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { + DISPLAY, + FLEX_DIRECTION, + SIZES, +} from '../../../helpers/constants/design-system'; + +import Box from '../../ui/box/box'; + +import { TextField } from '../text-field'; +import { HelpText } from '../help-text'; +import { Label } from '../label'; + +export const FormTextField = ({ + autoComplete, + autoFocus, + className, + defaultValue, + disabled, + error, + helpText, + helpTextProps, + id, + inputProps, + inputRef, + label, + labelProps, + leftAccessory, + maxLength, + name, + onBlur, + onChange, + onFocus, + placeholder, + readOnly, + required, + rightAccessory, + size = SIZES.MD, + textFieldProps, + truncate, + showClearButton, + clearButtonOnClick, + clearButtonProps, + type = 'text', + value, + ...props +}) => ( + + {label && ( + + )} + + {helpText && ( + + {helpText} + + )} + +); + +FormTextField.propTypes = { + /** + * An additional className to apply to the form-text-field + */ + className: PropTypes.string, + /** + * The id of the FormTextField + * Required if label prop exists to ensure accessibility + * + * @param {object} props - The props passed to the component. + * @param {string} propName - The prop name in this case 'id'. + * @param {string} componentName - The name of the component. + */ + id: (props, propName, componentName) => { + if (props.label && !props[propName]) { + return new Error( + `If a label prop exists you must provide an ${propName} prop for the label's htmlFor attribute for accessibility. Warning coming from ${componentName} ui/components/component-library/form-text-field/form-text-field.js`, + ); + } + return null; + }, + /** + * The content of the Label component + */ + label: PropTypes.string, + /** + * Props that are applied to the Label component + */ + labelProps: PropTypes.object, + /** + * The content of the HelpText component + */ + helpText: PropTypes.string, + /** + * Props that are applied to the HelpText component + */ + helpTextProps: PropTypes.object, + /** + * Props that are applied to the TextField component + */ + textFieldProps: PropTypes.object, + /** + * FormTextField accepts all the props from TextField and Box + */ + ...TextField.propTypes, +}; diff --git a/ui/components/component-library/form-text-field/form-text-field.scss b/ui/components/component-library/form-text-field/form-text-field.scss new file mode 100644 index 000000000000..c4316f8c16e3 --- /dev/null +++ b/ui/components/component-library/form-text-field/form-text-field.scss @@ -0,0 +1,9 @@ +.mm-form-text-field { + --help-text-opacity-disabled: 0.5; + + &--disabled { + .mm-form-text-field__help-text { + opacity: var(--help-text-opacity-disabled); + } + } +} diff --git a/ui/components/component-library/form-text-field/form-text-field.stories.js b/ui/components/component-library/form-text-field/form-text-field.stories.js new file mode 100644 index 000000000000..d38f27f7b1da --- /dev/null +++ b/ui/components/component-library/form-text-field/form-text-field.stories.js @@ -0,0 +1,481 @@ +import React, { useState, useEffect } from 'react'; +import { useArgs } from '@storybook/client-api'; + +import { + SIZES, + DISPLAY, + COLORS, + ALIGN_ITEMS, + TEXT, + JUSTIFY_CONTENT, +} from '../../../helpers/constants/design-system'; + +import Box from '../../ui/box/box'; + +import { + ButtonLink, + ButtonPrimary, + ButtonSecondary, + HelpText, + Icon, + ICON_NAMES, + Label, + Text, + TEXT_FIELD_SIZES, + TEXT_FIELD_TYPES, +} from '..'; + +import { FormTextField } from './form-text-field'; + +import README from './README.mdx'; + +const marginSizeControlOptions = [ + undefined, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 'auto', +]; + +export default { + title: 'Components/ComponentLibrary/FormTextField', + id: __filename, + component: FormTextField, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + value: { + control: 'text', + }, + onChange: { + action: 'onChange', + }, + labelProps: { + control: 'object', + }, + textFieldProps: { + control: 'object', + }, + helpTextProps: { + control: 'object', + }, + showClearButton: { + control: 'boolean', + table: { category: 'text field props' }, + }, + clearButtonOnClick: { + action: 'clearButtonOnClick', + table: { category: 'text field props' }, + }, + clearButtonProps: { + control: 'object', + table: { category: 'text field props' }, + }, + autoComplete: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + autoFocus: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + className: { + control: 'text', + table: { category: 'text field base props' }, + }, + disabled: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + error: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + id: { + control: 'text', + table: { category: 'text field base props' }, + }, + inputProps: { + control: 'object', + table: { category: 'text field base props' }, + }, + leftAccessory: { + control: 'text', + table: { category: 'text field base props' }, + }, + maxLength: { + control: 'number', + table: { category: 'text field base props' }, + }, + name: { + control: 'text', + table: { category: 'text field base props' }, + }, + onBlur: { + action: 'onBlur', + table: { category: 'text field base props' }, + }, + onClick: { + action: 'onClick', + table: { category: 'text field base props' }, + }, + onFocus: { + action: 'onFocus', + table: { category: 'text field base props' }, + }, + onKeyDown: { + action: 'onKeyDown', + table: { category: 'text field base props' }, + }, + onKeyUp: { + action: 'onKeyUp', + table: { category: 'text field base props' }, + }, + placeholder: { + control: 'text', + table: { category: 'text field base props' }, + }, + readOnly: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + required: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + rightAccessory: { + control: 'text', + table: { category: 'text field base props' }, + }, + size: { + control: 'select', + options: Object.values(TEXT_FIELD_SIZES), + table: { category: 'text field base props' }, + }, + type: { + control: 'select', + options: Object.values(TEXT_FIELD_TYPES), + table: { category: 'text field base props' }, + }, + truncate: { + control: 'boolean', + table: { category: 'text field base props' }, + }, + marginTop: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginRight: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginBottom: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginLeft: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + }, + args: { + placeholder: 'Form text field', + label: 'Label', + id: 'form-text-field', + helpText: 'Help text', + }, +}; + +const Template = (args) => { + const [{ value }, updateArgs] = useArgs(); + const handleOnChange = (e) => { + updateArgs({ value: e.target.value }); + }; + const handleOnClear = () => { + updateArgs({ value: '' }); + }; + return ( + + ); +}; + +export const DefaultStory = Template.bind({}); +DefaultStory.storyName = 'Default'; + +export const Id = Template.bind({}); +Id.args = { + id: 'accessible-input-id', + label: 'If label prop exists id prop is required for accessibility', + helpText: '', +}; + +export const LabelStory = Template.bind({}); +LabelStory.storyName = 'Label'; // Need to use LabelStory to avoid conflict with Label component +LabelStory.args = { + id: 'input-with-label', + label: 'Label content appears here', + helpText: '', +}; + +export const HelpTextStory = (args) => { + const [{ value }, updateArgs] = useArgs(); + const handleOnChange = (e) => { + updateArgs({ value: e.target.value }); + }; + const handleOnClear = () => { + updateArgs({ value: '' }); + }; + return ( + <> + + + + ); +}; +HelpTextStory.storyName = 'HelpText'; // Need to use HelpTextStory to avoid conflict with HelpTextStory component +HelpTextStory.args = { + label: '', + helpText: 'HelpText content appears here', +}; + +export const FormExample = () => { + const FORM_STATE = { + DEFAULT: 'default', + SUCCESS: 'success', + ERROR: 'error', + }; + const VALIDATED_VALUES = { + NETWORK_NAME: 'network name', + NEW_RPC_URL: 'new rpc url', + CHAIN_ID: 'chain id', + }; + const ERROR_MESSAGES = { + NETWORK_NAME: `Please enter "${VALIDATED_VALUES.NETWORK_NAME}"`, + NEW_RPC_URL: `Please enter "${VALIDATED_VALUES.NEW_RPC_URL}"`, + CHAIN_ID: `Please enter "${VALIDATED_VALUES.CHAIN_ID}"`, + }; + const [submitted, setSubmitted] = useState(FORM_STATE.DEFAULT); + const [values, setValues] = useState({ + networkName: '', + newRpcUrl: '', + chainId: '', + }); + const [errors, setErrors] = useState({ + networkName: '', + newRpcUrl: '', + chainId: '', + }); + useEffect(() => { + setErrors({ + networkName: + values.networkName && + values.networkName.toLowerCase() !== VALIDATED_VALUES.NETWORK_NAME + ? ERROR_MESSAGES.NETWORK_NAME + : '', + newRpcUrl: + values.newRpcUrl && + values.newRpcUrl.toLowerCase() !== VALIDATED_VALUES.NEW_RPC_URL + ? ERROR_MESSAGES.NEW_RPC_URL + : '', + chainId: + values.chainId && + values.chainId.toLowerCase() !== VALIDATED_VALUES.CHAIN_ID + ? ERROR_MESSAGES.CHAIN_ID + : '', + }); + }, [ + values, + ERROR_MESSAGES.CHAIN_ID, + ERROR_MESSAGES.NETWORK_NAME, + ERROR_MESSAGES.NEW_RPC_URL, + VALIDATED_VALUES.CHAIN_ID, + VALIDATED_VALUES.NETWORK_NAME, + VALIDATED_VALUES.NEW_RPC_URL, + ]); + const handleClearForm = () => { + setValues({ networkName: '', newRpcUrl: '', chainId: '' }); + setErrors({ networkName: '', newRpcUrl: '', chainId: '' }); + setSubmitted(FORM_STATE.DEFAULT); + }; + const handleOnChange = (e) => { + if (submitted === FORM_STATE.ERROR) { + setErrors({ networkName: '', newRpcUrl: '', chainId: '' }); + setSubmitted(FORM_STATE.DEFAULT); + } + setValues({ + ...values, + [e.target.name]: e.target.value, + }); + }; + const handleOnSubmit = (e) => { + e.preventDefault(); + if (errors.networkName || errors.newRpcUrl || errors.chainId) { + setSubmitted(FORM_STATE.ERROR); + } else { + setSubmitted(FORM_STATE.SUCCESS); + } + }; + return ( + <> + + + + + + Submit + + + + Clear form + + {submitted === FORM_STATE.SUCCESS && ( + + Form successfully submitted! + + )} + + ); +}; + +export const CustomLabelOrHelpText = () => ( + <> + + Examples of how one might customize the Label or HelpText within the + FormTextField component + + + + {/* If you need a custom label + or require adding some form of customization + import the Label component separately */} + + + + Use default + + Max} + marginBottom={4} + type={TEXT_FIELD_TYPES.NUMBER} + /> + + + {/* If you need a custom help text + or require adding some form of customization + import the HelpText component separately and handle the error + logic yourself */} + + Only enter a number that you're comfortable with the contract + accessing now or in the future. You can always increase the token limit + later. + + + Max + + + +); diff --git a/ui/components/component-library/form-text-field/form-text-field.test.js b/ui/components/component-library/form-text-field/form-text-field.test.js new file mode 100644 index 000000000000..02edd58349ff --- /dev/null +++ b/ui/components/component-library/form-text-field/form-text-field.test.js @@ -0,0 +1,349 @@ +/* eslint-disable jest/require-top-level-describe */ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; + +import { + renderControlledInput, + renderWithUserEvent, +} from '../../../../test/lib/render-helpers'; + +import { SIZES } from '../../../helpers/constants/design-system'; + +import { FormTextField } from './form-text-field'; + +describe('FormTextField', () => { + it('should render correctly', () => { + const { getByRole, container } = render(); + expect(getByRole('textbox')).toBeDefined(); + expect(container).toMatchSnapshot(); + }); + // autoComplete + it('should render with autoComplete', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('form-text-field-auto-complete')).toHaveAttribute( + 'autocomplete', + 'on', + ); + }); + // autoFocus + it('should render with autoFocus', () => { + const { getByRole } = render(); + expect(getByRole('textbox')).toHaveFocus(); + }); + // className + it('should render with custom className', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('form-text-field')).toHaveClass('test-class'); + }); + // defaultValue + it('should render with a defaultValue', () => { + const { getByRole } = render( + , + ); + expect(getByRole('textbox').value).toBe('default value'); + }); + // disabled + it('should render in disabled state and not focus or be clickable', async () => { + const mockOnClick = jest.fn(); + const mockOnFocus = jest.fn(); + const { getByRole, user, getByLabelText } = renderWithUserEvent( + , + ); + + await user.click(getByLabelText('test label')); + expect(mockOnFocus).toHaveBeenCalledTimes(0); + await user.type(getByRole('textbox'), 'test value'); + expect(getByRole('textbox')).not.toHaveValue('test value'); + + expect(getByRole('textbox')).toBeDisabled(); + expect(mockOnClick).toHaveBeenCalledTimes(0); + expect(mockOnFocus).toHaveBeenCalledTimes(0); + }); + // error + it('should render with error classNames on TextField and HelpText components when error is true', () => { + const { getByTestId, getByText } = render( + , + ); + expect(getByTestId('text-field')).toHaveClass('mm-text-field-base--error'); + expect(getByText('test help text')).toHaveClass( + 'text--color-error-default', + ); + }); + // helpText + it('should render with helpText', () => { + const { getByText } = render(); + expect(getByText('test help text')).toBeDefined(); + }); + // helpTextProps + it('should render with helpText and helpTextProps', () => { + const { getByText, getByTestId } = render( + , + ); + expect(getByText('test help text')).toBeDefined(); + expect(getByTestId('help-text-test')).toBeDefined(); + }); + // id + it('should render the FormTextField with an id and pass it to input and Label as htmlFor. When clicking on Label the input should have focus', async () => { + const onFocus = jest.fn(); + const { getByRole, getByLabelText, user } = renderWithUserEvent( + , + ); + expect(getByRole('textbox')).toHaveAttribute('id', 'test-id'); + await user.click(getByLabelText('test label')); + expect(onFocus).toHaveBeenCalledTimes(1); + expect(getByRole('textbox')).toHaveFocus(); + }); + // inputProps + it('should render with inputProps', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('test-id')).toBeDefined(); + }); + // inputRef + it('should render with working ref using inputRef prop', () => { + // Because the 'ref' attribute wont flow down to the DOM + // I'm not exactly sure how to test this? + const mockRef = jest.fn(); + const { getByRole } = render(); + expect(getByRole('textbox')).toBeDefined(); + expect(mockRef).toHaveBeenCalledTimes(1); + }); + // label + it('should render with a label', () => { + const { getByLabelText } = render( + , + ); + expect(getByLabelText('test label')).toBeDefined(); + }); + // labelProps + it('should render with a labelProps', () => { + const { getByTestId, getByLabelText } = render( + , + ); + expect(getByLabelText('test label')).toBeDefined(); + expect(getByTestId('label-test-id')).toBeDefined(); + }); + // leftAccessory, // rightAccessory + it('should render with right and left accessories', () => { + const { getByRole, getByText } = render( + left accessory
} + rightAccessory={
right accessory
} + />, + ); + expect(getByRole('textbox')).toBeDefined(); + expect(getByText('left accessory')).toBeDefined(); + expect(getByText('right accessory')).toBeDefined(); + }); + // maxLength; + it('should render with maxLength and not allow more than the set characters', async () => { + const { getByRole, user } = renderWithUserEvent( + , + ); + const formTextField = getByRole('textbox'); + await user.type(formTextField, '1234567890'); + expect(getByRole('textbox')).toBeDefined(); + expect(formTextField.maxLength).toBe(5); + expect(formTextField.value).toBe('12345'); + expect(formTextField.value).toHaveLength(5); + }); + // name + it('should render with name prop', () => { + const { getByRole } = render(); + expect(getByRole('textbox')).toHaveAttribute('name', 'test-name'); + }); + // onBlur, // onFocus + it('should render and fire onFocus and onBlur events', async () => { + const onFocus = jest.fn(); + const onBlur = jest.fn(); + const { getByTestId, user } = renderWithUserEvent( + , + ); + const formTextField = getByTestId('form-text-field'); + + await user.click(formTextField); + expect(onFocus).toHaveBeenCalledTimes(1); + fireEvent.blur(formTextField); + expect(onBlur).toHaveBeenCalledTimes(1); + }); + // onChange + it('should render and fire onChange event', async () => { + const onChange = jest.fn(); + const { user, getByRole } = renderWithUserEvent( + , + ); + await user.type(getByRole('textbox'), 'test'); + expect(onChange).toHaveBeenCalledTimes(4); + }); + // placeholder + it('should render with placeholder', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('form-text-field-auto-complete')).toHaveAttribute( + 'placeholder', + 'test placeholder', + ); + }); + // readOnly + it('should render with readOnly attr when readOnly is true', async () => { + const { getByRole, user } = renderWithUserEvent( + , + ); + await user.type(getByRole('textbox'), 'test'); + expect(getByRole('textbox')).toHaveValue('test value'); + expect(getByRole('textbox')).toHaveAttribute('readonly', ''); + }); + // required + it('should render with required asterisk after Label', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('label-test-id')).toHaveTextContent('test label*'); + }); + // size = SIZES.MD + it('should render with different size classes', () => { + const { getByTestId } = render( + <> + + + + , + ); + expect(getByTestId('sm')).toHaveClass('mm-text-field-base--size-sm'); + expect(getByTestId('md')).toHaveClass('mm-text-field-base--size-md'); + expect(getByTestId('lg')).toHaveClass('mm-text-field-base--size-lg'); + }); + // textFieldProps + it('should render with textFieldProps', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('test-text-field')).toBeDefined(); + }); + // truncate + it('should render with truncate class as true by default and remove it when truncate is false', () => { + const { getByTestId } = render( + <> + + + , + ); + expect(getByTestId('truncate')).toHaveClass('mm-text-field-base--truncate'); + expect(getByTestId('no-truncate')).not.toHaveClass( + 'mm-text-field-base--truncate', + ); + }); + // showClearButton + it('should render showClearButton button when showClearButton is true and value exists', async () => { + // As showClearButton is intended to be used with a controlled input we need to use renderControlledInput + const { user, getByRole } = renderControlledInput(FormTextField, { + showClearButton: true, + }); + await user.type(getByRole('textbox'), 'test value'); + expect(getByRole('textbox')).toHaveValue('test value'); + expect(getByRole('button', { name: /Clear/u })).toBeDefined(); + }); + // clearButtonOnClick + it('should fire onClick event when passed to clearButtonOnClick when clear button is clicked', async () => { + // As showClearButton is intended to be used with a controlled input we need to use renderControlledInput + const fn = jest.fn(); + const { user, getByRole } = renderControlledInput(FormTextField, { + showClearButton: true, + clearButtonOnClick: fn, + }); + await user.type(getByRole('textbox'), 'test value'); + await user.click(getByRole('button', { name: /Clear/u })); + expect(fn).toHaveBeenCalledTimes(1); + }); + // clearButtonProps, + it('should fire onClick event when passed to clearButtonProps.onClick prop', async () => { + // As showClearButton is intended to be used with a controlled input we need to use renderControlledInput + const fn = jest.fn(); + const { user, getByRole } = renderControlledInput(FormTextField, { + showClearButton: true, + clearButtonProps: { onClick: fn }, + }); + await user.type(getByRole('textbox'), 'test value'); + await user.click(getByRole('button', { name: /Clear/u })); + expect(fn).toHaveBeenCalledTimes(1); + }); + // type, + it('should render with different types', () => { + const { getByTestId } = render( + <> + + + + , + ); + expect(getByTestId('form-text-field-text')).toHaveAttribute('type', 'text'); + expect(getByTestId('form-text-field-number')).toHaveAttribute( + 'type', + 'number', + ); + expect(getByTestId('form-text-field-password')).toHaveAttribute( + 'type', + 'password', + ); + }); +}); diff --git a/ui/components/component-library/form-text-field/index.js b/ui/components/component-library/form-text-field/index.js new file mode 100644 index 000000000000..ddcb200ba72c --- /dev/null +++ b/ui/components/component-library/form-text-field/index.js @@ -0,0 +1 @@ +export { FormTextField } from './form-text-field'; diff --git a/ui/components/component-library/icon/README.mdx b/ui/components/component-library/icon/README.mdx index d18913802c4e..55a7e3f93ebb 100644 --- a/ui/components/component-library/icon/README.mdx +++ b/ui/components/component-library/icon/README.mdx @@ -20,21 +20,22 @@ The `Icon` accepts all props below as well as all [Box](/docs/ui-components-ui-b Use the `name` prop and the `ICON_NAMES` object to change the icon. -Use the [IconSearch](/ui-components-component-library-icon-icon-stories-js--name) story to find the icon you want to use. +Use the [IconSearch](/story/ui-components-component-library-icon-icon-stories-js--default-story) story to find the icon you want to use. + + + + ```jsx -import { Icon, ICON_NAMES } from '../../ui/components/component-library'; +import { Icon, ICON_NAMES } from '../../components/component-library'; +// etc... ``` - - - - ### Size Use the `size` prop and the `SIZES` object from `./ui/helpers/constants/design-system.js` to change the size of `Icon`. Defaults to `SIZES.SM` @@ -55,7 +56,7 @@ Possible sizes include: ```jsx import { SIZES } from '../../../helpers/constants/design-system'; -import { Icon, ICON_NAMES } from '../../ui/components/component-library'; +import { Icon, ICON_NAMES } from '../../components/component-library'; @@ -79,7 +80,7 @@ Use the `color` prop and the `COLORS` object from `./ui/helpers/constants/design ```jsx import { COLORS } from '../../../helpers/constants/design-system'; -import { Icon, ICON_NAMES } from '../../ui/components/component-library'; +import { Icon, ICON_NAMES } from '../../components/component-library'; diff --git a/ui/components/component-library/icon/__snapshots__/icon.test.js.snap b/ui/components/component-library/icon/__snapshots__/icon.test.js.snap new file mode 100644 index 000000000000..fa37d3955da7 --- /dev/null +++ b/ui/components/component-library/icon/__snapshots__/icon.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Icon should render correctly 1`] = ` +
+
+
+`; diff --git a/ui/components/component-library/icon/icon.constants.js b/ui/components/component-library/icon/icon.constants.js index ca47099bce8e..7cd4da2c1cda 100644 --- a/ui/components/component-library/icon/icon.constants.js +++ b/ui/components/component-library/icon/icon.constants.js @@ -1,11 +1,25 @@ +import { SIZES } from '../../../helpers/constants/design-system'; + /** * The ICON_NAMES object contains all the possible icon names. - * It is generated using the generateIconNames script in development/generate-icon-names.js - * and stored in the environment variable ICON_NAMES - * To add a new icon, add the icon svg file to app/images/icons - * Ensure the svg has been optimized, is kebab case and starts with "icon-" - * See "Adding a new icon" in ./README.md for more details + * + * Search for an icon: https://metamask.github.io/metamask-storybook/?path=/story/ui-components-component-library-icon-icon-stories-js--default-story + * + * Add an icon: https://metamask.github.io/metamask-storybook/?path=/docs/ui-components-component-library-icon-icon-stories-js--default-story#adding-a-new-icon + * + * ICON_NAMES is generated using svgs in app/images/icons and + * the generateIconNames script in development/generate-icon-names.js + * then stored as an environment variable */ /* eslint-disable prefer-destructuring*/ // process.env is not a standard JavaScript object, so we are not able to use object destructuring export const ICON_NAMES = JSON.parse(process.env.ICON_NAMES); +export const ICON_SIZES = { + XXS: SIZES.XXS, + XS: SIZES.XS, + SM: SIZES.SM, + MD: SIZES.MD, + LG: SIZES.LG, + XL: SIZES.XL, + AUTO: SIZES.AUTO, +}; diff --git a/ui/components/component-library/icon/icon.js b/ui/components/component-library/icon/icon.js index 8b18b183a1eb..37c2c83a0e26 100644 --- a/ui/components/component-library/icon/icon.js +++ b/ui/components/component-library/icon/icon.js @@ -10,6 +10,8 @@ import { ICON_COLORS, } from '../../../helpers/constants/design-system'; +import { ICON_SIZES } from './icon.constants'; + export const Icon = ({ name, size = SIZES.MD, @@ -21,15 +23,15 @@ export const Icon = ({ return ( ; - -DefaultStory.storyName = 'Default'; - -export const Name = (args) => { +export const DefaultStory = (args) => { const [search, setSearch] = useState(''); const iconList = Object.keys(ICON_NAMES) .filter( @@ -106,98 +113,134 @@ export const Name = (args) => { setSearch(e.target.value); }; + const handleOnClear = () => { + setSearch(''); + }; + return ( <> Icon search - - - - - {iconList.length > 0 ? ( - <> - {iconList.map((item) => { - return ( - - - - - { - const tempEl = document.createElement('textarea'); - tempEl.value = item; - document.body.appendChild(tempEl); - tempEl.select(); - document.execCommand('copy'); - document.body.removeChild(tempEl); - }} - > - {item} - - - ); - })} - - ) : ( - - No matches. Please try again or ask in the{' '} - - #metamask-design-system - {' '} - channel on slack. - - )} + + {/* TODO replace with FormTextField */} + + + + {iconList.length > 0 ? ( + + {iconList.map((item) => { + return ( + + + { + const tempEl = document.createElement('textarea'); + tempEl.value = item; + document.body.appendChild(tempEl); + tempEl.select(); + document.execCommand('copy'); + document.body.removeChild(tempEl); + }} + /> + } + /> + + ); + })} + + ) : ( + + No matches. Please try again or ask in the{' '} + + #metamask-design-system + {' '} + channel on slack. + + )} ); }; +DefaultStory.storyName = 'Default'; + +export const Name = (args) => ( + <> + + {Object.keys(ICON_NAMES).map((item) => { + console.log('item:', item); + return ( + + + + ); + })} + + +); export const Size = (args) => ( <> @@ -220,6 +263,7 @@ export const Size = (args) => ( ); + export const Color = (args) => ( diff --git a/ui/components/component-library/icon/icon.test.js b/ui/components/component-library/icon/icon.test.js index 510a1c87c934..d7922b89a1f2 100644 --- a/ui/components/component-library/icon/icon.test.js +++ b/ui/components/component-library/icon/icon.test.js @@ -12,6 +12,37 @@ describe('Icon', () => { ); expect(getByTestId('icon')).toBeDefined(); expect(container.querySelector('svg')).toBeDefined(); + expect(container).toMatchSnapshot(); + }); + it('should render with a custom class', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('icon')).toHaveClass('test-class'); + }); + it('should render with an aria-label attribute', () => { + /** + * We aren't specifically adding an ariaLabel prop because in most cases + * the icon should be decorative or be accompanied by text. Also if the icon + * is to be used as a button in most cases ButtonIcon should be used. However + * we should test if it's possible to pass an aria-label attribute to the + * root html element. + */ + const { getByTestId } = render( + , + ); + expect(getByTestId('icon')).toHaveAttribute( + 'aria-label', + 'test aria label', + ); }); it('should render with different icons using mask-image and image urls', () => { const { getByTestId } = render( @@ -33,16 +64,16 @@ describe('Icon', () => { ); expect( window.getComputedStyle(getByTestId('icon-add-square-filled')).maskImage, - ).toBe(`url('./images/icons/icon-add-square-filled.svg`); + ).toBe(`url('./images/icons/icon-add-square-filled.svg')`); expect( window.getComputedStyle(getByTestId('icon-bank-filled')).maskImage, - ).toBe(`url('./images/icons/icon-bank-filled.svg`); + ).toBe(`url('./images/icons/icon-bank-filled.svg')`); expect( window.getComputedStyle(getByTestId('icon-bookmark-filled')).maskImage, - ).toBe(`url('./images/icons/icon-bookmark-filled.svg`); + ).toBe(`url('./images/icons/icon-bookmark-filled.svg')`); expect( window.getComputedStyle(getByTestId('icon-calculator-filled')).maskImage, - ).toBe(`url('./images/icons/icon-calculator-filled.svg`); + ).toBe(`url('./images/icons/icon-calculator-filled.svg')`); }); it('should render with different size classes', () => { const { getByTestId } = render( @@ -84,13 +115,13 @@ describe('Icon', () => { /> , ); - expect(getByTestId('icon-xxs')).toHaveClass('icon--size-xxs'); - expect(getByTestId('icon-xs')).toHaveClass('icon--size-xs'); - expect(getByTestId('icon-sm')).toHaveClass('icon--size-sm'); - expect(getByTestId('icon-md')).toHaveClass('icon--size-md'); - expect(getByTestId('icon-lg')).toHaveClass('icon--size-lg'); - expect(getByTestId('icon-xl')).toHaveClass('icon--size-xl'); - expect(getByTestId('icon-auto')).toHaveClass('icon--size-auto'); + expect(getByTestId('icon-xxs')).toHaveClass('mm-icon--size-xxs'); + expect(getByTestId('icon-xs')).toHaveClass('mm-icon--size-xs'); + expect(getByTestId('icon-sm')).toHaveClass('mm-icon--size-sm'); + expect(getByTestId('icon-md')).toHaveClass('mm-icon--size-md'); + expect(getByTestId('icon-lg')).toHaveClass('mm-icon--size-lg'); + expect(getByTestId('icon-xl')).toHaveClass('mm-icon--size-xl'); + expect(getByTestId('icon-auto')).toHaveClass('mm-icon--size-auto'); }); it('should render with icon colors', () => { const { getByTestId } = render( diff --git a/ui/components/component-library/icon/index.js b/ui/components/component-library/icon/index.js index 97ac040b5ed8..270538cebd1d 100644 --- a/ui/components/component-library/icon/index.js +++ b/ui/components/component-library/icon/index.js @@ -1,2 +1,2 @@ export { Icon } from './icon'; -export { ICON_NAMES } from './icon.constants'; +export { ICON_NAMES, ICON_SIZES } from './icon.constants'; diff --git a/ui/components/component-library/index.js b/ui/components/component-library/index.js index 6927e233496d..d728e7423e8f 100644 --- a/ui/components/component-library/index.js +++ b/ui/components/component-library/index.js @@ -3,23 +3,25 @@ export { AvatarFavicon } from './avatar-favicon'; export { AvatarNetwork } from './avatar-network'; export { AvatarToken } from './avatar-token'; export { AvatarWithBadge } from './avatar-with-badge'; -export { BaseAvatar } from './base-avatar'; +export { AvatarBase } from './avatar-base'; export { Button } from './button'; export { ButtonBase } from './button-base'; export { ButtonIcon } from './button-icon'; export { ButtonLink } from './button-link'; export { ButtonPrimary } from './button-primary'; export { ButtonSecondary } from './button-secondary'; +export { FormTextField } from './form-text-field'; export { HelpText } from './help-text'; -export { Icon, ICON_NAMES } from './icon'; +export { Icon, ICON_NAMES, ICON_SIZES } from './icon'; export { Label } from './label'; export { PickerNetwork } from './picker-network'; export { Tag } from './tag'; export { TagUrl } from './tag-url'; export { Text } from './text'; -export { TextField } from './text-field'; +export { TextField, TEXT_FIELD_TYPES, TEXT_FIELD_SIZES } from './text-field'; export { TextFieldBase, TEXT_FIELD_BASE_SIZES, TEXT_FIELD_BASE_TYPES, } from './text-field-base'; +export { TextFieldSearch } from './text-field-search'; diff --git a/ui/components/component-library/picker-network/__snapshots__/picker-network.test.js.snap b/ui/components/component-library/picker-network/__snapshots__/picker-network.test.js.snap index b064e413c807..8abe2cbd5693 100644 --- a/ui/components/component-library/picker-network/__snapshots__/picker-network.test.js.snap +++ b/ui/components/component-library/picker-network/__snapshots__/picker-network.test.js.snap @@ -7,7 +7,7 @@ exports[`PickerNetwork should render the label inside the PickerNetwork 1`] = ` data-testid="picker-network" >
I
@@ -17,8 +17,8 @@ exports[`PickerNetwork should render the label inside the PickerNetwork 1`] = ` Imported

diff --git a/ui/components/component-library/tag-url/__snapshots__/tag-url.test.js.snap b/ui/components/component-library/tag-url/__snapshots__/tag-url.test.js.snap index 8c8865383e28..cb9ba9753d87 100644 --- a/ui/components/component-library/tag-url/__snapshots__/tag-url.test.js.snap +++ b/ui/components/component-library/tag-url/__snapshots__/tag-url.test.js.snap @@ -7,12 +7,12 @@ exports[`TagUrl should render the label inside the TagUrl 1`] = ` data-testid="tag-url" >

{ it('should render correctly', () => { const { getByRole } = render(); expect(getByRole('textbox')).toBeDefined(); }); it('should render and be able to input text', async () => { - const { user, getByRole } = setup(); + const { user, getByRole } = renderWithUserEvent(); const textField = getByRole('textbox'); await user.type(textField, 'text value'); expect(textField).toHaveValue('text value'); @@ -46,7 +39,7 @@ describe('TextField', () => { }); it('should render and fire onChange event', async () => { const onChange = jest.fn(); - const { user, getByRole } = setup( + const { user, getByRole } = renderWithUserEvent( { }); it('should render and fire onClick event', async () => { const onClick = jest.fn(); - const { user, getByTestId } = setup( + const { user, getByTestId } = renderWithUserEvent( , ); await user.click(getByTestId('text-field')); diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js index 508ca3e0b226..3bc90813d17b 100644 --- a/ui/helpers/utils/permission.js +++ b/ui/helpers/utils/permission.js @@ -74,6 +74,11 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({ leftIcon: 'fas fa-door-open', rightIcon: null, }, + [RestrictedMethods.snap_getEntropy]: { + label: (t) => t('permission_getEntropy'), + leftIcon: 'fas fa-key', + rightIcon: null, + }, [RestrictedMethods.snap_manageState]: { label: (t) => t('permission_manageState'), leftIcon: 'fas fa-download', @@ -107,6 +112,11 @@ const PERMISSION_DESCRIPTIONS = deepFreeze({ leftIcon: 'fas fa-clock', rightIcon: null, }, + [EndowmentPermissions['endowment:ethereum-provider']]: { + label: (t) => t('permission_ethereumProvider'), + leftIcon: 'fab fa-ethereum', + rightIcon: null, + }, ///: END:ONLY_INCLUDE_IN [UNKNOWN_PERMISSION]: { label: (t, permissionName) => diff --git a/ui/pages/permissions-connect/flask/snap-install/snap-install.js b/ui/pages/permissions-connect/flask/snap-install/snap-install.js index 6fc1170be260..f01d2543c28c 100644 --- a/ui/pages/permissions-connect/flask/snap-install/snap-install.js +++ b/ui/pages/permissions-connect/flask/snap-install/snap-install.js @@ -42,7 +42,7 @@ export default function SnapInstall({ request?.permissions && Object.keys(request.permissions).length > 0; const warnings = getSnapInstallWarnings( - request.permissions, + request?.permissions ?? {}, targetSubjectMetadata, t, ); diff --git a/ui/pages/permissions-connect/flask/util.js b/ui/pages/permissions-connect/flask/util.js index 3f7fbe4cf478..ddea93342fc0 100644 --- a/ui/pages/permissions-connect/flask/util.js +++ b/ui/pages/permissions-connect/flask/util.js @@ -1,4 +1,4 @@ -import { flatMap } from '@metamask/snap-utils'; +import { flatMap } from '@metamask/snaps-utils'; import { coinTypeToProtocolName } from '../../../helpers/utils/util'; export function getSnapInstallWarnings(permissions, targetSubjectMetadata, t) { diff --git a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js index 6c21c2e707c0..3e92838979db 100644 --- a/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js +++ b/ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js @@ -143,7 +143,7 @@ export default class AddContact extends PureComponent {

{ await addToAddressBook( domainResolution || this.state.ethAddress, diff --git a/yarn.lock b/yarn.lock index 528d719db0d0..8858753c310a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2559,6 +2559,13 @@ through2 "^4.0.2" umd "^3.0.3" +"@lavamoat/snow@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@lavamoat/snow/-/snow-1.2.1.tgz#87587e46940b26a966b665e62cd867d0cfbce47e" + integrity sha512-gC4oyvjNkFOOjhL9W4wP1YC0dcdQJPYKNW8fbwcP5iYogLL2fs1KIsHoF41WIhuWQ9mUdfg1jKBCTq41m+5VSw== + dependencies: + is-cross-origin "^1.0.1" + "@ledgerhq/cryptoassets@^5.27.2": version "5.53.0" resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-5.53.0.tgz#11dcc93211960c6fd6620392e4dd91896aaabe58" @@ -2779,6 +2786,11 @@ resolved "https://registry.yarnpkg.com/@metamask/browser-passworder/-/browser-passworder-3.0.0.tgz#c06744e66a968ffa13f70cc71a7d3b15d86b0ee7" integrity sha512-hD10mgvhcDkZX8wnauw8udp1K2MzcbZfrN7Yon9sQ+OqVK9kiQ4VhZAyZNZTy9KJLtfoVD9Y2F6L4gEese7hDA== +"@metamask/browser-passworder@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/browser-passworder/-/browser-passworder-4.0.1.tgz#bb2613bb1886774e18da4107ab94a21042e5e3da" + integrity sha512-mtekzCKph/S/15hRfWFDIUrpvz9mNoIU0CmH0SOlTHpLhalonEsZ+56MbQQUDshXbytzHp1eKr29OHrIx0u9iQ== + "@metamask/contract-metadata@^1.31.0", "@metamask/contract-metadata@^1.35.0": version "1.36.0" resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.36.0.tgz#8e277190195e9c26733752457d2004d149fd7e0e" @@ -2973,23 +2985,6 @@ resolved "https://registry.yarnpkg.com/@metamask/etherscan-link/-/etherscan-link-2.2.0.tgz#76314d0c1405a0669fc4a0a19e0877bd3d0c389f" integrity sha512-xUgehvgU+ZbzeJ44m4sUtsyf6Dwou+SlYhiKfi6lkRcbWh6Jl3TCi0YM9C7XWgxfnLSdQBO1ndvcp0kslKgMsA== -"@metamask/execution-environments@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@metamask/execution-environments/-/execution-environments-0.23.0.tgz#55b573ebc116d17b3b19cf15335c4acd97cabaff" - integrity sha512-OU0gEQ/oDMf19b7B7RZX3jWaZCAjDQA1+6cuGBKbxbFsgEYg3tHUuvn2KtH4Lah4SUGHAv3JZoTShX9j+wHGyg== - dependencies: - "@metamask/object-multiplex" "^1.2.0" - "@metamask/post-message-stream" "^6.0.0" - "@metamask/providers" "^9.0.0" - "@metamask/snap-types" "^0.23.0" - "@metamask/snap-utils" "^0.23.0" - "@metamask/utils" "^3.3.0" - eth-rpc-errors "^4.0.3" - pump "^3.0.0" - ses "^0.17.0" - stream-browserify "^3.0.0" - superstruct "^0.16.7" - "@metamask/forwarder@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@metamask/forwarder/-/forwarder-1.1.0.tgz#13829d8244bbf19ea658c0b20d21a77b67de0bdd" @@ -3082,10 +3077,10 @@ "@metamask/utils" "^2.0.0" readable-stream "2.3.3" -"@metamask/providers@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@metamask/providers/-/providers-10.2.1.tgz#61304940adeccc7421dcda30ffd1d834273cc77b" - integrity sha512-p2TXw2a1Nb8czntDGfeIYQnk4LLVbd5vlcb3GY//lylYlKdSqp+uUTegCvxiFblRDOT68jsY8Ib1VEEzVUOolA== +"@metamask/providers@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@metamask/providers/-/providers-10.2.0.tgz#8131de667db0c55a61a150438c2a7f17b2d53615" + integrity sha512-qO3cOZZr/YJ8LLOqhR+51GGBiRknalfV/na7hwXyqZ1R/uxeLeNdqCyg+g8l3Z8JcLoEiaKGNJOEV3FFyLw8mQ== dependencies: "@metamask/object-multiplex" "^1.1.0" "@metamask/safe-event-emitter" "^2.0.0" @@ -3096,14 +3091,14 @@ fast-deep-equal "^2.0.1" is-stream "^2.0.0" json-rpc-engine "^6.1.0" - json-rpc-middleware-stream "^4.2.1" + json-rpc-middleware-stream "^4.2.0" pump "^3.0.0" webextension-polyfill-ts "^0.25.0" -"@metamask/providers@^9.0.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@metamask/providers/-/providers-9.1.0.tgz#ccbbfd698eeb777c5c45aee91c3ad97e20eab20b" - integrity sha512-ZMfdIZ8PzaK1m0NblQOPTuDaMuTStSxrUYJiDNRi+UDqVd84WItQVXe3jy0k7TzhpjkbzgXrTK7rUcoQRhkwtw== +"@metamask/providers@^10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@metamask/providers/-/providers-10.2.1.tgz#61304940adeccc7421dcda30ffd1d834273cc77b" + integrity sha512-p2TXw2a1Nb8czntDGfeIYQnk4LLVbd5vlcb3GY//lylYlKdSqp+uUTegCvxiFblRDOT68jsY8Ib1VEEzVUOolA== dependencies: "@metamask/object-multiplex" "^1.1.0" "@metamask/safe-event-emitter" "^2.0.0" @@ -3114,20 +3109,21 @@ fast-deep-equal "^2.0.1" is-stream "^2.0.0" json-rpc-engine "^6.1.0" - json-rpc-middleware-stream "^3.0.0" + json-rpc-middleware-stream "^4.2.1" pump "^3.0.0" webextension-polyfill-ts "^0.25.0" -"@metamask/rpc-methods@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.23.0.tgz#51e1ebb91891d7227d346b229a6f112b67984c8e" - integrity sha512-jUsdkyOgOv3e7HMuLK/HNLVw431gguNAQf1vYRihV4+OdUZjV0sqVYliCkYuC5F4GPSlqnUK4JEDRI8e8rivsw== +"@metamask/rpc-methods@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.24.1.tgz#77bb9d3c0960a53b04aeec5e97967a7581a341f7" + integrity sha512-mUwN5Ya1F51p/yq81MAqUwR1D3R8CU1cLw3sKypfH0gVtwHNxXogp6Jeyv1VI/46Hrh4i9yXDztsRMOr+AjZEw== dependencies: "@metamask/controllers" "^32.0.2" "@metamask/key-tree" "^6.0.0" - "@metamask/snap-utils" "^0.23.0" + "@metamask/snaps-utils" "^0.24.1" "@metamask/types" "^1.1.0" - "@metamask/utils" "^3.3.0" + "@metamask/utils" "^3.3.1" + "@noble/hashes" "^1.1.3" eth-rpc-errors "^4.0.2" nanoid "^3.1.31" superstruct "^0.16.7" @@ -3157,20 +3153,20 @@ isomorphic-fetch "^3.0.0" lodash "^4.17.21" -"@metamask/snap-controllers@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@metamask/snap-controllers/-/snap-controllers-0.23.0.tgz#edcf3ec723c94899ec5b31ec54c6ed94be0b8551" - integrity sha512-TqPmlVDdbUu0gsYtoSHXxKQihXxhcRhPde7TnvrauouA13DAi1c/kiOSpCY8S14mMaLVSoc3WBm1t8YziG/cig== +"@metamask/snaps-controllers@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@metamask/snaps-controllers/-/snaps-controllers-0.24.1.tgz#602542b8b35e8faefcea4393589a82a5d3d8dfd8" + integrity sha512-RI9uZJipGHyh/+9joZVV6OC1bMI4h1XSfSJeJtNl121i0CZIsAIZQHWDfPToPfwKV2vVkpd3AEPrWIaHZ94hHg== dependencies: "@metamask/browser-passworder" "^3.0.0" "@metamask/controllers" "^32.0.2" - "@metamask/execution-environments" "^0.23.0" "@metamask/object-multiplex" "^1.1.0" "@metamask/post-message-stream" "^6.0.0" - "@metamask/rpc-methods" "^0.23.0" - "@metamask/snap-types" "^0.23.0" - "@metamask/snap-utils" "^0.23.0" - "@metamask/utils" "^3.3.0" + "@metamask/rpc-methods" "^0.24.1" + "@metamask/snaps-execution-environments" "^0.24.1" + "@metamask/snaps-types" "^0.24.1" + "@metamask/snaps-utils" "^0.24.1" + "@metamask/utils" "^3.3.1" "@xstate/fsm" "^2.0.0" concat-stream "^2.0.0" cron-parser "^4.5.0" @@ -3178,30 +3174,48 @@ gunzip-maybe "^1.4.2" immer "^9.0.6" json-rpc-engine "^6.1.0" - json-rpc-middleware-stream "^3.0.0" + json-rpc-middleware-stream "^4.2.0" nanoid "^3.1.31" pump "^3.0.0" readable-web-to-node-stream "^3.0.2" tar-stream "^2.2.0" -"@metamask/snap-types@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@metamask/snap-types/-/snap-types-0.23.0.tgz#bcd491a100b2410cb91bbecc59007a278fa508d5" - integrity sha512-l5QK9XKw5aGEn8ohofyopTQndtlrVzYjXyZik3RhxZccRcGA4rp7juvTuIoyBE/0fiqTc30BHCmKtVvkaTyd9Q== +"@metamask/snaps-execution-environments@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@metamask/snaps-execution-environments/-/snaps-execution-environments-0.24.1.tgz#fd2765b0ca24670c245896330a85e2c1b57b2997" + integrity sha512-pMsKM3htIXmdeRKkXrl4ktiRrXojo3oxCqv3GhIhY0Tbm+nuJM4x105utLEi62LFeuAFvIVFB8uU1dnjtl6bhg== + dependencies: + "@metamask/object-multiplex" "^1.2.0" + "@metamask/post-message-stream" "^6.0.0" + "@metamask/providers" "^10.2.0" + "@metamask/snaps-types" "^0.24.1" + "@metamask/snaps-utils" "^0.24.1" + "@metamask/utils" "^3.3.1" + eth-rpc-errors "^4.0.3" + json-rpc-engine "^6.1.0" + pump "^3.0.0" + ses "^0.17.0" + stream-browserify "^3.0.0" + superstruct "^0.16.7" + +"@metamask/snaps-types@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@metamask/snaps-types/-/snaps-types-0.24.1.tgz#f315321f954611a7bdb514cf786a9c008897a3b6" + integrity sha512-Kt6pacC+nEISJm2BSa9Gu9HvYApIk03THCl6pmG8/HQr5g6r+VXRtQ56nQw7pHAiyV6eKlSITl/rFY+dYuJg6Q== dependencies: - "@metamask/providers" "^9.0.0" - "@metamask/snap-utils" "^0.23.0" + "@metamask/providers" "^10.2.0" + "@metamask/snaps-utils" "^0.24.1" "@metamask/types" "^1.1.0" -"@metamask/snap-utils@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@metamask/snap-utils/-/snap-utils-0.23.0.tgz#9a116a2361910ade229749e1f65f8312567faa01" - integrity sha512-S0/D+OZ70tvpyMIBIJJzZp/0X1SPVfofbGU3wQCG0OX7TWMpLsnQnBZfRyK7Jw+ZC+j7fyYMUyXEpvimYfHQBA== +"@metamask/snaps-utils@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@metamask/snaps-utils/-/snaps-utils-0.24.1.tgz#b95678828a739a5cf0c4e25c0843094931d3a0c7" + integrity sha512-W0wtDcvZd/y6MGyPWKnOBQ4ORpUTSCy8cwuxS0NsMuV1FhLgl9RlTmNWUlUS+eEGkUiQxdX8l+W34Cfe4ZZnuA== dependencies: "@babel/core" "^7.18.6" "@babel/types" "^7.18.7" - "@metamask/snap-types" "^0.23.0" - "@metamask/utils" "^3.3.0" + "@metamask/snaps-types" "^0.24.1" + "@metamask/utils" "^3.3.1" "@noble/hashes" "^1.1.3" "@scure/base" "^1.1.1" cron-parser "^4.5.0" @@ -3229,10 +3243,10 @@ dependencies: fast-deep-equal "^3.1.3" -"@metamask/utils@^3.0.1", "@metamask/utils@^3.0.3", "@metamask/utils@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-3.3.0.tgz#e5e7885c72a30f50f9e2b29690f543da1d6ab496" - integrity sha512-GT8jMTCiGl3z9L1lvALjgW/6urJsl5Cwnix4C65NzJInF0cK2GxqpLkEMQJ50Mdky2qc2P7+F5++d4utvx2TtA== +"@metamask/utils@^3.0.1", "@metamask/utils@^3.0.3", "@metamask/utils@^3.3.0", "@metamask/utils@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-3.3.1.tgz#04a00a24469e3eb03bde111432053c05afb45326" + integrity sha512-r65Swl91wQ2YDkEQXZah1l7it0iBJK+trTeX9uPHplLQ0lzWZ/yODbEMFZVrStRQxDU8RARXryDyfUX5CLVvLA== dependencies: "@types/debug" "^4.1.7" debug "^4.3.4" @@ -11196,6 +11210,18 @@ eth-keyring-controller@^7.0.2: eth-simple-keyring "^4.2.0" obs-store "^4.0.3" +eth-keyring-controller@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eth-keyring-controller/-/eth-keyring-controller-8.0.1.tgz#02bddda96251aadc739179802ab3dd2f9415d12a" + integrity sha512-6LDnvOIxbA+P6lAAAzde4Kb6hyB70Zs8gPp/enuDrvvFO79LCWGCzP0rre7OQC+agwFsU0gSHJXkRZK8llgPSg== + dependencies: + "@metamask/bip39" "^4.0.0" + "@metamask/browser-passworder" "^4.0.1" + "@metamask/eth-hd-keyring" "^4.0.2" + eth-sig-util "^3.0.1" + eth-simple-keyring "^4.2.0" + obs-store "^4.0.3" + eth-lattice-keyring@^0.12.3: version "0.12.3" resolved "https://registry.yarnpkg.com/eth-lattice-keyring/-/eth-lattice-keyring-0.12.3.tgz#32ee3db427d15f1d76f907a36ddc34f4aab205ba" @@ -14492,6 +14518,13 @@ is-core-module@^2.4.0, is-core-module@^2.8.1, is-core-module@^2.9.0: dependencies: has "^1.0.3" +is-cross-origin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-cross-origin/-/is-cross-origin-1.0.1.tgz#b73ee0bbcf9f640a4c4f17bcf3cc229e3dae11e8" + integrity sha512-l0VqOliZJ0tJTFkNGa1kJilL9q+wCKw2wLX6qoXVtWUFKiqXvLaf4kAu4+9xAg6QJGKc41CDaFYSxmO9Iw1vuw== + dependencies: + is-window "^1.0.2" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -15881,10 +15914,10 @@ json-rpc-middleware-stream@^2.1.1: readable-stream "^2.3.3" safe-event-emitter "^1.0.1" -json-rpc-middleware-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-3.0.0.tgz#8540331d884f36b9e0ad31054cc68ac6b5a89b52" - integrity sha512-JmZmlehE0xF3swwORpLHny/GvW3MZxCsb2uFNBrn8TOqMqivzCfz232NSDLLOtIQlrPlgyEjiYpyzyOPFOzClw== +json-rpc-middleware-stream@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-4.2.0.tgz#a235814e031a2f85cc14b213aa7d42b75527104b" + integrity sha512-vaPaFVnhozfAVx6ImO3YuSrzl6A1OCktDi9Prw6NASn2VRYfe3pzv+2QGzFHRjViztr61oHi6KC3k8cujrfK7A== dependencies: "@metamask/safe-event-emitter" "^2.0.0" readable-stream "^2.3.3"