From a166147abab910c4b294ea93239ca5f2b284c533 Mon Sep 17 00:00:00 2001
From: George Weiler
Date: Thu, 27 Jun 2024 10:16:34 -0600
Subject: [PATCH 01/15] feat(ramps): introduces dynamic support for rampable
networks (#24041)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
This PR introduces a new reducer for Ramps to store an array of
"buyable" networks. A "buyable chain" is one that the native token has
onramp support for. This BUYABLE_CHAINS_MAP list is currently
hard-coded, and this PR fetches a dynamic network list from the Ramps
API instead. The list of supported networks by the MetMask onramp team
is dynamic and is based on provider support among other things.
There are several fallback protections in place. Buyable chains will
default to the current hard-coded list before loading and will default
to that same list if there are any errors. There is no need for loading
or error states.
The ramps base API url has been added as a new environment variable,
defaulted to production. example:
METAMASK_RAMP_API_BASE_URL=https://on-ramp-content.metaswap.codefi.network
Here's screenshot to show the issue this PR will fix. Base network is
supported as a buyable network. but because the hard-coded array of
networks does not include base we do not enable the buy CTAs:

[](https://codespaces.new/MetaMask/metamask-extension/pull/24041?quickstart=1)
## **Related issues**
Fixes:
## **Manual testing steps**
1. Go to this page...
2.
3.
## **Screenshots/Recordings**
**Video Description of the changes in the PR (5 min)**
https://www.loom.com/share/973960816e7e497aae51ed1cdc3cebf5
### **Before**
### **After**
## **Pre-merge author checklist**
- [x] I’ve followed [MetaMask Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
---------
Co-authored-by: Nicolas Ferro
Co-authored-by: Pedro Pablo Aste Kompen
---
builds.yml | 2 +
privacy-snapshot.json | 2 +
shared/constants/network.ts | 118 -----------
test/data/mock-send-state.json | 137 +++++++++++++
test/data/mock-state.json | 137 +++++++++++++
.../errors-after-init-opt-in-ui-state.json | 1 +
.../app/add-network/add-network.test.js | 1 +
ui/components/app/asset-list/asset-list.js | 6 +-
ui/components/app/nfts-tab/nfts-tab.js | 4 +-
.../selected-account-component.test.js | 1 +
.../transaction-list.component.js | 5 +-
.../app/wallet-overview/coin-buttons.tsx | 3 +-
.../app/wallet-overview/eth-overview.js | 7 +-
.../app/wallet-overview/eth-overview.test.js | 26 +--
.../multichain/ramps-card/ramps-card.js | 2 +-
ui/ducks/index.js | 2 +
ui/ducks/ramps/constants.ts | 137 +++++++++++++
ui/ducks/ramps/index.ts | 1 +
ui/ducks/ramps/ramps.test.ts | 187 ++++++++++++++++++
ui/ducks/ramps/ramps.ts | 78 ++++++++
ui/ducks/ramps/types.ts | 7 +
ui/helpers/ramps/rampApi/rampAPI.test.ts | 23 +++
ui/helpers/ramps/rampApi/rampAPI.ts | 25 +++
.../useRamps/useRamps.test.tsx} | 23 ++-
.../useRamps}/useRamps.ts | 4 +-
ui/hooks/useTheme.test.ts | 1 +
ui/pages/asset/components/asset-page.tsx | 4 +-
ui/pages/asset/components/token-buttons.tsx | 10 +-
.../confirm-page-container.component.js | 10 +-
.../confirm-page-container.container.js | 3 -
.../confirm-transaction-base.container.js | 4 +-
.../confirm-transaction-base.test.js | 5 +
.../hooks/useConfirmationAlertActions.ts | 2 +-
.../hooks/useTransactionFunction.test.js | 1 +
.../send/gas-display/gas-display.js | 6 +-
ui/pages/home/home.component.js | 5 +-
ui/pages/home/home.container.js | 2 +
ui/pages/routes/routes.component.test.js | 6 +-
.../swaps/prepare-swap-page/review-quote.js | 2 +-
ui/selectors/selectors.js | 6 -
40 files changed, 812 insertions(+), 194 deletions(-)
create mode 100644 ui/ducks/ramps/constants.ts
create mode 100644 ui/ducks/ramps/index.ts
create mode 100644 ui/ducks/ramps/ramps.test.ts
create mode 100644 ui/ducks/ramps/ramps.ts
create mode 100644 ui/ducks/ramps/types.ts
create mode 100644 ui/helpers/ramps/rampApi/rampAPI.test.ts
create mode 100644 ui/helpers/ramps/rampApi/rampAPI.ts
rename ui/hooks/{experiences/useRamps.test.js => ramps/useRamps/useRamps.test.tsx} (85%)
rename ui/hooks/{experiences => ramps/useRamps}/useRamps.ts (90%)
diff --git a/builds.yml b/builds.yml
index 2e449c687591..aa93f756db49 100644
--- a/builds.yml
+++ b/builds.yml
@@ -266,6 +266,8 @@ env:
# Enables the notifications feature within the build:
- NOTIFICATIONS: ''
+ - METAMASK_RAMP_API_CONTENT_BASE_URL: https://on-ramp-content.api.cx.metamask.io
+
###
# Meta variables
###
diff --git a/privacy-snapshot.json b/privacy-snapshot.json
index b45cf79a6e8f..fe6579bfab73 100644
--- a/privacy-snapshot.json
+++ b/privacy-snapshot.json
@@ -30,6 +30,8 @@
"phishing-detection.api.cx.metamask.io",
"portfolio.metamask.io",
"price.api.cx.metamask.io",
+ "on-ramp-content.api.cx.metamask.io",
+ "on-ramp-content.uat-api.cx.metamask.io",
"proxy.api.cx.metamask.io",
"raw.githubusercontent.com",
"registry.npmjs.org",
diff --git a/shared/constants/network.ts b/shared/constants/network.ts
index 452f0584ffaa..7754b2a0f16f 100644
--- a/shared/constants/network.ts
+++ b/shared/constants/network.ts
@@ -66,22 +66,6 @@ export type RPCDefinition = {
rpcPrefs: RPCPreferences;
};
-/**
- * For each chain that we support fiat onramps for, we provide a set of
- * configuration options that help for initializing the connectiong to the
- * onramp providers.
- */
-type BuyableChainSettings = {
- /**
- * The native currency for the given chain
- */
- nativeCurrency: CurrencySymbol | TestNetworkCurrencySymbol;
- /**
- * The network name or identifier
- */
- network: string;
-};
-
/**
* Throughout the extension we set the current provider by referencing its
* "type", which can be any of the values in the below object. These values
@@ -908,108 +892,6 @@ export const UNSUPPORTED_RPC_METHODS = new Set([
export const IPFS_DEFAULT_GATEWAY_URL = 'dweb.link';
-// The first item in transakCurrencies must be the
-// default crypto currency for the network
-const BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME = 'ethereum';
-
-export const BUYABLE_CHAINS_MAP: {
- [K in Exclude<
- ChainId,
- | typeof CHAIN_IDS.LOCALHOST
- | typeof CHAIN_IDS.OPTIMISM_TESTNET
- | typeof CHAIN_IDS.OPTIMISM_GOERLI
- | typeof CHAIN_IDS.BASE_TESTNET
- | typeof CHAIN_IDS.OPBNB_TESTNET
- | typeof CHAIN_IDS.OPBNB
- | typeof CHAIN_IDS.BSC_TESTNET
- | typeof CHAIN_IDS.POLYGON_TESTNET
- | typeof CHAIN_IDS.AVALANCHE_TESTNET
- | typeof CHAIN_IDS.FANTOM_TESTNET
- | typeof CHAIN_IDS.MOONBEAM_TESTNET
- | typeof CHAIN_IDS.LINEA_GOERLI
- | typeof CHAIN_IDS.LINEA_SEPOLIA
- | typeof CHAIN_IDS.GOERLI
- | typeof CHAIN_IDS.SEPOLIA
- | typeof CHAIN_IDS.GNOSIS
- | typeof CHAIN_IDS.AURORA
- | typeof CHAIN_IDS.ARBITRUM_GOERLI
- | typeof CHAIN_IDS.BLAST
- | typeof CHAIN_IDS.FILECOIN
- | typeof CHAIN_IDS.POLYGON_ZKEVM
- | typeof CHAIN_IDS.SCROLL
- | typeof CHAIN_IDS.SCROLL_SEPOLIA
- | typeof CHAIN_IDS.WETHIO
- | typeof CHAIN_IDS.CHZ
- | typeof CHAIN_IDS.NUMBERS
- | typeof CHAIN_IDS.SEI
- >]: BuyableChainSettings;
-} = {
- [CHAIN_IDS.MAINNET]: {
- nativeCurrency: CURRENCY_SYMBOLS.ETH,
- network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
- },
- [CHAIN_IDS.BSC]: {
- nativeCurrency: CURRENCY_SYMBOLS.BNB,
- network: 'bsc',
- },
- [CHAIN_IDS.POLYGON]: {
- nativeCurrency: CURRENCY_SYMBOLS.MATIC,
- network: 'polygon',
- },
- [CHAIN_IDS.AVALANCHE]: {
- nativeCurrency: CURRENCY_SYMBOLS.AVALANCHE,
- network: 'avaxcchain',
- },
- [CHAIN_IDS.FANTOM]: {
- nativeCurrency: CURRENCY_SYMBOLS.FANTOM,
- network: 'fantom',
- },
- [CHAIN_IDS.CELO]: {
- nativeCurrency: CURRENCY_SYMBOLS.CELO,
- network: 'celo',
- },
- [CHAIN_IDS.OPTIMISM]: {
- nativeCurrency: CURRENCY_SYMBOLS.ETH,
- network: 'optimism',
- },
- [CHAIN_IDS.ARBITRUM]: {
- nativeCurrency: CURRENCY_SYMBOLS.ARBITRUM,
- network: 'arbitrum',
- },
- [CHAIN_IDS.CRONOS]: {
- nativeCurrency: CURRENCY_SYMBOLS.CRONOS,
- network: 'cronos',
- },
- [CHAIN_IDS.MOONBEAM]: {
- nativeCurrency: CURRENCY_SYMBOLS.GLIMMER,
- network: 'moonbeam',
- },
- [CHAIN_IDS.MOONRIVER]: {
- nativeCurrency: CURRENCY_SYMBOLS.MOONRIVER,
- network: 'moonriver',
- },
- [CHAIN_IDS.HARMONY]: {
- nativeCurrency: CURRENCY_SYMBOLS.ONE,
- network: 'harmony',
- },
- [CHAIN_IDS.PALM]: {
- nativeCurrency: CURRENCY_SYMBOLS.PALM,
- network: 'palm',
- },
- [CHAIN_IDS.LINEA_MAINNET]: {
- nativeCurrency: CURRENCY_SYMBOLS.ETH,
- network: 'linea',
- },
- [CHAIN_IDS.ZKSYNC_ERA]: {
- nativeCurrency: CURRENCY_SYMBOLS.ETH,
- network: 'zksync',
- },
- [CHAIN_IDS.BASE]: {
- nativeCurrency: CURRENCY_SYMBOLS.ETH,
- network: 'base',
- },
-};
-
export const FEATURED_RPCS: RPCDefinition[] = [
{
chainId: CHAIN_IDS.ARBITRUM,
diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json
index c8604c96c055..5beee30824ce 100644
--- a/test/data/mock-send-state.json
+++ b/test/data/mock-send-state.json
@@ -1242,6 +1242,143 @@
],
"swapsState": {}
},
+ "ramps": {
+ "buyableChains": [
+ {
+ "active": true,
+ "chainId": 1,
+ "chainName": "Ethereum Mainnet",
+ "shortName": "Ethereum",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 10,
+ "chainName": "Optimism Mainnet",
+ "shortName": "Optimism",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 25,
+ "chainName": "Cronos Mainnet",
+ "shortName": "Cronos",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 56,
+ "chainName": "BNB Chain Mainnet",
+ "shortName": "BNB Chain",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 100,
+ "chainName": "Gnosis Mainnet",
+ "shortName": "Gnosis",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 137,
+ "chainName": "Polygon Mainnet",
+ "shortName": "Polygon",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 250,
+ "chainName": "Fantom Mainnet",
+ "shortName": "Fantom",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 324,
+ "chainName": "zkSync Era Mainnet",
+ "shortName": "zkSync Era",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1101,
+ "chainName": "Polygon zkEVM",
+ "shortName": "Polygon zkEVM",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1284,
+ "chainName": "Moonbeam Mainnet",
+ "shortName": "Moonbeam",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1285,
+ "chainName": "Moonriver Mainnet",
+ "shortName": "Moonriver",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 8453,
+ "chainName": "Base Mainnet",
+ "shortName": "Base",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 42161,
+ "chainName": "Arbitrum Mainnet",
+ "shortName": "Arbitrum",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 42220,
+ "chainName": "Celo Mainnet",
+ "shortName": "Celo",
+ "nativeTokenSupported": false
+ },
+ {
+ "active": true,
+ "chainId": 43114,
+ "chainName": "Avalanche C-Chain Mainnet",
+ "shortName": "Avalanche C-Chain",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 59144,
+ "chainName": "Linea",
+ "shortName": "Linea",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1313161554,
+ "chainName": "Aurora Mainnet",
+ "shortName": "Aurora",
+ "nativeTokenSupported": false
+ },
+ {
+ "active": true,
+ "chainId": 1666600000,
+ "chainName": "Harmony Mainnet (Shard 0)",
+ "shortName": "Harmony (Shard 0)",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 11297108109,
+ "chainName": "Palm Mainnet",
+ "shortName": "Palm",
+ "nativeTokenSupported": false
+ }
+ ]
+ },
"send": {
"amountMode": "INPUT",
"currentTransactionUUID": "1-tx",
diff --git a/test/data/mock-state.json b/test/data/mock-state.json
index 920533023a58..9e595ad8f0e0 100644
--- a/test/data/mock-state.json
+++ b/test/data/mock-state.json
@@ -1943,6 +1943,143 @@
}
}
},
+ "ramps": {
+ "buyableChains": [
+ {
+ "active": true,
+ "chainId": 1,
+ "chainName": "Ethereum Mainnet",
+ "shortName": "Ethereum",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 10,
+ "chainName": "Optimism Mainnet",
+ "shortName": "Optimism",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 25,
+ "chainName": "Cronos Mainnet",
+ "shortName": "Cronos",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 56,
+ "chainName": "BNB Chain Mainnet",
+ "shortName": "BNB Chain",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 100,
+ "chainName": "Gnosis Mainnet",
+ "shortName": "Gnosis",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 137,
+ "chainName": "Polygon Mainnet",
+ "shortName": "Polygon",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 250,
+ "chainName": "Fantom Mainnet",
+ "shortName": "Fantom",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 324,
+ "chainName": "zkSync Era Mainnet",
+ "shortName": "zkSync Era",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1101,
+ "chainName": "Polygon zkEVM",
+ "shortName": "Polygon zkEVM",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1284,
+ "chainName": "Moonbeam Mainnet",
+ "shortName": "Moonbeam",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1285,
+ "chainName": "Moonriver Mainnet",
+ "shortName": "Moonriver",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 8453,
+ "chainName": "Base Mainnet",
+ "shortName": "Base",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 42161,
+ "chainName": "Arbitrum Mainnet",
+ "shortName": "Arbitrum",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 42220,
+ "chainName": "Celo Mainnet",
+ "shortName": "Celo",
+ "nativeTokenSupported": false
+ },
+ {
+ "active": true,
+ "chainId": 43114,
+ "chainName": "Avalanche C-Chain Mainnet",
+ "shortName": "Avalanche C-Chain",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 59144,
+ "chainName": "Linea",
+ "shortName": "Linea",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 1313161554,
+ "chainName": "Aurora Mainnet",
+ "shortName": "Aurora",
+ "nativeTokenSupported": false
+ },
+ {
+ "active": true,
+ "chainId": 1666600000,
+ "chainName": "Harmony Mainnet (Shard 0)",
+ "shortName": "Harmony (Shard 0)",
+ "nativeTokenSupported": true
+ },
+ {
+ "active": true,
+ "chainId": 11297108109,
+ "chainName": "Palm Mainnet",
+ "shortName": "Palm",
+ "nativeTokenSupported": false
+ }
+ ]
+ },
"send": {
"amountMode": "INPUT",
"currentTransactionUUID": null,
diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json
index 1dd7775ec70f..4b7972acd311 100644
--- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json
+++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json
@@ -257,6 +257,7 @@
"encryptionKey": "string",
"encryptionSalt": "string"
},
+ "ramps": "object",
"send": "object",
"swaps": "object",
"unconnectedAccount": { "state": "CLOSED" }
diff --git a/ui/components/app/add-network/add-network.test.js b/ui/components/app/add-network/add-network.test.js
index 3a15f4d33c3e..f3084b1db065 100644
--- a/ui/components/app/add-network/add-network.test.js
+++ b/ui/components/app/add-network/add-network.test.js
@@ -6,6 +6,7 @@ import mockState from '../../../../test/data/mock-state.json';
import AddNetwork from './add-network';
jest.mock('../../../selectors', () => ({
+ ...jest.requireActual('../../../selectors'),
getNetworkConfigurations: () => ({
networkConfigurationId: {
chainId: '0x539',
diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js
index 82c45a27134e..959789b8eebc 100644
--- a/ui/components/app/asset-list/asset-list.js
+++ b/ui/components/app/asset-list/asset-list.js
@@ -9,9 +9,6 @@ import {
getDetectedTokensInCurrentNetwork,
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
getShouldHideZeroBalanceTokens,
- ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
- getIsBuyableChain,
- ///: END:ONLY_INCLUDE_IF
getSelectedAccount,
getPreferences,
} from '../../../selectors';
@@ -48,6 +45,7 @@ import {
RAMPS_CARD_VARIANT_TYPES,
RampsCard,
} from '../../multichain/ramps-card/ramps-card';
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
///: END:ONLY_INCLUDE_IF
const AssetList = ({ onClickAsset }) => {
@@ -109,7 +107,7 @@ const AssetList = ({ onClickAsset }) => {
});
const balanceIsZero = Number(totalFiatBalance) === 0;
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const shouldShowBuy = isBuyableChain && balanceIsZero;
///: END:ONLY_INCLUDE_IF
diff --git a/ui/components/app/nfts-tab/nfts-tab.js b/ui/components/app/nfts-tab/nfts-tab.js
index bc54ca4c9766..43a0bd1a9d64 100644
--- a/ui/components/app/nfts-tab/nfts-tab.js
+++ b/ui/components/app/nfts-tab/nfts-tab.js
@@ -17,7 +17,6 @@ import { useNftsCollections } from '../../../hooks/useNftsCollections';
import {
getCurrentNetwork,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
- getIsBuyableChain,
getShouldHideZeroBalanceTokens,
getSelectedAccount,
///: END:ONLY_INCLUDE_IF
@@ -49,6 +48,7 @@ import {
RampsCard,
} from '../../multichain/ramps-card/ramps-card';
import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance';
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
///: END:ONLY_INCLUDE_IF
import Spinner from '../../ui/spinner';
@@ -73,7 +73,7 @@ export default function NftsTab() {
shouldHideZeroBalanceTokens,
);
const balanceIsZero = Number(totalFiatBalance) === 0;
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const showRampsCard = isBuyableChain && balanceIsZero;
///: END:ONLY_INCLUDE_IF
diff --git a/ui/components/app/selected-account/selected-account-component.test.js b/ui/components/app/selected-account/selected-account-component.test.js
index a48e24a661c6..ae8a3ff000db 100644
--- a/ui/components/app/selected-account/selected-account-component.test.js
+++ b/ui/components/app/selected-account/selected-account-component.test.js
@@ -52,6 +52,7 @@ jest.mock('../../../selectors', () => {
return {
getAccountType: mockGetAccountType,
getSelectedInternalAccount: mockGetSelectedAccount,
+ getCurrentChainId: jest.fn(() => '0x1'),
};
});
diff --git a/ui/components/app/transaction-list/transaction-list.component.js b/ui/components/app/transaction-list/transaction-list.component.js
index b47130c5d739..20c41ef33e4d 100644
--- a/ui/components/app/transaction-list/transaction-list.component.js
+++ b/ui/components/app/transaction-list/transaction-list.component.js
@@ -10,7 +10,6 @@ import {
getCurrentChainId,
getSelectedAccount,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
- getIsBuyableChain,
getShouldHideZeroBalanceTokens,
///: END:ONLY_INCLUDE_IF
} from '../../../selectors';
@@ -33,6 +32,7 @@ import {
RAMPS_CARD_VARIANT_TYPES,
RampsCard,
} from '../../multichain/ramps-card/ramps-card';
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
///: END:ONLY_INCLUDE_IF
const PAGE_INCREMENT = 10;
@@ -141,8 +141,7 @@ export default function TransactionList({
shouldHideZeroBalanceTokens,
);
const balanceIsZero = Number(totalFiatBalance) === 0;
- const isBuyableChain = useSelector(getIsBuyableChain);
-
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const showRampsCard = isBuyableChain && balanceIsZero;
///: END:ONLY_INCLUDE_IF
diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx
index 5bba897121e4..8eb24128b60e 100644
--- a/ui/components/app/wallet-overview/coin-buttons.tsx
+++ b/ui/components/app/wallet-overview/coin-buttons.tsx
@@ -55,7 +55,8 @@ import { Box, Icon, IconName } from '../../component-library';
import IconButton from '../../ui/icon-button';
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
import { getPortfolioUrl } from '../../../helpers/utils/portfolio';
-import useRamps from '../../../hooks/experiences/useRamps';
+import useRamps from '../../../hooks/ramps/useRamps/useRamps';
+
///: END:ONLY_INCLUDE_IF
const CoinButtons = ({
diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js
index 5eeec7a59cbf..684c6cffbf6b 100644
--- a/ui/components/app/wallet-overview/eth-overview.js
+++ b/ui/components/app/wallet-overview/eth-overview.js
@@ -1,7 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
-
import { EthMethod } from '@metamask/keyring-api';
import { isEqual } from 'lodash';
import {
@@ -13,15 +12,17 @@ import {
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
getSwapsDefaultToken,
getIsBridgeChain,
- getIsBuyableChain,
///: END:ONLY_INCLUDE_IF
} from '../../../selectors';
+///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
+///: END:ONLY_INCLUDE_IF
import { CoinOverview } from './coin-overview';
const EthOverview = ({ className }) => {
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const isBridgeChain = useSelector(getIsBridgeChain);
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
// FIXME: This causes re-renders, so use isEqual to avoid this
const defaultSwapsToken = useSelector(getSwapsDefaultToken, isEqual);
///: END:ONLY_INCLUDE_IF
diff --git a/ui/components/app/wallet-overview/eth-overview.test.js b/ui/components/app/wallet-overview/eth-overview.test.js
index 0d13bff7e1ef..0d079a32f104 100644
--- a/ui/components/app/wallet-overview/eth-overview.test.js
+++ b/ui/components/app/wallet-overview/eth-overview.test.js
@@ -12,27 +12,11 @@ import {
import { renderWithProvider } from '../../../../test/jest/rendering';
import { KeyringType } from '../../../../shared/constants/keyring';
import { useIsOriginalNativeTokenSymbol } from '../../../hooks/useIsOriginalNativeTokenSymbol';
+import { defaultBuyableChains } from '../../../ducks/ramps/constants';
import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods';
import { getIntlLocale } from '../../../ducks/locale/locale';
import EthOverview from './eth-overview';
-// Mock BUYABLE_CHAINS_MAP
-jest.mock('../../../../shared/constants/network', () => ({
- ...jest.requireActual('../../../../shared/constants/network'),
- BUYABLE_CHAINS_MAP: {
- // MAINNET
- '0x1': {
- nativeCurrency: 'ETH',
- network: 'ethereum',
- },
- // POLYGON
- '0x89': {
- nativeCurrency: 'MATIC',
- network: 'polygon',
- },
- },
-}));
-
jest.mock('../../../hooks/useIsOriginalNativeTokenSymbol', () => {
return {
useIsOriginalNativeTokenSymbol: jest.fn(),
@@ -138,6 +122,9 @@ describe('EthOverview', () => {
},
],
},
+ ramps: {
+ buyableChains: defaultBuyableChains,
+ },
};
const store = configureMockStore([thunk])(mockStore);
@@ -181,6 +168,7 @@ describe('EthOverview', () => {
it('should show the cached primary balance', async () => {
const mockedStoreWithCachedBalance = {
+ ...mockStore,
metamask: {
...mockStore.metamask,
accounts: {
@@ -267,6 +255,7 @@ describe('EthOverview', () => {
it('should open the MMI PD Swaps URI when clicking on Swap button with a Custody account', async () => {
const mockedStoreWithCustodyKeyring = {
+ ...mockStore,
metamask: {
...mockStore.metamask,
mmiConfiguration: {
@@ -375,6 +364,7 @@ describe('EthOverview', () => {
it('should have the Buy native token button disabled if chain id is not part of supported buyable chains', () => {
const mockedStoreWithUnbuyableChainId = {
+ ...mockStore,
metamask: {
...mockStore.metamask,
providerConfig: {
@@ -399,6 +389,7 @@ describe('EthOverview', () => {
it('should have the Buy native token enabled if chain id is part of supported buyable chains', () => {
const mockedStoreWithUnbuyableChainId = {
+ ...mockStore,
metamask: {
...mockStore.metamask,
providerConfig: {
@@ -432,6 +423,7 @@ describe('EthOverview', () => {
it('should open the Buy native token URI when clicking on Buy button for a buyable chain ID', async () => {
const mockedStoreWithBuyableChainId = {
+ ...mockStore,
metamask: {
...mockStore.metamask,
providerConfig: {
diff --git a/ui/components/multichain/ramps-card/ramps-card.js b/ui/components/multichain/ramps-card/ramps-card.js
index e1c61f2a35c1..2fa793b7e958 100644
--- a/ui/components/multichain/ramps-card/ramps-card.js
+++ b/ui/components/multichain/ramps-card/ramps-card.js
@@ -21,7 +21,7 @@ import {
import { MetaMetricsContext } from '../../../contexts/metametrics';
import useRamps, {
RampsMetaMaskEntry,
-} from '../../../hooks/experiences/useRamps';
+} from '../../../hooks/ramps/useRamps/useRamps';
import { ORIGIN_METAMASK } from '../../../../shared/constants/app';
import { getCurrentLocale } from '../../../ducks/locale/locale';
diff --git a/ui/ducks/index.js b/ui/ducks/index.js
index 145dd708cb90..f72918460655 100644
--- a/ui/ducks/index.js
+++ b/ui/ducks/index.js
@@ -11,6 +11,7 @@ import gasReducer from './gas/gas.duck';
import { invalidCustomNetwork, unconnectedAccount } from './alerts';
import swapsReducer from './swaps/swaps';
import historyReducer from './history/history';
+import rampsReducer from './ramps/ramps';
import confirmAlertsReducer from './confirm-alerts/confirm-alerts';
export default combineReducers({
@@ -26,6 +27,7 @@ export default combineReducers({
confirmAlerts: confirmAlertsReducer,
confirmTransaction: confirmTransactionReducer,
swaps: swapsReducer,
+ ramps: rampsReducer,
gas: gasReducer,
localeMessages: localeMessagesReducer,
});
diff --git a/ui/ducks/ramps/constants.ts b/ui/ducks/ramps/constants.ts
new file mode 100644
index 000000000000..7a451658807e
--- /dev/null
+++ b/ui/ducks/ramps/constants.ts
@@ -0,0 +1,137 @@
+import { AggregatorNetwork } from './types';
+
+export const defaultBuyableChains: AggregatorNetwork[] = [
+ {
+ active: true,
+ chainId: 1,
+ chainName: 'Ethereum Mainnet',
+ shortName: 'Ethereum',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 10,
+ chainName: 'Optimism Mainnet',
+ shortName: 'Optimism',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 25,
+ chainName: 'Cronos Mainnet',
+ shortName: 'Cronos',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 56,
+ chainName: 'BNB Chain Mainnet',
+ shortName: 'BNB Chain',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 100,
+ chainName: 'Gnosis Mainnet',
+ shortName: 'Gnosis',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 137,
+ chainName: 'Polygon Mainnet',
+ shortName: 'Polygon',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 250,
+ chainName: 'Fantom Mainnet',
+ shortName: 'Fantom',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 324,
+ chainName: 'zkSync Era Mainnet',
+ shortName: 'zkSync Era',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 1101,
+ chainName: 'Polygon zkEVM',
+ shortName: 'Polygon zkEVM',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 1284,
+ chainName: 'Moonbeam Mainnet',
+ shortName: 'Moonbeam',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 1285,
+ chainName: 'Moonriver Mainnet',
+ shortName: 'Moonriver',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 8453,
+ chainName: 'Base Mainnet',
+ shortName: 'Base',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 42161,
+ chainName: 'Arbitrum Mainnet',
+ shortName: 'Arbitrum',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 42220,
+ chainName: 'Celo Mainnet',
+ shortName: 'Celo',
+ nativeTokenSupported: false,
+ },
+ {
+ active: true,
+ chainId: 43114,
+ chainName: 'Avalanche C-Chain Mainnet',
+ shortName: 'Avalanche C-Chain',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 59144,
+ chainName: 'Linea',
+ shortName: 'Linea',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 1313161554,
+ chainName: 'Aurora Mainnet',
+ shortName: 'Aurora',
+ nativeTokenSupported: false,
+ },
+ {
+ active: true,
+ chainId: 1666600000,
+ chainName: 'Harmony Mainnet (Shard 0)',
+ shortName: 'Harmony (Shard 0)',
+ nativeTokenSupported: true,
+ },
+ {
+ active: true,
+ chainId: 11297108109,
+ chainName: 'Palm Mainnet',
+ shortName: 'Palm',
+ nativeTokenSupported: false,
+ },
+];
diff --git a/ui/ducks/ramps/index.ts b/ui/ducks/ramps/index.ts
new file mode 100644
index 000000000000..b6f8f2473810
--- /dev/null
+++ b/ui/ducks/ramps/index.ts
@@ -0,0 +1 @@
+export * from './ramps';
diff --git a/ui/ducks/ramps/ramps.test.ts b/ui/ducks/ramps/ramps.test.ts
new file mode 100644
index 000000000000..c4ca4089815a
--- /dev/null
+++ b/ui/ducks/ramps/ramps.test.ts
@@ -0,0 +1,187 @@
+import { configureStore, Store } from '@reduxjs/toolkit';
+import RampAPI from '../../helpers/ramps/rampApi/rampAPI';
+import { getCurrentChainId, getUseExternalServices } from '../../selectors';
+import { CHAIN_IDS } from '../../../shared/constants/network';
+import rampsReducer, {
+ fetchBuyableChains,
+ getBuyableChains,
+ getIsNativeTokenBuyable,
+} from './ramps';
+import { defaultBuyableChains } from './constants';
+
+jest.mock('../../helpers/ramps/rampApi/rampAPI');
+const mockedRampAPI = RampAPI as jest.Mocked;
+
+jest.mock('../../selectors', () => ({
+ getCurrentChainId: jest.fn(),
+ getUseExternalServices: jest.fn(),
+ getNames: jest.fn(),
+}));
+
+describe('rampsSlice', () => {
+ let store: Store;
+
+ beforeEach(() => {
+ store = configureStore({
+ reducer: {
+ ramps: rampsReducer,
+ },
+ });
+ mockedRampAPI.getNetworks.mockReset();
+ });
+
+ it('should set the initial state to defaultBuyableChains', () => {
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState).toEqual({
+ buyableChains: defaultBuyableChains,
+ });
+ });
+
+ describe('setBuyableChains', () => {
+ it('should update the buyableChains state when setBuyableChains is dispatched', () => {
+ const mockBuyableChains = [{ chainId: '0x1' }];
+ store.dispatch({
+ type: 'ramps/setBuyableChains',
+ payload: mockBuyableChains,
+ });
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(mockBuyableChains);
+ });
+ it('should disregard invalid array and set buyableChains to default', () => {
+ store.dispatch({
+ type: 'ramps/setBuyableChains',
+ payload: 'Invalid array',
+ });
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(defaultBuyableChains);
+ });
+
+ it('should disregard empty array and set buyableChains to default', () => {
+ store.dispatch({
+ type: 'ramps/setBuyableChains',
+ payload: [],
+ });
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(defaultBuyableChains);
+ });
+
+ it('should disregard array with invalid elements and set buyableChains to default', () => {
+ store.dispatch({
+ type: 'ramps/setBuyableChains',
+ payload: ['some invalid', 'element'],
+ });
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(defaultBuyableChains);
+ });
+ });
+
+ describe('getBuyableChains', () => {
+ it('returns buyableChains', () => {
+ const state = store.getState();
+ expect(getBuyableChains(state)).toBe(state.ramps.buyableChains);
+ });
+ });
+
+ describe('fetchBuyableChains', () => {
+ beforeEach(() => {
+ // simulate the Basic Functionality Toggle being on
+ const getUseExternalServicesMock = jest.mocked(getUseExternalServices);
+ getUseExternalServicesMock.mockReturnValue(true);
+ });
+
+ it('should call RampAPI.getNetworks when the Basic Functionality Toggle is on', async () => {
+ // @ts-expect-error this is a valid action
+ await store.dispatch(fetchBuyableChains());
+ expect(RampAPI.getNetworks).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not call RampAPI.getNetworks when the Basic Functionality Toggle is off', async () => {
+ const getUseExternalServicesMock = jest.mocked(getUseExternalServices);
+ getUseExternalServicesMock.mockReturnValue(false);
+
+ // @ts-expect-error this is a valid action
+ await store.dispatch(fetchBuyableChains());
+
+ expect(RampAPI.getNetworks).not.toHaveBeenCalled();
+ });
+
+ it('should update the state with the data that is returned', async () => {
+ const mockBuyableChains = [
+ {
+ active: true,
+ chainId: 1,
+ chainName: 'Ethereum Mainnet',
+ nativeTokenSupported: true,
+ shortName: 'Ethereum',
+ },
+ ];
+ jest.spyOn(RampAPI, 'getNetworks').mockResolvedValue(mockBuyableChains);
+ // @ts-expect-error this is a valid action
+ await store.dispatch(fetchBuyableChains());
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(mockBuyableChains);
+ });
+ it('should set state to defaultBuyableChains when returned networks are undefined', async () => {
+ // @ts-expect-error forcing undefined to test the behavior
+ jest.spyOn(RampAPI, 'getNetworks').mockResolvedValue(undefined);
+ // @ts-expect-error this is a valid action
+ await store.dispatch(fetchBuyableChains());
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(defaultBuyableChains);
+ });
+
+ it('should set state to defaultBuyableChains when returned networks are empty', async () => {
+ jest.spyOn(RampAPI, 'getNetworks').mockResolvedValue([]);
+ // @ts-expect-error this is a valid action
+ await store.dispatch(fetchBuyableChains());
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(defaultBuyableChains);
+ });
+
+ it('should set state to defaultBuyableChains when API request fails', async () => {
+ jest
+ .spyOn(RampAPI, 'getNetworks')
+ .mockRejectedValue(new Error('API error'));
+ // @ts-expect-error this is a valid action
+ await store.dispatch(fetchBuyableChains());
+ const { ramps: rampsState } = store.getState();
+ expect(rampsState.buyableChains).toEqual(defaultBuyableChains);
+ });
+ });
+
+ describe('getIsNativeTokenBuyable', () => {
+ const getCurrentChainIdMock = jest.mocked(getCurrentChainId);
+
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ it('should return true when current chain is buyable', () => {
+ getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET);
+ const state = store.getState();
+ expect(getIsNativeTokenBuyable(state)).toEqual(true);
+ });
+
+ it('should return false when current chain is not buyable', () => {
+ getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.GOERLI);
+ const state = store.getState();
+ expect(getIsNativeTokenBuyable(state)).toEqual(false);
+ });
+
+ it('should return false when current chain is not a valid hex string', () => {
+ getCurrentChainIdMock.mockReturnValue('0x');
+ const state = store.getState();
+ expect(getIsNativeTokenBuyable(state)).toEqual(false);
+ });
+
+ it('should return false when buyable chains is a corrupted array', () => {
+ const mockState = {
+ ramps: {
+ buyableChains: [null, null, null],
+ },
+ };
+ getCurrentChainIdMock.mockReturnValue(CHAIN_IDS.MAINNET);
+ expect(getIsNativeTokenBuyable(mockState)).toEqual(false);
+ });
+ });
+});
diff --git a/ui/ducks/ramps/ramps.ts b/ui/ducks/ramps/ramps.ts
new file mode 100644
index 000000000000..afff609cd4d8
--- /dev/null
+++ b/ui/ducks/ramps/ramps.ts
@@ -0,0 +1,78 @@
+import { createSelector } from 'reselect';
+import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
+import { getCurrentChainId, getUseExternalServices } from '../../selectors';
+import RampAPI from '../../helpers/ramps/rampApi/rampAPI';
+import { hexToDecimal } from '../../../shared/modules/conversion.utils';
+import { defaultBuyableChains } from './constants';
+import { AggregatorNetwork } from './types';
+
+export const fetchBuyableChains = createAsyncThunk(
+ 'ramps/fetchBuyableChains',
+ async (_, { getState }) => {
+ const state = getState();
+ const allowExternalRequests = getUseExternalServices(state);
+ if (!allowExternalRequests) {
+ return defaultBuyableChains;
+ }
+ return await RampAPI.getNetworks();
+ },
+);
+
+const rampsSlice = createSlice({
+ name: 'ramps',
+ initialState: {
+ buyableChains: defaultBuyableChains,
+ },
+ reducers: {
+ setBuyableChains: (state, action) => {
+ if (
+ Array.isArray(action.payload) &&
+ action.payload.length > 0 &&
+ action.payload.every((network) => network?.chainId)
+ ) {
+ state.buyableChains = action.payload;
+ } else {
+ state.buyableChains = defaultBuyableChains;
+ }
+ },
+ },
+ extraReducers: (builder) => {
+ builder
+ .addCase(fetchBuyableChains.fulfilled, (state, action) => {
+ const networks = action.payload;
+ if (networks && networks.length > 0) {
+ state.buyableChains = networks;
+ } else {
+ state.buyableChains = defaultBuyableChains;
+ }
+ })
+ .addCase(fetchBuyableChains.rejected, (state) => {
+ state.buyableChains = defaultBuyableChains;
+ });
+ },
+});
+
+const { reducer } = rampsSlice;
+
+// Can be typed to RootState if/when the interface is defined
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const getBuyableChains = (state: any) =>
+ state.ramps?.buyableChains ?? defaultBuyableChains;
+
+export const getIsNativeTokenBuyable = createSelector(
+ [getCurrentChainId, getBuyableChains],
+ (currentChainId, buyableChains) => {
+ try {
+ return buyableChains
+ .filter(Boolean)
+ .some(
+ (network: AggregatorNetwork) =>
+ String(network.chainId) === hexToDecimal(currentChainId),
+ );
+ } catch (e) {
+ return false;
+ }
+ },
+);
+
+export default reducer;
diff --git a/ui/ducks/ramps/types.ts b/ui/ducks/ramps/types.ts
new file mode 100644
index 000000000000..6a1571715dfe
--- /dev/null
+++ b/ui/ducks/ramps/types.ts
@@ -0,0 +1,7 @@
+export type AggregatorNetwork = {
+ active: boolean;
+ chainId: number;
+ chainName: string;
+ nativeTokenSupported: boolean;
+ shortName: string;
+};
diff --git a/ui/helpers/ramps/rampApi/rampAPI.test.ts b/ui/helpers/ramps/rampApi/rampAPI.test.ts
new file mode 100644
index 000000000000..bf6e2297481d
--- /dev/null
+++ b/ui/helpers/ramps/rampApi/rampAPI.test.ts
@@ -0,0 +1,23 @@
+import nock from 'nock';
+import { defaultBuyableChains } from '../../../ducks/ramps/constants';
+import rampAPI from './rampAPI';
+
+const mockedResponse = {
+ networks: defaultBuyableChains,
+};
+
+describe('rampAPI', () => {
+ afterEach(() => {
+ nock.cleanAll();
+ });
+
+ it('should fetch networks', async () => {
+ nock('https://on-ramp-content.uat-api.cx.metamask.io')
+ .get('/regions/networks')
+ .query(true)
+ .reply(200, mockedResponse);
+
+ const result = await rampAPI.getNetworks();
+ expect(result).toStrictEqual(mockedResponse.networks);
+ });
+});
diff --git a/ui/helpers/ramps/rampApi/rampAPI.ts b/ui/helpers/ramps/rampApi/rampAPI.ts
new file mode 100644
index 000000000000..a1da6da8ef0c
--- /dev/null
+++ b/ui/helpers/ramps/rampApi/rampAPI.ts
@@ -0,0 +1,25 @@
+import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout';
+import { AggregatorNetwork } from '../../../ducks/ramps/types';
+
+const fetchWithTimeout = getFetchWithTimeout();
+
+const isProdEnv = process.env.NODE_ENV === 'production';
+const PROD_RAMP_API_BASE_URL = 'https://on-ramp-content.api.cx.metamask.io';
+const UAT_RAMP_API_BASE_URL = 'https://on-ramp-content.uat-api.cx.metamask.io';
+
+const rampApiBaseUrl =
+ process.env.METAMASK_RAMP_API_CONTENT_BASE_URL ||
+ (isProdEnv ? PROD_RAMP_API_BASE_URL : UAT_RAMP_API_BASE_URL);
+
+const RampAPI = {
+ async getNetworks(): Promise {
+ const url = new URL('/regions/networks', rampApiBaseUrl);
+ url.searchParams.set('context', 'extension');
+ const response = await fetchWithTimeout(url.toString());
+
+ const { networks } = await response.json();
+ return networks;
+ },
+};
+
+export default RampAPI;
diff --git a/ui/hooks/experiences/useRamps.test.js b/ui/hooks/ramps/useRamps/useRamps.test.tsx
similarity index 85%
rename from ui/hooks/experiences/useRamps.test.js
rename to ui/hooks/ramps/useRamps/useRamps.test.tsx
index dcb1dc2eb43c..9c53eb9da247 100644
--- a/ui/hooks/experiences/useRamps.test.js
+++ b/ui/hooks/ramps/useRamps/useRamps.test.tsx
@@ -1,7 +1,7 @@
-import React from 'react';
+import React, { FC } from 'react';
import { Provider } from 'react-redux';
import { renderHook } from '@testing-library/react-hooks';
-import configureStore from '../../store/store';
+import configureStore from '../../../store/store';
import useRamps, { RampsMetaMaskEntry } from './useRamps';
const mockedMetametricsId = '0xtestMetaMetricsId';
@@ -15,17 +15,18 @@ let mockStoreState = {
},
};
-const wrapper = ({ children }) => (
+const wrapper: FC = ({ children }) => (
{children}
);
describe('useRamps', () => {
- beforeEach(() => {
- global.platform = { openTab: jest.fn() };
- });
-
- afterEach(() => {
- jest.clearAllMocks();
+ // mock the openTab function to test if it is called with the correct URL when opening the Pdapp
+ beforeAll(() => {
+ Object.defineProperty(global, 'platform', {
+ value: {
+ openTab: jest.fn(),
+ },
+ });
});
it('should default the metamask entry param when opening the buy crypto URL', () => {
@@ -81,9 +82,11 @@ describe('useRamps', () => {
});
});
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
it.each(['0x1', '0x38', '0xa'])(
'should open the buy crypto URL with the currently connected chain ID',
- (mockChainId) => {
+ (mockChainId: string) => {
mockStoreState = {
...mockStoreState,
metamask: {
diff --git a/ui/hooks/experiences/useRamps.ts b/ui/hooks/ramps/useRamps/useRamps.ts
similarity index 90%
rename from ui/hooks/experiences/useRamps.ts
rename to ui/hooks/ramps/useRamps/useRamps.ts
index 76bcdfe47879..7219d5fe4193 100644
--- a/ui/hooks/experiences/useRamps.ts
+++ b/ui/hooks/ramps/useRamps/useRamps.ts
@@ -1,8 +1,8 @@
import { useCallback } from 'react';
import { useSelector } from 'react-redux';
import type { Hex } from '@metamask/utils';
-import { ChainId } from '../../../shared/constants/network';
-import { getCurrentChainId, getMetaMetricsId } from '../../selectors';
+import { ChainId } from '../../../../shared/constants/network';
+import { getCurrentChainId, getMetaMetricsId } from '../../../selectors';
type IUseRamps = {
openBuyCryptoInPdapp: VoidFunction;
diff --git a/ui/hooks/useTheme.test.ts b/ui/hooks/useTheme.test.ts
index 8b0f2edd66ac..5b44d2c918eb 100644
--- a/ui/hooks/useTheme.test.ts
+++ b/ui/hooks/useTheme.test.ts
@@ -3,6 +3,7 @@ import { renderHookWithProvider } from '../../test/lib/render-helpers';
import { useTheme } from './useTheme';
jest.mock('../selectors', () => ({
+ ...jest.requireActual('../selectors'),
getTheme: jest.fn(),
}));
diff --git a/ui/pages/asset/components/asset-page.tsx b/ui/pages/asset/components/asset-page.tsx
index d83f6cfe24f8..38ea46e475f3 100644
--- a/ui/pages/asset/components/asset-page.tsx
+++ b/ui/pages/asset/components/asset-page.tsx
@@ -7,7 +7,6 @@ import { isEqual } from 'lodash';
import {
getCurrentCurrency,
getIsBridgeChain,
- getIsBuyableChain,
getIsSwapsChain,
getSelectedInternalAccount,
getSwapsDefaultToken,
@@ -42,6 +41,7 @@ import { DEFAULT_ROUTE } from '../../../helpers/constants/routes';
import { getConversionRate } from '../../../ducks/metamask/metamask';
import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils';
import CoinButtons from '../../../components/app/wallet-overview/coin-buttons';
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
import AssetChart from './chart/asset-chart';
import TokenButtons from './token-buttons';
@@ -102,7 +102,7 @@ const AssetPage = ({
const conversionRate = useSelector(getConversionRate);
const allMarketData = useSelector(getTokensMarketData);
const isBridgeChain = useSelector(getIsBridgeChain);
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const defaultSwapsToken = useSelector(getSwapsDefaultToken, isEqual);
const account = useSelector(getSelectedInternalAccount, isEqual);
const isSwapsChain = useSelector(getIsSwapsChain);
diff --git a/ui/pages/asset/components/token-buttons.tsx b/ui/pages/asset/components/token-buttons.tsx
index f0e39f046e24..6cd78fab7693 100644
--- a/ui/pages/asset/components/token-buttons.tsx
+++ b/ui/pages/asset/components/token-buttons.tsx
@@ -13,7 +13,7 @@ import { startNewDraftTransaction } from '../../../ducks/send';
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
import { isHardwareKeyring } from '../../../helpers/utils/hardware';
import { setSwapsFromToken } from '../../../ducks/swaps/swaps';
-import useRamps from '../../../hooks/experiences/useRamps';
+import useRamps from '../../../hooks/ramps/useRamps/useRamps';
import { getPortfolioUrl } from '../../../helpers/utils/portfolio';
///: END:ONLY_INCLUDE_IF
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
@@ -28,11 +28,9 @@ import {
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
getIsBridgeChain,
getCurrentKeyring,
- getIsBuyableChain,
getMetaMetricsId,
///: END:ONLY_INCLUDE_IF
} from '../../../selectors';
-
import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys';
import { showModal } from '../../../store/actions';
import { MetaMetricsContext } from '../../../contexts/metametrics';
@@ -42,7 +40,6 @@ import {
MetaMetricsSwapsEventSource,
} from '../../../../shared/constants/metametrics';
import { AssetType } from '../../../../shared/constants/transaction';
-
import {
Display,
IconColor,
@@ -50,6 +47,9 @@ import {
} from '../../../helpers/constants/design-system';
import IconButton from '../../../components/ui/icon-button/icon-button';
import { Box, Icon, IconName } from '../../../components/component-library';
+///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
+///: END:ONLY_INCLUDE_IF
import { Asset } from './asset-page';
const TokenButtons = ({
@@ -71,7 +71,7 @@ const TokenButtons = ({
const isSwapsChain = useSelector(getIsSwapsChain);
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const isBridgeChain = useSelector(getIsBridgeChain);
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const metaMetricsId = useSelector(getMetaMetricsId);
const { openBuyCryptoInPdapp } = useRamps();
///: END:ONLY_INCLUDE_IF
diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.component.js b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.component.js
index d177bc6b8df2..d4a92172aed6 100644
--- a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.component.js
+++ b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.component.js
@@ -43,12 +43,11 @@ import {
getAccountName,
getAddressBookEntry,
getInternalAccounts,
- getIsBuyableChain,
getMetadataContractName,
getNetworkIdentifier,
getSwapsDefaultToken,
} from '../../../../selectors';
-import useRamps from '../../../../hooks/experiences/useRamps';
+import useRamps from '../../../../hooks/ramps/useRamps/useRamps';
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
import { MetaMetricsContext } from '../../../../contexts/metametrics';
import {
@@ -58,6 +57,7 @@ import {
///: END:ONLY_INCLUDE_IF
import { BlockaidResultType } from '../../../../../shared/constants/security-provider';
+import { getIsNativeTokenBuyable } from '../../../../ducks/ramps';
import {
ConfirmPageContainerHeader,
ConfirmPageContainerContent,
@@ -118,7 +118,7 @@ const ConfirmPageContainer = (props) => {
const [collectionBalance, setCollectionBalance] = useState('0');
const [isShowingTxInsightWarnings, setIsShowingTxInsightWarnings] =
useState(false);
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const contact = useSelector((state) => getAddressBookEntry(state, toAddress));
const networkIdentifier = useSelector(getNetworkIdentifier);
const defaultToken = useSelector(getSwapsDefaultToken);
@@ -131,10 +131,6 @@ const ConfirmPageContainer = (props) => {
getMetadataContractName(state, toAddress),
);
- // TODO: Move useRamps hook to the confirm-transaction-base parent component.
- // TODO: openBuyCryptoInPdapp should be passed to this component as a custom prop.
- // We try to keep this component for layout purpose only, we need to move this hook to the confirm-transaction-base parent
- // component once it is converted to a functional component
const { openBuyCryptoInPdapp } = useRamps();
const isSetApproveForAll =
diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js
index be3ba7793eaf..db07ad4117e7 100644
--- a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js
+++ b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js
@@ -1,7 +1,6 @@
import { connect } from 'react-redux';
import {
getAddressBookEntry,
- getIsBuyableChain,
getNetworkIdentifier,
getSwapsDefaultToken,
getMetadataContractName,
@@ -12,7 +11,6 @@ import ConfirmPageContainer from './confirm-page-container.component';
function mapStateToProps(state, ownProps) {
const to = ownProps.toAddress;
- const isBuyableChain = getIsBuyableChain(state);
const contact = getAddressBookEntry(state, to);
const networkIdentifier = getNetworkIdentifier(state);
const defaultToken = getSwapsDefaultToken(state);
@@ -23,7 +21,6 @@ function mapStateToProps(state, ownProps) {
const toMetadataName = getMetadataContractName(state, to);
return {
- isBuyableChain,
contact,
toName,
toMetadataName,
diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js
index abe09d6f63dc..dc225367090c 100644
--- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js
+++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js
@@ -47,7 +47,6 @@ import {
getPreferences,
doesAddressRequireLedgerHidConnection,
getTokenList,
- getIsBuyableChain,
getEnsResolutionByAddress,
getUnapprovedTransaction,
getFullTxData,
@@ -109,6 +108,7 @@ import { showCustodyConfirmLink } from '../../../store/institutional/institution
///: END:ONLY_INCLUDE_IF
import { calcGasTotal } from '../../../../shared/lib/transactions-controller-utils';
import { subtractHexes } from '../../../../shared/modules/conversion.utils';
+import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
import ConfirmTransactionBase from './confirm-transaction-base.component';
let customNonceValue = '';
@@ -164,7 +164,7 @@ const mapStateToProps = (state, ownProps) => {
const isGasEstimatesLoading = getIsGasEstimatesLoading(state);
const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state);
- const isBuyableChain = getIsBuyableChain(state);
+ const isBuyableChain = getIsNativeTokenBuyable(state);
const { confirmTransaction, metamask } = state;
const conversionRate = getConversionRate(state);
const { addressBook, nextNonce } = metamask;
diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js
index 918c24ed6681..bfa23af0af75 100644
--- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js
+++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js
@@ -27,6 +27,7 @@ import {
BlockaidReason,
BlockaidResultType,
} from '../../../../shared/constants/security-provider';
+import { defaultBuyableChains } from '../../../ducks/ramps/constants';
import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods';
import ConfirmTransactionBase from './confirm-transaction-base.container';
@@ -203,6 +204,9 @@ const baseStore = {
appState: {
sendInputCurrencySwitched: false,
},
+ ramps: {
+ buyableChains: defaultBuyableChains,
+ },
};
const mockedStoreWithConfirmTxParams = (
@@ -497,6 +501,7 @@ describe('Confirm Transaction Base', () => {
it('handleMMISubmit calls sendTransaction correctly and then showCustodianDeepLink', async () => {
const state = {
+ ...baseStore,
appState: {
...baseStore.appState,
gasLoadingAnimationIsShowing: false,
diff --git a/ui/pages/confirmations/hooks/useConfirmationAlertActions.ts b/ui/pages/confirmations/hooks/useConfirmationAlertActions.ts
index fae80bc53316..b02ef8f809a6 100644
--- a/ui/pages/confirmations/hooks/useConfirmationAlertActions.ts
+++ b/ui/pages/confirmations/hooks/useConfirmationAlertActions.ts
@@ -1,6 +1,6 @@
import { useCallback } from 'react';
import { AlertActionKey } from '../../../components/app/confirm/info/row/constants';
-import useRamps from '../../../hooks/experiences/useRamps';
+import useRamps from '../../../hooks/ramps/useRamps/useRamps';
import { useTransactionModalContext } from '../../../contexts/transaction-modal';
const useConfirmationAlertActions = () => {
diff --git a/ui/pages/confirmations/hooks/useTransactionFunction.test.js b/ui/pages/confirmations/hooks/useTransactionFunction.test.js
index cc4e69863892..22984ba71a37 100644
--- a/ui/pages/confirmations/hooks/useTransactionFunction.test.js
+++ b/ui/pages/confirmations/hooks/useTransactionFunction.test.js
@@ -22,6 +22,7 @@ useGasEstimates.mockImplementation(() => FEE_MARKET_ESTIMATE_RETURN_VALUE);
jest.mock('../../../selectors', () => ({
checkNetworkAndAccountSupports1559: () => true,
+ getCurrentChainId: jest.fn().mockReturnValue('0x1'),
}));
const wrapper = ({ children }) => (
diff --git a/ui/pages/confirmations/send/gas-display/gas-display.js b/ui/pages/confirmations/send/gas-display/gas-display.js
index e07c5473bdb9..5fbad8445cd6 100644
--- a/ui/pages/confirmations/send/gas-display/gas-display.js
+++ b/ui/pages/confirmations/send/gas-display/gas-display.js
@@ -23,7 +23,6 @@ import TransactionDetail from '../../components/transaction-detail';
import ActionableMessage from '../../../../components/ui/actionable-message';
import {
getPreferences,
- getIsBuyableChain,
transactionFeeSelector,
getIsTestnet,
getUseCurrencyRateCheck,
@@ -46,7 +45,8 @@ import {
MetaMetricsEventName,
} from '../../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../../contexts/metametrics';
-import useRamps from '../../../../hooks/experiences/useRamps';
+import useRamps from '../../../../hooks/ramps/useRamps/useRamps';
+import { getIsNativeTokenBuyable } from '../../../../ducks/ramps';
export default function GasDisplay({ gasError }) {
const t = useContext(I18nContext);
@@ -58,7 +58,7 @@ export default function GasDisplay({ gasError }) {
const providerConfig = useSelector(getProviderConfig);
const isTestnet = useSelector(getIsTestnet);
- const isBuyableChain = useSelector(getIsBuyableChain);
+ const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const draftTransaction = useSelector(getCurrentDraftTransaction);
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
const { showFiatInTestnets, useNativeCurrencyAsPrimaryCurrency } =
diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js
index 3a3dfc7ed753..cb177340501d 100644
--- a/ui/pages/home/home.component.js
+++ b/ui/pages/home/home.component.js
@@ -25,8 +25,8 @@ import Popover from '../../components/ui/popover';
import ConnectedSites from '../connected-sites';
import ConnectedAccounts from '../connected-accounts';
import { isMv3ButOffscreenDocIsMissing } from '../../../shared/modules/mv3.utils';
-
import ActionableMessage from '../../components/ui/actionable-message/actionable-message';
+
import {
FontWeight,
Display,
@@ -220,6 +220,7 @@ export default class Home extends PureComponent {
custodianDeepLink: PropTypes.object,
accountType: PropTypes.string,
///: END:ONLY_INCLUDE_IF
+ fetchBuyableChains: PropTypes.func.isRequired,
};
state = {
@@ -362,6 +363,8 @@ export default class Home extends PureComponent {
setWaitForConfirmDeepLinkDialog(false);
});
///: END:ONLY_INCLUDE_IF
+
+ this.props.fetchBuyableChains();
}
static getDerivedStateFromProps(props) {
diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js
index 7b1334f80e0e..ea9c7171448a 100644
--- a/ui/pages/home/home.container.js
+++ b/ui/pages/home/home.container.js
@@ -85,6 +85,7 @@ import {
} from '../../ducks/app/app';
import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask';
import { getSwapsFeatureIsLive } from '../../ducks/swaps/swaps';
+import { fetchBuyableChains } from '../../ducks/ramps';
import { getEnvironmentType } from '../../../app/scripts/lib/util';
import { getIsBrowserDeprecated } from '../../helpers/utils/util';
import {
@@ -319,6 +320,7 @@ const mapDispatchToProps = (dispatch) => {
///: END:ONLY_INCLUDE_IF
setBasicFunctionalityModalOpen: () =>
dispatch(openBasicFunctionalityModal()),
+ fetchBuyableChains: () => dispatch(fetchBuyableChains()),
};
};
diff --git a/ui/pages/routes/routes.component.test.js b/ui/pages/routes/routes.component.test.js
index 8c7d127c88e1..3024ea436e35 100644
--- a/ui/pages/routes/routes.component.test.js
+++ b/ui/pages/routes/routes.component.test.js
@@ -1,7 +1,7 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import { act } from '@testing-library/react';
-
+import thunk from 'redux-thunk';
import { SEND_STAGES } from '../../ducks/send';
import {
CONFIRMATION_V_NEXT_ROUTE,
@@ -14,6 +14,8 @@ import mockState from '../../../test/data/mock-state.json';
import { useIsOriginalNativeTokenSymbol } from '../../hooks/useIsOriginalNativeTokenSymbol';
import Routes from '.';
+const middlewares = [thunk];
+
const mockShowNetworkDropdown = jest.fn();
const mockHideNetworkDropdown = jest.fn();
@@ -74,7 +76,7 @@ jest.mock('../../helpers/utils/feature-flags', () => ({
}));
const render = async (route, state) => {
- const store = configureMockStore()({
+ const store = configureMockStore(middlewares)({
...mockSendState,
...state,
});
diff --git a/ui/pages/swaps/prepare-swap-page/review-quote.js b/ui/pages/swaps/prepare-swap-page/review-quote.js
index 5fb2badd8bcb..0dc3f89009cb 100644
--- a/ui/pages/swaps/prepare-swap-page/review-quote.js
+++ b/ui/pages/swaps/prepare-swap-page/review-quote.js
@@ -141,7 +141,7 @@ import {
import { GAS_FEES_LEARN_MORE_URL } from '../../../../shared/lib/ui-utils';
import ExchangeRateDisplay from '../exchange-rate-display';
import InfoTooltip from '../../../components/ui/info-tooltip';
-import useRamps from '../../../hooks/experiences/useRamps';
+import useRamps from '../../../hooks/ramps/useRamps/useRamps';
import ViewQuotePriceDifference from './view-quote-price-difference';
import SlippageNotificationModal from './slippage-notification-modal';
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index f613015efb7c..cd049873dfa4 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -13,7 +13,6 @@ import { TransactionStatus } from '@metamask/transaction-controller';
import { addHexPrefix, getEnvironmentType } from '../../app/scripts/lib/util';
import {
TEST_CHAINS,
- BUYABLE_CHAINS_MAP,
MAINNET_DISPLAY_NAME,
BSC_DISPLAY_NAME,
POLYGON_DISPLAY_NAME,
@@ -1307,11 +1306,6 @@ export function getIsBridgeChain(state) {
const chainId = getCurrentChainId(state);
return ALLOWED_BRIDGE_CHAIN_IDS.includes(chainId);
}
-
-export function getIsBuyableChain(state) {
- const chainId = getCurrentChainId(state);
- return Object.keys(BUYABLE_CHAINS_MAP).includes(chainId);
-}
export function getNativeCurrencyImage(state) {
const chainId = getCurrentChainId(state);
return CHAIN_ID_TOKEN_IMAGE_MAP[chainId];
From 66bf2f19632feeb44c6a981c18db497ae2754596 Mon Sep 17 00:00:00 2001
From: Ariella Vu <20778143+digiwand@users.noreply.github.com>
Date: Thu, 27 Jun 2024 18:21:48 +0200
Subject: [PATCH 02/15] feat: Add domain binding SIWE redesign alert row
(#25281)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
- Adds Alert for Domain Binding SIWE
- Minor out-of-scope cleanup:
- update useBlockaidAlert to use currentConfirmationSelector instead of
useCurrentConfirmation c21feb210598157a5ca1b5c667ebb5324dfb1ab0
- force return boolean for isSIWESignatureRequest
f779176f54f3529d2c1ee1d94e53d3be2c089cb1
Currently, the existing logic displays the same friction modal for
multiple alerts with only one danger alert. There will be a separate PR
to update the Alert System to follow the designs mentioned in the Issue
ticket
## **Related issues**
Fixes: https://github.com/MetaMask/metamask-extension/issues/24683
## **Manual testing steps**
1. comment out code in useCurrentConfirmation:
```
// // comment if condition below to enable re-design for SIWE signatures
```
2. yarn start
3. open dapp and test SIWE Bad Domain
## **Screenshots/Recordings**
### **Before**
### **After**
(There is another PR that is hiding the "i" icon beside the Alert for
SIWE)
## **Pre-merge author checklist**
- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
---
app/_locales/en/messages.json | 6 +
test/data/confirmations/personal_sign.ts | 27 ++--
.../__snapshots__/siwe-sign.test.tsx.snap | 24 ++++
.../personal-sign/siwe-sign/siwe-sign.tsx | 2 +-
.../useDomainMismatchAlerts.test.ts | 135 ++++++++++++++++++
.../signatures/useDomainMismatchAlerts.ts | 45 ++++++
.../hooks/useConfirmationAlerts.ts | 12 +-
.../hooks/useCurrentConfirmation.ts | 4 +
ui/pages/confirmations/types/confirm.ts | 18 +--
ui/pages/confirmations/utils/confirm.ts | 2 +-
10 files changed, 246 insertions(+), 29 deletions(-)
create mode 100644 ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts
create mode 100644 ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 53961ff86b35..d59f9d572f6c 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -412,6 +412,9 @@
"alertMessagePendingTransactions": {
"message": "This transaction won’t go through until a previous transaction is complete. Learn how to cancel or speed up a transaction."
},
+ "alertMessageSignInDomainMismatch": {
+ "message": "The site making the request is not the site you’re signing into. This could be an attempt to steal your login credentials."
+ },
"alertMessageSigningOrSubmitting": {
"message": "This transaction will only go through once your previous transaction is complete."
},
@@ -445,6 +448,9 @@
"alertReasonPendingTransactions": {
"message": "Pending transaction"
},
+ "alertReasonSignIn": {
+ "message": "Suspicious sign-in request"
+ },
"alertSettingsUnconnectedAccount": {
"message": "Browsing a website with an unconnected account selected"
},
diff --git a/test/data/confirmations/personal_sign.ts b/test/data/confirmations/personal_sign.ts
index bac5b3f9838a..6ac82dfe57bb 100644
--- a/test/data/confirmations/personal_sign.ts
+++ b/test/data/confirmations/personal_sign.ts
@@ -35,15 +35,20 @@ export const signatureRequestSIWE = {
siwe: {
isSIWEMessage: true,
parsedMessage: {
- domain: 'metamask.github.io',
address: '0x935e73edb9ff52e23bac7f7e049a1ecd06d05477',
+ chainId: 1,
+ domain: 'metamask.github.io',
+ expirationTime: null,
+ issuedAt: '2021-09-30T16:25:24.000Z',
+ nonce: '32891757',
+ notBefore: '2022-03-17T12:45:13.610Z',
+ requestId: 'some_id',
+ scheme: null,
statement:
'I accept the MetaMask Terms of Service: https://community.metamask.io/tos',
uri: 'https://metamask.github.io',
version: '1',
- chainId: 1,
- nonce: '32891757',
- issuedAt: '2021-09-30T16:25:24.000Z',
+ resources: null,
},
},
},
@@ -67,17 +72,19 @@ export const SignatureRequestSIWEWithResources = {
siwe: {
isSIWEMessage: true,
parsedMessage: {
- domain: 'metamask.github.io',
address: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
- statement:
- 'I accept the MetaMask Terms of Service: https://community.metamask.io/tos',
- uri: 'https://metamask.github.io',
- version: '1',
chainId: 1,
- nonce: '32891757',
+ domain: 'metamask.github.io',
+ expirationTime: null,
issuedAt: '2021-09-30T16:25:24.000Z',
+ nonce: '32891757',
notBefore: '2022-03-17T12:45:13.610Z',
requestId: 'some_id',
+ scheme: null,
+ statement:
+ 'I accept the MetaMask Terms of Service: https://community.metamask.io/tos',
+ uri: 'https://metamask.github.io',
+ version: '1',
resources: [
'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu',
'https://example.com/my-web2-claim.json',
diff --git a/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/__snapshots__/siwe-sign.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/__snapshots__/siwe-sign.test.tsx.snap
index 1e62756cf223..d6b27a2629ad 100644
--- a/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/__snapshots__/siwe-sign.test.tsx.snap
+++ b/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/__snapshots__/siwe-sign.test.tsx.snap
@@ -241,6 +241,30 @@ exports[`SIWESignInfo renders correctly for SIWE signature request 1`] = `
+
`;
diff --git a/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/siwe-sign.tsx b/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/siwe-sign.tsx
index 94389b253450..8c00c16f9c02 100644
--- a/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/siwe-sign.tsx
+++ b/ui/pages/confirmations/components/confirm/info/personal-sign/siwe-sign/siwe-sign.tsx
@@ -44,7 +44,7 @@ const SIWESignInfo: React.FC = () => {
return (
<>
-
+
diff --git a/ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts b/ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts
new file mode 100644
index 000000000000..bfb63ee8496f
--- /dev/null
+++ b/ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.test.ts
@@ -0,0 +1,135 @@
+import { ApprovalType } from '@metamask/controller-utils';
+import { Severity } from '../../../../../helpers/constants/design-system';
+import { renderHookWithProvider } from '../../../../../../test/lib/render-helpers';
+import mockState from '../../../../../../test/data/mock-state.json';
+import useDomainMismatchAlert from './useDomainMismatchAlerts';
+
+const MOCK_ORIGIN = 'https://example-dapp.example';
+const MOCK_SUSPICIOUS_DOMAIN = 'http://suspicious.example';
+const MOCK_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc';
+
+const mockSiwe = {
+ isSIWEMessage: true,
+ parsedMessage: {
+ domain: MOCK_SUSPICIOUS_DOMAIN,
+ address: MOCK_ADDRESS,
+ statement:
+ 'Click to sign in and accept the Terms of Service: https://community.metamask.io/tos',
+ uri: 'http://localhost:8080',
+ version: '1',
+ nonce: 'STMt6KQMwwdOXE306',
+ chainId: 1,
+ issuedAt: '2023-03-18T21:40:40.823Z',
+ resources: [
+ 'ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu',
+ 'https://example.com/my-web2-claim.json',
+ ],
+ },
+};
+
+const mockCurrentConfirmation = {
+ id: '1',
+ status: 'unapproved',
+ time: new Date().getTime(),
+ type: ApprovalType.PersonalSign,
+ msgParams: {
+ from: MOCK_ADDRESS,
+ data: '0x6c6f63616c686f73743a383038302077616e747320796f7520746f207369676e20696e207769746820796f757220457468657265756d206163636f756e743a0a3078466232433135303034333433393034653566343038323537386334653865313131303563463765330a0a436c69636b20746f207369676e20696e20616e642061636365707420746865205465726d73206f6620536572766963653a2068747470733a2f2f636f6d6d756e6974792e6d6574616d61736b2e696f2f746f730a0a5552493a20687474703a2f2f6c6f63616c686f73743a383038300a56657273696f6e3a20310a436861696e2049443a20310a4e6f6e63653a2053544d74364b514d7777644f58453330360a4973737565642041743a20323032322d30332d31385432313a34303a34302e3832335a0a5265736f75726365733a0a2d20697066733a2f2f516d653773733341525667787636725871565069696b4d4a3875324e4c676d67737a673133705972444b456f69750a2d2068747470733a2f2f6578616d706c652e636f6d2f6d792d776562322d636c61696d2e6a736f6e',
+ origin: MOCK_ORIGIN,
+ siwe: mockSiwe,
+ },
+};
+
+const mockExpectedState = {
+ ...mockState,
+ metamask: {
+ ...mockState.metamask,
+ unapprovedPersonalMsgs: {
+ '1': { ...mockCurrentConfirmation },
+ },
+ pendingApprovals: {
+ '1': {
+ ...mockCurrentConfirmation,
+ // origin: MOCK_ORIGIN,
+ requestData: {},
+ requestState: null,
+ expectsResult: false,
+ },
+ },
+ preferences: { redesignedConfirmationsEnabled: true },
+ },
+ confirm: { currentConfirmation: mockCurrentConfirmation },
+};
+
+describe('useDomainMismatchAlert', () => {
+ beforeAll(() => {
+ process.env.ENABLE_CONFIRMATION_REDESIGN = 'true';
+ });
+
+ afterAll(() => {
+ process.env.ENABLE_CONFIRMATION_REDESIGN = 'false';
+ });
+
+ describe('returns an empty array', () => {
+ it('when there is no current confirmation', () => {
+ const { result } = renderHookWithProvider(
+ () => useDomainMismatchAlert(),
+ mockState,
+ );
+ expect(result.current).toEqual([]);
+ });
+
+ it('when the current confirmation is not a SIWE request', () => {
+ const { result } = renderHookWithProvider(
+ () => useDomainMismatchAlert(),
+ {
+ ...mockExpectedState,
+ confirm: {
+ currentConfirmation: {
+ ...mockCurrentConfirmation,
+ msgParams: {
+ ...mockCurrentConfirmation.msgParams,
+ siwe: {
+ isSIWEMessage: false,
+ parsedMessage: mockSiwe.parsedMessage,
+ },
+ },
+ },
+ },
+ },
+ );
+ expect(result.current).toEqual([]);
+ });
+
+ it('when the SIWE domain matches origin', () => {
+ const originalDomain = mockSiwe.parsedMessage.domain;
+ mockSiwe.parsedMessage.domain = MOCK_ORIGIN;
+
+ const { result } = renderHookWithProvider(
+ () => useDomainMismatchAlert(),
+ mockExpectedState,
+ );
+ expect(result.current).toEqual([]);
+
+ mockSiwe.parsedMessage.domain = originalDomain;
+ });
+ });
+
+ it('returns an alert when the SIWE domain does not match the origin', () => {
+ const alertResponseExpected = {
+ field: 'requestFrom',
+ key: 'requestFrom',
+ message:
+ 'The site making the request is not the site you’re signing into. This could be an attempt to steal your login credentials.',
+ reason: 'Suspicious sign-in request',
+ severity: Severity.Danger,
+ };
+ const { result } = renderHookWithProvider(
+ () => useDomainMismatchAlert(),
+ mockExpectedState,
+ );
+
+ expect(result.current).toHaveLength(1);
+ expect(result.current[0]).toStrictEqual(alertResponseExpected);
+ });
+});
diff --git a/ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts b/ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts
new file mode 100644
index 000000000000..f75aa657ec2c
--- /dev/null
+++ b/ui/pages/confirmations/hooks/alerts/signatures/useDomainMismatchAlerts.ts
@@ -0,0 +1,45 @@
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import {
+ isValidSIWEOrigin,
+ WrappedSIWERequest,
+} from '@metamask/controller-utils';
+
+import { Alert } from '../../../../../ducks/confirm-alerts/confirm-alerts';
+import { Severity } from '../../../../../helpers/constants/design-system';
+import { useI18nContext } from '../../../../../hooks/useI18nContext';
+import { currentConfirmationSelector } from '../../../../../selectors';
+
+import { SignatureRequestType } from '../../../types/confirm';
+import { isSIWESignatureRequest } from '../../../utils';
+
+export default function useDomainMismatchAlerts(): Alert[] {
+ const t = useI18nContext();
+
+ const currentConfirmation = useSelector(
+ currentConfirmationSelector,
+ ) as SignatureRequestType;
+ const { msgParams } = currentConfirmation || {};
+
+ const isSIWE = isSIWESignatureRequest(currentConfirmation);
+ const isInvalidSIWEDomain =
+ isSIWE && !isValidSIWEOrigin(msgParams as WrappedSIWERequest);
+
+ const alerts = useMemo(() => {
+ if (!isInvalidSIWEDomain) {
+ return [];
+ }
+
+ return [
+ {
+ field: 'requestFrom',
+ key: 'requestFrom',
+ message: t('alertMessageSignInDomainMismatch'),
+ reason: t('alertReasonSignIn'),
+ severity: Severity.Danger,
+ },
+ ] as Alert[];
+ }, [isInvalidSIWEDomain, t]);
+
+ return alerts;
+}
diff --git a/ui/pages/confirmations/hooks/useConfirmationAlerts.ts b/ui/pages/confirmations/hooks/useConfirmationAlerts.ts
index 5bbfe2a721e2..9b93b285b871 100644
--- a/ui/pages/confirmations/hooks/useConfirmationAlerts.ts
+++ b/ui/pages/confirmations/hooks/useConfirmationAlerts.ts
@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { Alert } from '../../../ducks/confirm-alerts/confirm-alerts';
import useBlockaidAlerts from './alerts/useBlockaidAlerts';
+import useDomainMismatchAlerts from './alerts/signatures/useDomainMismatchAlerts';
import { useInsufficientBalanceAlerts } from './alerts/transactions/useInsufficientBalanceAlerts';
import { useGasEstimateFailedAlerts } from './alerts/transactions/useGasEstimateFailedAlerts';
import { usePendingTransactionAlerts } from './alerts/transactions/usePendingTransactionAlerts';
@@ -10,6 +11,12 @@ import { useGasTooLowAlerts } from './alerts/transactions/useGasTooLowAlerts';
import { useNoGasPriceAlerts } from './alerts/transactions/useNoGasPriceAlerts';
import { useNetworkBusyAlerts } from './alerts/transactions/useNetworkBusyAlerts';
+function useSignatureAlerts(): Alert[] {
+ const domainMismatchAlerts = useDomainMismatchAlerts();
+
+ return useMemo(() => [...domainMismatchAlerts], [domainMismatchAlerts]);
+}
+
function useTransactionAlerts(): Alert[] {
const gasEstimateFailedAlerts = useGasEstimateFailedAlerts();
const gasFeeLowAlerts = useGasFeeLowAlerts();
@@ -46,10 +53,11 @@ function useTransactionAlerts(): Alert[] {
export default function useConfirmationAlerts(): Alert[] {
const blockaidAlerts = useBlockaidAlerts();
+ const signatureAlerts = useSignatureAlerts();
const transactionAlerts = useTransactionAlerts();
return useMemo(
- () => [...blockaidAlerts, ...transactionAlerts],
- [blockaidAlerts, transactionAlerts],
+ () => [...blockaidAlerts, ...signatureAlerts, ...transactionAlerts],
+ [blockaidAlerts, signatureAlerts, transactionAlerts],
);
}
diff --git a/ui/pages/confirmations/hooks/useCurrentConfirmation.ts b/ui/pages/confirmations/hooks/useCurrentConfirmation.ts
index 77356e848cde..880eedca7399 100644
--- a/ui/pages/confirmations/hooks/useCurrentConfirmation.ts
+++ b/ui/pages/confirmations/hooks/useCurrentConfirmation.ts
@@ -63,6 +63,10 @@ const useCurrentConfirmation = () => {
if (
!redesignedConfirmationsEnabled ||
(!isCorrectTransactionType && !isCorrectApprovalType) ||
+ /**
+ * @todo remove isSIWE check when we want to enable SIWE in redesigned confirmations
+ * @see {@link https://github.com/MetaMask/metamask-extension/issues/24617}
+ */
isSIWE
) {
return { currentConfirmation: undefined };
diff --git a/ui/pages/confirmations/types/confirm.ts b/ui/pages/confirmations/types/confirm.ts
index 2050aae32c05..36a1b6a31397 100644
--- a/ui/pages/confirmations/types/confirm.ts
+++ b/ui/pages/confirmations/types/confirm.ts
@@ -1,4 +1,6 @@
import { ApprovalControllerState } from '@metamask/approval-controller';
+import { SIWEMessage } from '@metamask/controller-utils';
+
import {
TransactionMeta,
TransactionType,
@@ -27,21 +29,7 @@ export type SignatureRequestType = {
data: string | TypedSignDataV1Type;
version?: string;
signatureMethod?: string;
- siwe?: {
- isSIWEMessage: boolean;
- parsedMessage: null | {
- domain: string;
- address: string;
- statement: string;
- uri: string;
- version: string;
- chainId: number;
- nonce: string;
- issuedAt: string;
- requestId?: string;
- resources?: string[];
- };
- };
+ siwe?: SIWEMessage;
};
type: TransactionType;
custodyId?: string;
diff --git a/ui/pages/confirmations/utils/confirm.ts b/ui/pages/confirmations/utils/confirm.ts
index 3325f8dd81d1..73fa4450d3f5 100644
--- a/ui/pages/confirmations/utils/confirm.ts
+++ b/ui/pages/confirmations/utils/confirm.ts
@@ -46,7 +46,7 @@ export const parseSanitizeTypedDataMessage = (dataToParse: string) => {
};
export const isSIWESignatureRequest = (request: SignatureRequestType) =>
- request.msgParams?.siwe?.isSIWEMessage;
+ Boolean(request?.msgParams?.siwe?.isSIWEMessage);
export const isPermitSignatureRequest = (request: SignatureRequestType) => {
if (
From e01083d0273238fca4006cdb159c7034afbeffe2 Mon Sep 17 00:00:00 2001
From: Ariella Vu <20778143+digiwand@users.noreply.github.com>
Date: Thu, 27 Jun 2024 18:29:29 +0200
Subject: [PATCH 03/15] refactor: Confirm Alert Modal - no logic changes
(#25371)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
No logic changes; Only refactor / cleanup
- split Confirm footer button w/ condition `hasDangerAlerts`
- deprecate `inlineAlert` translation; replace with `alert` dupe
- rename variables
- confirmAlertModalAcknowledgeBlockaid →
confirmAlertModalAcknowledgeSingle
- confirmAlertModalAcknowledge → confirmAlertModalAcknowledgeMultiple
[](https://codespaces.new/MetaMask/metamask-extension/pull/25371?quickstart=1)
## **Related issues**
Fixes:
## **Manual testing steps**
1. Go to this page...
2.
3.
## **Screenshots/Recordings**
### **Before**
### **After**
## **Pre-merge author checklist**
- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
---
app/_locales/de/messages.json | 5 +--
app/_locales/el/messages.json | 5 +--
app/_locales/en/messages.json | 7 ++--
app/_locales/es/messages.json | 5 +--
app/_locales/fr/messages.json | 5 +--
app/_locales/hi/messages.json | 5 +--
app/_locales/id/messages.json | 5 +--
app/_locales/ja/messages.json | 5 +--
app/_locales/ko/messages.json | 5 +--
app/_locales/pt/messages.json | 5 +--
app/_locales/ru/messages.json | 5 +--
app/_locales/tl/messages.json | 5 +--
app/_locales/tr/messages.json | 5 +--
app/_locales/vi/messages.json | 5 +--
app/_locales/zh_CN/messages.json | 5 +--
.../confirm-alert-modal.tsx | 4 +--
.../__snapshots__/inline-alert.test.tsx.snap | 6 ++--
.../inline-alert/inline-alert.tsx | 2 +-
.../components/confirm/footer/footer.tsx | 34 +++++++++++++------
.../simulation-error-message.js | 1 +
20 files changed, 46 insertions(+), 78 deletions(-)
diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json
index 2500ff05f254..0160c032ed88 100644
--- a/app/_locales/de/messages.json
+++ b/app/_locales/de/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Bestätigen"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Ich habe die Benachrichtigungen zur Kenntnis genommen und möchte trotzdem fortfahren"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "Ihre erste Transaktion wurde vom Netzwerk bestätigt. Klicken Sie auf „OK“, um zurückzukehren."
},
- "inlineAlert": {
- "message": "Warnung"
- },
"inputLogicEmptyState": {
"message": "Geben Sie nur eine Nummer ein, die Sie den Drittanbieter jetzt oder in Zukunft ausgeben lassen möchten. Sie können die Ausgabenobergrenze später jederzeit ändern."
},
diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json
index bff686407193..a53b48a7be40 100644
--- a/app/_locales/el/messages.json
+++ b/app/_locales/el/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Επιβεβαίωση"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Έχω ενημερωθεί για τις ειδοποιήσεις και εξακολουθώ να θέλω να συνεχίσω"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "Η αρχική σας συναλλαγή επιβεβαιώθηκε από το δίκτυο. Κάντε κλικ στο OK για να επιστρέψετε."
},
- "inlineAlert": {
- "message": "Ειδοποίηση"
- },
"inputLogicEmptyState": {
"message": "Πληκτρολογήστε μόνο έναν αριθμό που σας βολεύει να ξοδέψει ο τρίτος τώρα ή στο μέλλον. Μπορείτε πάντα να αυξήσετε το ανώτατο όριο δαπανών αργότερα."
},
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index d59f9d572f6c..6590fea16133 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -951,10 +951,10 @@
"confirm": {
"message": "Confirm"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "I have acknowledged the alerts and still want to proceed"
},
- "confirmAlertModalAcknowledgeBlockaid": {
+ "confirmAlertModalAcknowledgeSingle": {
"message": "I have acknowledged the alert and still want to proceed"
},
"confirmAlertModalDetails": {
@@ -2301,9 +2301,6 @@
"initialTransactionConfirmed": {
"message": "Your initial transaction was confirmed by the network. Click OK to go back."
},
- "inlineAlert": {
- "message": "Alert"
- },
"inputLogicEmptyState": {
"message": "Only enter a number that you're comfortable with the third party spending now or in the future. You can always increase the spending cap later."
},
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index 6a70bf40e68f..244c70313634 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "Confirmar"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Soy consciente de las alertas y aun así deseo continuar"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "La red confirmó la transacción inicial. Haga clic en Aceptar para volver."
},
- "inlineAlert": {
- "message": "Alerta"
- },
"inputLogicEmptyState": {
"message": "Ingrese solo una cantidad que esté dispuesto a gastar en el tercero ahora o en el futuro. Siempre puede aumentar el límite de gastos más adelante."
},
diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json
index 0cd50d4788f8..1b2a54f3c4c1 100644
--- a/app/_locales/fr/messages.json
+++ b/app/_locales/fr/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Confirmer"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "J’ai pris connaissance des alertes, mais je souhaite quand même continuer"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "Votre transaction initiale a été confirmée par le réseau. Cliquez sur OK pour retourner à l’écran précédent."
},
- "inlineAlert": {
- "message": "Alerte"
- },
"inputLogicEmptyState": {
"message": "N'entrez qu'une somme que vous pouvez accepter que le tiers dépense aujourd'hui ou à l'avenir. Vous pourrez toujours augmenter le plafond de dépenses ultérieurement."
},
diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json
index 5f83ad80bfdf..dc6318004958 100644
--- a/app/_locales/hi/messages.json
+++ b/app/_locales/hi/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "कन्फर्म करें"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "मैंने एलर्ट को स्वीकार कर लिया है और इसके बावजूद आगे बढ़ना चाहता/चाहती हूं"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "नेटवर्क द्वारा आपके प्रारंभिक ट्रांसेक्शन को कन्फर्म किया गया था। वापस जाने के लिए ठीक पर क्लिक करें।"
},
- "inlineAlert": {
- "message": "एलर्ट"
- },
"inputLogicEmptyState": {
"message": "केवल वही संख्या डालें जो आप अभी या भविष्य में थर्ड पार्टी खर्च के साथ सहज महसूस करते हैं। आप बाद में कभी भी खर्च करने की लिमिट बढ़ा सकते हैं।"
},
diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json
index 45073d2af42b..0faef246fd39 100644
--- a/app/_locales/id/messages.json
+++ b/app/_locales/id/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Konfirmasikan"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Saya telah mengetahui peringatannya dan tetap ingin melanjutkan"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "Transaksi awal Anda dikonfirmasikan oleh jaringan. Klik Oke untuk kembali."
},
- "inlineAlert": {
- "message": "Peringatan"
- },
"inputLogicEmptyState": {
"message": "Masukkan angka yang menurut Anda dapat digunakan pihak ketiga sekarang atau di masa mendatang. Anda selalu dapat meningkatkan batas penggunaan nantinya."
},
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index ad69880ffd8e..4308665d7bcb 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "確認"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "アラートを確認したうえで続行します"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "最初のトランザクションはネットワークによって承認されました。戻るには「OK」をクリックします。"
},
- "inlineAlert": {
- "message": "アラート"
- },
"inputLogicEmptyState": {
"message": "現在または今後サードパーティが使用しても構わない額のみを入力してください。使用上限は後でいつでも増額できます。"
},
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index 9121a9b84281..464c3bf9e250 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "컨펌"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "경고를 인지했으며, 계속 진행합니다"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "최초 트랜잭션을 네트워크에서 컨펌했습니다. 돌아가려면 컨펌을 클릭하세요."
},
- "inlineAlert": {
- "message": "경고"
- },
"inputLogicEmptyState": {
"message": "타사에서 현재나 추후 지출하기에 무리가 없는 금액만 입력하세요. 지출 한도는 나중에 언제든지 상향할 수 있습니다."
},
diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json
index e83e1b6d4eea..f201f475a4ec 100644
--- a/app/_locales/pt/messages.json
+++ b/app/_locales/pt/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Confirmar"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Confirmo que recebi os alertas e ainda quero prosseguir"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "Sua transação inicial foi confirmada pela rede. Clique em OK para voltar."
},
- "inlineAlert": {
- "message": "Alerta"
- },
"inputLogicEmptyState": {
"message": "Somente insira um número com o qual esteja confortável de o terceiro gastar agora ou no futuro. Você pode aumentar o limite de gastos a qualquer momento."
},
diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json
index cb41df257fe1..3c419a274034 100644
--- a/app/_locales/ru/messages.json
+++ b/app/_locales/ru/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Подтвердить"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Я подтвердил(-а) получение оповещений и все еще хочу продолжить"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "Ваша первоначальная транзакция подтверждена сетью. Нажмите ОК, чтобы вернуться."
},
- "inlineAlert": {
- "message": "Оповещение"
- },
"inputLogicEmptyState": {
"message": "Введите только ту сумму, которую третья сторона, по вашему мнению, может тратить сейчас или в будущем. Вы всегда можете увеличить лимит расходов позже."
},
diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json
index cbd81acfcbc6..b6d454c012af 100644
--- a/app/_locales/tl/messages.json
+++ b/app/_locales/tl/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "Kumpirmahin"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Kinikilala ko ang mga alerto at nais ko pa rin magpatuloy"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "Nakumpirma na ng network ang iyong inisyal na transaksyon. I-click ang OK para bumalik."
},
- "inlineAlert": {
- "message": "Alerto"
- },
"inputLogicEmptyState": {
"message": "Maglagay lamang ng numero na komportable ka sa paggastos ng third party ngayon o sa hinaharap. Maaari mong palaging taasan ang limitasyon sa paggastos sa ibang pagkakataon."
},
diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json
index 13887164bfd1..cd6508892738 100644
--- a/app/_locales/tr/messages.json
+++ b/app/_locales/tr/messages.json
@@ -838,7 +838,7 @@
"confirm": {
"message": "Onayla"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Uyarıları kabul ediyorum ve yine de ilerlemek istiyorum"
},
"confirmAlertModalDetails": {
@@ -2122,9 +2122,6 @@
"initialTransactionConfirmed": {
"message": "İlk işleminiz ağ tarafından onaylanmıştır. Geri gitmek için Tamam düğmesine tıklayın."
},
- "inlineAlert": {
- "message": "Uyarı"
- },
"inputLogicEmptyState": {
"message": "Sadece şu anda ya da gelecekte üçüncü taraf harcaması konusunda rahat olduğunuz bir sayı girin. Harcama üst limitini daha sonra dilediğiniz zaman artırabilirsiniz."
},
diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json
index 3afd6bf2403e..ffc867cf6e76 100644
--- a/app/_locales/vi/messages.json
+++ b/app/_locales/vi/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "Xác nhận"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "Tôi đã hiểu rõ các cảnh báo và vẫn muốn tiếp tục"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "Mạng đã xác nhận giao dịch ban đầu của bạn. Nhấp OK để quay lại."
},
- "inlineAlert": {
- "message": "Cảnh báo"
- },
"inputLogicEmptyState": {
"message": "Chỉ nhập số mà bạn cảm thấy thoải mái đối với hạn mức chi tiêu ở hiện tại hoặc trong tương lai của bên thứ ba. Bạn luôn có thể tăng hạn mức chi tiêu sau này."
},
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index 6cdb6aaf1e3f..14c8e355be1f 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -835,7 +835,7 @@
"confirm": {
"message": "确认"
},
- "confirmAlertModalAcknowledge": {
+ "confirmAlertModalAcknowledgeMultiple": {
"message": "我已知晓提醒并仍想继续"
},
"confirmAlertModalDetails": {
@@ -2119,9 +2119,6 @@
"initialTransactionConfirmed": {
"message": "您的初始交易已被网络确认。请点击“确定”返回。"
},
- "inlineAlert": {
- "message": "提醒"
- },
"inputLogicEmptyState": {
"message": "仅需输入一个您觉得比较恰当的现在或将来第三方支出的数字。以后您可以随时提高支出上限。"
},
diff --git a/ui/components/app/alert-system/confirm-alert-modal/confirm-alert-modal.tsx b/ui/components/app/alert-system/confirm-alert-modal/confirm-alert-modal.tsx
index c9647cedf633..0c4165182b18 100644
--- a/ui/components/app/alert-system/confirm-alert-modal/confirm-alert-modal.tsx
+++ b/ui/components/app/alert-system/confirm-alert-modal/confirm-alert-modal.tsx
@@ -180,8 +180,8 @@ export function ConfirmAlertModal({
onCheckboxClick={handleConfirmCheckbox}
label={
selectedAlert?.provider === SecurityProvider.Blockaid
- ? t('confirmAlertModalAcknowledgeBlockaid')
- : t('confirmAlertModalAcknowledge')
+ ? t('confirmAlertModalAcknowledgeSingle')
+ : t('confirmAlertModalAcknowledgeMultiple')
}
/>
}
diff --git a/ui/components/app/alert-system/inline-alert/__snapshots__/inline-alert.test.tsx.snap b/ui/components/app/alert-system/inline-alert/__snapshots__/inline-alert.test.tsx.snap
index ef56b906038f..d869e2f978fa 100644
--- a/ui/components/app/alert-system/inline-alert/__snapshots__/inline-alert.test.tsx.snap
+++ b/ui/components/app/alert-system/inline-alert/__snapshots__/inline-alert.test.tsx.snap
@@ -16,7 +16,7 @@ exports[`Inline Alert renders alert with danger severity 1`] = `
- [inlineAlert]
+ [alert]
- [inlineAlert]
+ [alert]
- [inlineAlert]
+ [alert]
- {t('inlineAlert')}
+ {t('alert')}
diff --git a/ui/pages/confirmations/components/confirm/footer/footer.tsx b/ui/pages/confirmations/components/confirm/footer/footer.tsx
index 6d48f42ae420..a3d3e1bbc71d 100644
--- a/ui/pages/confirmations/components/confirm/footer/footer.tsx
+++ b/ui/pages/confirmations/components/confirm/footer/footer.tsx
@@ -65,17 +65,29 @@ const ConfirmButton = ({
onSubmit={onSubmit}
/>
)}
-
+ {hasDangerAlerts ? (
+
+ ) : (
+
+ )}
>
);
};
diff --git a/ui/pages/confirmations/components/simulation-error-message/simulation-error-message.js b/ui/pages/confirmations/components/simulation-error-message/simulation-error-message.js
index a80bd4b6a159..0edd91ad42e0 100644
--- a/ui/pages/confirmations/components/simulation-error-message/simulation-error-message.js
+++ b/ui/pages/confirmations/components/simulation-error-message/simulation-error-message.js
@@ -29,6 +29,7 @@ export default function SimulationErrorMessage({
],
},
});
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return userAcknowledgedGasMissing === true ? (
From 1d33d6f39a499aa6c82c4f97fc288875a55bec30 Mon Sep 17 00:00:00 2001
From: seaona <54408225+seaona@users.noreply.github.com>
Date: Thu, 27 Jun 2024 18:48:25 +0200
Subject: [PATCH 04/15] fix: flaky test `Swap-Send ETH to non-contract address
with data that matches swap data signature submits a transaction
successfully` (#25545)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
The problem is that we are looking for a pending transaction and then a
confirmed transaction. If the transaction is confirmed very fast, the
pending transaction is never found, thus making the test fail.
This is a bad pattern that introduces flakiness, as we are trying to
find an element by its transient state, which is variable. What we
really want to assert is that the transaction is in the end confirmed,
so we shouldn't try to look for the pending transaction (something that
disappears fast) in the first place.
Error:
```
TimeoutError: Waiting for element to be located By(css selector, .transaction-status-label--pending)
Wait timed out after 10081ms
```
[ci
error](https://app.circleci.com/pipelines/github/MetaMask/metamask-extension/89418/workflows/924ea8b8-1698-4dca-ae6e-b724264fe4a2/jobs/3304211/parallel-runs/17?filterBy=ALL)
[](https://codespaces.new/MetaMask/metamask-extension/pull/25545?quickstart=1)
## **Related issues**
Fixes: https://github.com/MetaMask/metamask-extension/issues/25546
## **Manual testing steps**
1. Check ci
## **Screenshots/Recordings**
See the screenshot in ci, how the expected transaction is indeed there
(confirmed). However, on the error we couldn't locate the element when
it was pending (possibly due to being really fast at occasions)


## **Pre-merge author checklist**
- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
---
test/e2e/tests/swap-send/swap-send-eth.spec.ts | 6 ------
1 file changed, 6 deletions(-)
diff --git a/test/e2e/tests/swap-send/swap-send-eth.spec.ts b/test/e2e/tests/swap-send/swap-send-eth.spec.ts
index 244693d513a2..e4be22ae1fa3 100644
--- a/test/e2e/tests/swap-send/swap-send-eth.spec.ts
+++ b/test/e2e/tests/swap-send/swap-send-eth.spec.ts
@@ -74,12 +74,6 @@ describe('Swap-Send ETH', function () {
// TODO assert swap api request payload
await swapSendPage.submitSwap();
- await swapSendPage.verifyHistoryEntry(
- 'Send ETH as TST',
- 'Pending',
- '-1 ETH',
- '',
- );
await swapSendPage.verifyHistoryEntry(
'Send ETH as TST',
'Confirmed',
From 574f81906c29e3caf251d6412d88705e7f319d00 Mon Sep 17 00:00:00 2001
From: David Murdoch <187813+davidmurdoch@users.noreply.github.com>
Date: Thu, 27 Jun 2024 17:04:22 -0400
Subject: [PATCH 05/15] chore: update `chokidar` to `v3.6.0` (#25571)
Some old versions are old so we might as well update!
---
lavamoat/build-system/policy.json | 1561 ++++++++++++-----------------
package.json | 3 +-
yarn.lock | 353 +------
3 files changed, 633 insertions(+), 1284 deletions(-)
diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json
index 02078b5d39fc..a01cf9c55765 100644
--- a/lavamoat/build-system/policy.json
+++ b/lavamoat/build-system/policy.json
@@ -2124,8 +2124,8 @@
},
"chokidar>anymatch": {
"packages": {
- "chokidar>anymatch>picomatch": true,
- "chokidar>normalize-path": true
+ "chokidar>anymatch>normalize-path": true,
+ "chokidar>anymatch>picomatch": true
}
},
"chokidar>anymatch>picomatch": {
@@ -3945,8 +3945,8 @@
},
"gulp-sourcemaps>@gulp-sourcemaps/identity-map": {
"packages": {
- "chokidar>normalize-path": true,
"gulp-sourcemaps>@gulp-sourcemaps/identity-map>acorn": true,
+ "gulp-sourcemaps>@gulp-sourcemaps/identity-map>normalize-path": true,
"gulp-sourcemaps>@gulp-sourcemaps/identity-map>postcss": true,
"gulp-sourcemaps>@gulp-sourcemaps/identity-map>source-map": true,
"gulp-sourcemaps>@gulp-sourcemaps/identity-map>through2": true
@@ -3997,13 +3997,8 @@
},
"gulp-sourcemaps>@gulp-sourcemaps/map-sources": {
"packages": {
- "gulp-sourcemaps>@gulp-sourcemaps/map-sources>normalize-path": true,
- "gulp-sourcemaps>@gulp-sourcemaps/map-sources>through2": true
- }
- },
- "gulp-sourcemaps>@gulp-sourcemaps/map-sources>normalize-path": {
- "packages": {
- "vinyl>remove-trailing-separator": true
+ "gulp-sourcemaps>@gulp-sourcemaps/map-sources>through2": true,
+ "gulp-watch>anymatch>normalize-path": true
}
},
"gulp-sourcemaps>@gulp-sourcemaps/map-sources>through2": {
@@ -4254,10 +4249,10 @@
"setTimeout": true
},
"packages": {
+ "chokidar": true,
"eslint>glob-parent": true,
"gulp-watch>ansi-colors": true,
"gulp-watch>anymatch": true,
- "gulp-watch>chokidar": true,
"gulp-watch>fancy-log": true,
"gulp-watch>path-is-absolute": true,
"gulp-watch>readable-stream": true,
@@ -4314,7 +4309,7 @@
"packages": {
"gulp-watch>anymatch>micromatch>braces>expand-range": true,
"gulp-watch>anymatch>micromatch>braces>preserve": true,
- "gulp-watch>chokidar>braces>repeat-element": true
+ "gulp-watch>anymatch>micromatch>braces>repeat-element": true
}
},
"gulp-watch>anymatch>micromatch>braces>expand-range": {
@@ -4327,7 +4322,7 @@
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>is-number": true,
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>isobject": true,
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>randomatic": true,
- "gulp-watch>chokidar>braces>repeat-element": true,
+ "gulp-watch>anymatch>micromatch>braces>repeat-element": true,
"stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
}
},
@@ -4455,8 +4450,6 @@
},
"packages": {
"chokidar>normalize-path": true,
- "del>is-glob": true,
- "eslint>glob-parent": true,
"eslint>is-glob": true,
"gulp-watch>chokidar>anymatch": true,
"gulp-watch>chokidar>async-each": true,
@@ -4470,750 +4463,234 @@
"pumpify>inherits": true
}
},
- "gulp-watch>chokidar>anymatch": {
+ "gulp-watch>chokidar>fsevents": {
"builtin": {
- "path.sep": true
+ "events.EventEmitter": true,
+ "fs.stat": true,
+ "path.join": true,
+ "util.inherits": true
+ },
+ "globals": {
+ "__dirname": true,
+ "console.assert": true,
+ "process.nextTick": true,
+ "process.platform": true,
+ "setImmediate": true
},
"packages": {
- "gulp-watch>chokidar>anymatch>micromatch": true,
- "gulp-watch>chokidar>anymatch>normalize-path": true
+ "gulp-watch>chokidar>fsevents>node-pre-gyp": true
}
},
- "gulp-watch>chokidar>anymatch>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": {
- "@babel/register>clone-deep>kind-of": true,
- "gulp-watch>chokidar>anymatch>micromatch>define-property": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob": true,
- "gulp-watch>chokidar>braces": true,
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp-zip>plugin-error>arr-diff": true,
- "gulp-zip>plugin-error>extend-shallow": true,
- "gulp>gulp-cli>liftoff>fined>object.pick": true,
- "gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
- "gulp>gulp-cli>matchdep>micromatch>nanomatch": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>define-property": {
- "packages": {
- "gulp>gulp-cli>isobject": true,
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>extglob": {
- "packages": {
- "gulp-watch>chokidar>anymatch>micromatch>extglob>define-property": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob>extend-shallow": true,
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>extglob>define-property": {
- "packages": {
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
+ "@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog": 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>rimraf": true,
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>semver": true
}
},
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets": {
- "globals": {
- "__filename": true
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>detect-libc": {
+ "builtin": {
+ "child_process.spawnSync": true,
+ "fs.readdirSync": true,
+ "os.platform": true
},
- "packages": {
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>debug": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>extend-shallow": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp>gulp-cli>matchdep>micromatch>extglob>expand-brackets>posix-character-classes": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
+ "globals": {
+ "process.env": true
}
},
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>debug": {
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt": {
"builtin": {
- "fs.SyncWriteStream": true,
- "net.Socket": true,
- "tty.WriteStream": true,
- "tty.isatty": true,
- "util": true
+ "path": true,
+ "stream.Stream": true,
+ "url": true
},
"globals": {
- "chrome": true,
"console": true,
- "document": true,
- "localStorage": true,
- "navigator": true,
- "process": true
+ "process.argv": true,
+ "process.env.DEBUG_NOPT": true,
+ "process.env.NOPT_DEBUG": true,
+ "process.platform": true
},
"packages": {
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>debug>ms": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property": {
- "packages": {
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor": {
- "packages": {
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": true,
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>kind-of": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": {
- "packages": {
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor>kind-of": true
- }
- },
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
+ "@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>nopt>abbrev": true,
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv": true
}
},
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": {
+ "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>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": true
+ "@storybook/core>@storybook/core-server>x-default-browser>default-browser-id>untildify>os-homedir": true,
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": true
}
},
- "gulp-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
+ "gulp-watch>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-watch>chokidar>anymatch>micromatch>extglob>expand-brackets>extend-shallow": {
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>rimraf": {
+ "builtin": {
+ "assert": true,
+ "fs": true,
+ "path.join": true
+ },
+ "globals": {
+ "process.platform": true,
+ "setTimeout": true
+ },
"packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ "nyc>glob": true
}
},
- "gulp-watch>chokidar>anymatch>micromatch>extglob>extend-shallow": {
- "packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ "gulp-watch>chokidar>fsevents>node-pre-gyp>semver": {
+ "globals": {
+ "console": true,
+ "process": true
}
},
- "gulp-watch>chokidar>anymatch>normalize-path": {
+ "gulp-watch>fancy-log": {
+ "globals": {
+ "console": true,
+ "process.argv.indexOf": true,
+ "process.stderr.write": true,
+ "process.stdout.write": true
+ },
"packages": {
- "vinyl>remove-trailing-separator": true
+ "fancy-log>ansi-gray": true,
+ "fancy-log>color-support": true,
+ "fancy-log>time-stamp": true
}
},
- "gulp-watch>chokidar>async-each": {
+ "gulp-watch>path-is-absolute": {
"globals": {
- "define": true
- }
- },
- "gulp-watch>chokidar>braces": {
- "packages": {
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>extend-shallow": true,
- "gulp-watch>chokidar>braces>fill-range": true,
- "gulp-watch>chokidar>braces>repeat-element": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>snapdragon-node": true,
- "gulp-watch>chokidar>braces>split-string": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp>gulp-cli>isobject": true,
- "gulp>undertaker>arr-flatten": true
+ "process.platform": true
}
},
- "gulp-watch>chokidar>braces>extend-shallow": {
+ "gulp-watch>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>object.omit>is-extendable": true
+ "gulp-watch>readable-stream>isarray": true,
+ "gulp-watch>readable-stream>safe-buffer": true,
+ "gulp-watch>readable-stream>string_decoder": true,
+ "pumpify>inherits": true,
+ "readable-stream-2>core-util-is": true,
+ "readable-stream-2>process-nextick-args": true,
+ "readable-stream>util-deprecate": true
}
},
- "gulp-watch>chokidar>braces>fill-range": {
+ "gulp-watch>readable-stream>safe-buffer": {
"builtin": {
- "util.inspect": true
- },
- "packages": {
- "gulp-watch>chokidar>braces>fill-range>extend-shallow": true,
- "gulp-watch>chokidar>braces>fill-range>is-number": true,
- "gulp-watch>chokidar>braces>fill-range>to-regex-range": true,
- "stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
+ "buffer": true
}
},
- "gulp-watch>chokidar>braces>fill-range>extend-shallow": {
+ "gulp-watch>readable-stream>string_decoder": {
"packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ "gulp-watch>readable-stream>safe-buffer": true
}
},
- "gulp-watch>chokidar>braces>fill-range>is-number": {
+ "gulp-watch>vinyl-file": {
+ "builtin": {
+ "path.resolve": true
+ },
+ "globals": {
+ "process.cwd": true
+ },
"packages": {
- "gulp-watch>chokidar>braces>fill-range>is-number>kind-of": true
+ "del>graceful-fs": true,
+ "gh-pages>globby>pinkie-promise": 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>braces>fill-range>is-number>kind-of": {
+ "gulp-watch>vinyl-file>strip-bom": {
+ "globals": {
+ "Buffer.isBuffer": true
+ },
"packages": {
- "browserify>insert-module-globals>is-buffer": true
+ "gulp>vinyl-fs>remove-bom-buffer>is-utf8": true
}
},
- "gulp-watch>chokidar>braces>fill-range>to-regex-range": {
+ "gulp-watch>vinyl-file>strip-bom-stream": {
"packages": {
- "gulp-watch>chokidar>braces>fill-range>is-number": true,
- "stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
+ "gulp-watch>vinyl-file>strip-bom": true,
+ "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream": true
}
},
- "gulp-watch>chokidar>braces>snapdragon": {
+ "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream": {
"builtin": {
- "fs.readFileSync": true,
- "path.dirname": true,
- "util.inspect": true
+ "util.inherits": true
},
"globals": {
- "__filename": true
+ "Buffer.concat": true,
+ "setImmediate": true
},
"packages": {
- "gulp-watch>chokidar>braces>snapdragon>base": true,
- "gulp-watch>chokidar>braces>snapdragon>debug": true,
- "gulp-watch>chokidar>braces>snapdragon>define-property": true,
- "gulp-watch>chokidar>braces>snapdragon>extend-shallow": true,
- "gulp-watch>chokidar>braces>snapdragon>map-cache": true,
- "gulp-watch>chokidar>braces>snapdragon>source-map": true,
- "gulp-watch>chokidar>braces>snapdragon>use": true,
- "resolve-url-loader>rework>css>source-map-resolve": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon-node": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon-node>define-property": true,
- "gulp-watch>chokidar>braces>snapdragon-node>snapdragon-util": true,
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon-node>define-property": {
- "packages": {
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon-node>snapdragon-util": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon-node>snapdragon-util>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon-node>snapdragon-util>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base": {
- "builtin": {
- "util.inherits": true
- },
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base": true,
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils": true,
- "gulp-watch>chokidar>braces>snapdragon>base>component-emitter": true,
- "gulp-watch>chokidar>braces>snapdragon>base>define-property": true,
- "gulp-watch>chokidar>braces>snapdragon>base>mixin-deep": true,
- "gulp-watch>chokidar>braces>snapdragon>base>pascalcase": true,
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>set-value": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>to-object-path": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>union-value": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value": true,
- "gulp-watch>chokidar>braces>snapdragon>base>component-emitter": true,
- "gulp>gulp-cli>array-sort>get-value": true,
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit>map-visit": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit>object-visit": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit>map-visit": {
- "builtin": {
- "util.inspect": true
- },
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit>object-visit": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>collection-visit>object-visit": {
- "packages": {
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values": true,
- "gulp>gulp-cli>array-sort>get-value": true,
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>set-value": {
- "packages": {
- "@babel/register>clone-deep>is-plain-object": true,
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>set-value>extend-shallow": true,
- "gulp-watch>chokidar>braces>split-string": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>set-value>extend-shallow": {
- "packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>to-object-path": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>to-object-path>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>to-object-path>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>union-value": {
- "packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>set-value": true,
- "gulp-zip>plugin-error>arr-union": true,
- "gulp>gulp-cli>array-sort>get-value": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value>has-value": true,
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value>has-value": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value>has-value>has-values": true,
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value>has-value>isobject": true,
- "gulp>gulp-cli>array-sort>get-value": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value>has-value>isobject": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>cache-base>unset-value>has-value>isobject>isarray": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils": {
- "builtin": {
- "util": true
- },
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend": true,
- "gulp-watch>chokidar>braces>snapdragon>define-property": true,
- "gulp-zip>plugin-error>arr-union": true,
- "gulp>gulp-cli>isobject": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend": {
- "builtin": {
- "util.inherits": true
- },
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend>object-copy": true,
- "gulp-watch>chokidar>braces>snapdragon>define-property": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend>object-copy": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend>object-copy>copy-descriptor": true,
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend>object-copy>kind-of": true,
- "gulp-watch>chokidar>braces>snapdragon>define-property": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>class-utils>static-extend>object-copy>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>define-property": {
- "packages": {
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>mixin-deep": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>base>mixin-deep>is-extendable": true,
- "gulp>undertaker>object.reduce>for-own>for-in": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>base>mixin-deep>is-extendable": {
- "packages": {
- "@babel/register>clone-deep>is-plain-object": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>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>braces>snapdragon>debug>ms": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>define-property": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-accessor-descriptor": true,
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-data-descriptor": true,
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-accessor-descriptor": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-data-descriptor": {
- "packages": {
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": {
- "packages": {
- "browserify>insert-module-globals>is-buffer": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>extend-shallow": {
- "packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
- }
- },
- "gulp-watch>chokidar>braces>snapdragon>use": {
- "packages": {
- "@babel/register>clone-deep>kind-of": true
- }
- },
- "gulp-watch>chokidar>braces>split-string": {
- "packages": {
- "gulp-zip>plugin-error>extend-shallow": true
- }
- },
- "gulp-watch>chokidar>braces>to-regex": {
- "packages": {
- "gulp-watch>chokidar>braces>to-regex>define-property": true,
- "gulp-watch>chokidar>braces>to-regex>safe-regex": true,
- "gulp-zip>plugin-error>extend-shallow": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
- }
- },
- "gulp-watch>chokidar>braces>to-regex>define-property": {
- "packages": {
- "gulp>gulp-cli>isobject": true,
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
- }
- },
- "gulp-watch>chokidar>braces>to-regex>safe-regex": {
- "packages": {
- "gulp-watch>chokidar>braces>to-regex>safe-regex>ret": true
- }
- },
- "gulp-watch>chokidar>fsevents": {
- "builtin": {
- "events.EventEmitter": true,
- "fs.stat": true,
- "path.join": true,
- "util.inherits": true
- },
- "globals": {
- "__dirname": true,
- "console.assert": true,
- "process.nextTick": true,
- "process.platform": true,
- "setImmediate": true
- },
- "packages": {
- "gulp-watch>chokidar>fsevents>node-pre-gyp": true
- }
- },
- "gulp-watch>chokidar>fsevents>node-pre-gyp": {
- "builtin": {
- "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": {
- "__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": {
- "@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>npmlog": 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>rimraf": true,
- "gulp-watch>chokidar>fsevents>node-pre-gyp>semver": 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>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": {
- "@lavamoat/allow-scripts>@npmcli/run-script>node-gyp>nopt>abbrev": true,
- "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv": true
- }
- },
- "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": {
- "@storybook/core>@storybook/core-server>x-default-browser>default-browser-id>untildify>os-homedir": true,
- "gulp-watch>chokidar>fsevents>node-pre-gyp>nopt>osenv>os-tmpdir": true
- }
- },
- "gulp-watch>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-watch>chokidar>fsevents>node-pre-gyp>rimraf": {
- "builtin": {
- "assert": true,
- "fs": true,
- "path.join": true
- },
- "globals": {
- "process.platform": true,
- "setTimeout": true
- },
- "packages": {
- "nyc>glob": true
- }
- },
- "gulp-watch>chokidar>fsevents>node-pre-gyp>semver": {
- "globals": {
- "console": true,
- "process": 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": {
- "del>graceful-fs": true,
- "gulp-watch>chokidar>anymatch>micromatch": true,
- "gulp-watch>readable-stream": 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>path-is-absolute": {
- "globals": {
- "process.platform": true
- }
- },
- "gulp-watch>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>readable-stream>isarray": true,
- "gulp-watch>readable-stream>safe-buffer": true,
- "gulp-watch>readable-stream>string_decoder": true,
- "pumpify>inherits": true,
- "readable-stream-2>core-util-is": true,
- "readable-stream-2>process-nextick-args": true,
- "readable-stream>util-deprecate": true
- }
- },
- "gulp-watch>readable-stream>safe-buffer": {
- "builtin": {
- "buffer": true
- }
- },
- "gulp-watch>readable-stream>string_decoder": {
- "packages": {
- "gulp-watch>readable-stream>safe-buffer": true
- }
- },
- "gulp-watch>vinyl-file": {
- "builtin": {
- "path.resolve": true
- },
- "globals": {
- "process.cwd": true
- },
- "packages": {
- "del>graceful-fs": true,
- "gh-pages>globby>pinkie-promise": 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": {
- "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream>readable-stream": true
+ "gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream>readable-stream": true
}
},
"gulp-watch>vinyl-file>strip-bom-stream>first-chunk-stream>readable-stream": {
@@ -5368,9 +4845,9 @@
},
"gulp>glob-watcher": {
"packages": {
+ "chokidar": true,
"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
@@ -5381,390 +4858,585 @@
"path.sep": true
},
"packages": {
- "gulp>glob-watcher>anymatch>micromatch": true,
- "gulp>glob-watcher>anymatch>normalize-path": true
+ "gulp-watch>anymatch>normalize-path": true,
+ "gulp>glob-watcher>anymatch>micromatch": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch": {
+ "builtin": {
+ "path.basename": true,
+ "path.sep": true,
+ "util.inspect": true
+ },
+ "globals": {
+ "process.platform": true
+ },
+ "packages": {
+ "@babel/register>clone-deep>kind-of": true,
+ "gulp-zip>plugin-error>arr-diff": true,
+ "gulp-zip>plugin-error>extend-shallow": true,
+ "gulp>glob-watcher>anymatch>micromatch>braces": true,
+ "gulp>glob-watcher>anymatch>micromatch>define-property": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob": true,
+ "gulp>gulp-cli>liftoff>fined>object.pick": true,
+ "gulp>gulp-cli>matchdep>micromatch>array-unique": true,
+ "gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
+ "gulp>gulp-cli>matchdep>micromatch>nanomatch": true,
+ "gulp>gulp-cli>matchdep>micromatch>regex-not": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces": {
+ "packages": {
+ "gulp-watch>anymatch>micromatch>braces>repeat-element": true,
+ "gulp>glob-watcher>anymatch>micromatch>braces>extend-shallow": true,
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range": true,
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>array-unique": true,
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node": true,
+ "gulp>gulp-cli>matchdep>micromatch>braces>split-string": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex": true,
+ "gulp>undertaker>arr-flatten": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces>extend-shallow": {
+ "packages": {
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range": {
+ "builtin": {
+ "util.inspect": true
+ },
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>extend-shallow": true,
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>is-number": true,
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>to-regex-range": true,
+ "stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>extend-shallow": {
+ "packages": {
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>is-number": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>is-number>kind-of": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>is-number>kind-of": {
+ "packages": {
+ "browserify>insert-module-globals>is-buffer": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>to-regex-range": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>braces>fill-range>is-number": true,
+ "stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>define-property": {
+ "packages": {
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>extglob>define-property": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob>extend-shallow": true,
+ "gulp>gulp-cli>matchdep>micromatch>array-unique": true,
+ "gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
+ "gulp>gulp-cli>matchdep>micromatch>regex-not": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>define-property": {
+ "packages": {
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets": {
+ "globals": {
+ "__filename": true
+ },
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>debug": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>extend-shallow": true,
+ "gulp>gulp-cli>matchdep>micromatch>extglob>expand-brackets>posix-character-classes": true,
+ "gulp>gulp-cli>matchdep>micromatch>regex-not": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>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>glob-watcher>anymatch>micromatch>extglob>expand-brackets>debug>ms": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": true,
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>kind-of": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor>kind-of": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor>kind-of": {
+ "packages": {
+ "browserify>insert-module-globals>is-buffer": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": {
+ "packages": {
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": {
+ "packages": {
+ "browserify>insert-module-globals>is-buffer": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>extend-shallow": {
+ "packages": {
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ }
+ },
+ "gulp>glob-watcher>anymatch>micromatch>extglob>extend-shallow": {
+ "packages": {
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ }
+ },
+ "gulp>glob-watcher>async-done": {
+ "globals": {
+ "process.nextTick": true
+ },
+ "packages": {
+ "@metamask/object-multiplex>once": true,
+ "duplexify>end-of-stream": true,
+ "gulp>glob-watcher>async-done>stream-exhaust": true,
+ "readable-stream-2>process-nextick-args": true
+ }
+ },
+ "gulp>glob-watcher>async-done>stream-exhaust": {
+ "builtin": {
+ "stream.Writable": true,
+ "util.inherits": true
+ },
+ "globals": {
+ "setImmediate": true
+ }
+ },
+ "gulp>glob-watcher>chokidar": {
+ "packages": {
+ "gulp>glob-watcher>chokidar>fsevents": true
+ }
+ },
+ "gulp>glob-watcher>chokidar>fsevents": {
+ "builtin": {
+ "events.EventEmitter": true,
+ "fs.stat": true,
+ "path.join": true,
+ "util.inherits": true
+ },
+ "globals": {
+ "__dirname": true,
+ "console.assert": true,
+ "process.nextTick": true,
+ "process.platform": true,
+ "setImmediate": true
+ },
+ "packages": {
+ "gulp-watch>chokidar>fsevents>node-pre-gyp": true
+ }
+ },
+ "gulp>glob-watcher>just-debounce": {
+ "globals": {
+ "clearTimeout": true,
+ "setTimeout": true
+ }
+ },
+ "gulp>gulp-cli>liftoff>fined>object.pick": {
+ "packages": {
+ "gulp>gulp-cli>isobject": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node": {
+ "packages": {
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node>define-property": true,
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node>snapdragon-util": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node>define-property": {
+ "packages": {
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node>snapdragon-util": {
+ "packages": {
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node>snapdragon-util>kind-of": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>braces>snapdragon-node>snapdragon-util>kind-of": {
+ "packages": {
+ "browserify>insert-module-globals>is-buffer": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>braces>split-string": {
+ "packages": {
+ "gulp-zip>plugin-error>extend-shallow": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": {
+ "packages": {
+ "@babel/register>clone-deep>kind-of": true,
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-accessor-descriptor": true,
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-data-descriptor": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-accessor-descriptor": {
+ "packages": {
+ "@babel/register>clone-deep>kind-of": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-data-descriptor": {
+ "packages": {
+ "@babel/register>clone-deep>kind-of": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>fragment-cache": {
+ "packages": {
+ "gulp>gulp-cli>liftoff>fined>parse-filepath>map-cache": true
}
},
- "gulp>glob-watcher>anymatch>micromatch": {
+ "gulp>gulp-cli>matchdep>micromatch>nanomatch": {
"builtin": {
"path.basename": true,
"path.sep": true,
"util.inspect": true
},
- "globals": {
- "process.platform": true
- },
"packages": {
"@babel/register>clone-deep>kind-of": true,
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
"gulp-zip>plugin-error>arr-diff": true,
"gulp-zip>plugin-error>extend-shallow": true,
- "gulp>glob-watcher>anymatch>micromatch>define-property": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob": true,
- "gulp>glob-watcher>chokidar>braces": true,
"gulp>gulp-cli>liftoff>fined>object.pick": true,
+ "gulp>gulp-cli>matchdep>micromatch>array-unique": true,
"gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
- "gulp>gulp-cli>matchdep>micromatch>nanomatch": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
+ "gulp>gulp-cli>matchdep>micromatch>nanomatch>define-property": true,
+ "gulp>gulp-cli>matchdep>micromatch>regex-not": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex": true,
+ "gulp>gulp-cli>replace-homedir>is-absolute>is-windows": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>define-property": {
+ "gulp>gulp-cli>matchdep>micromatch>nanomatch>define-property": {
"packages": {
"gulp>gulp-cli>isobject": true,
"gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob": {
- "packages": {
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>define-property": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>extend-shallow": true,
- "gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
- }
- },
- "gulp>glob-watcher>anymatch>micromatch>extglob>define-property": {
+ "gulp>gulp-cli>matchdep>micromatch>regex-not": {
"packages": {
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
+ "gulp-zip>plugin-error>extend-shallow": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex>safe-regex": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon": {
+ "builtin": {
+ "fs.readFileSync": true,
+ "path.dirname": true,
+ "util.inspect": true
+ },
"globals": {
"__filename": true
},
"packages": {
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>debug": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>extend-shallow": true,
- "gulp>gulp-cli>matchdep>micromatch>extglob>expand-brackets>posix-character-classes": true,
- "gulp>gulp-cli>matchdep>micromatch>regex-not": true
+ "gulp>gulp-cli>liftoff>fined>parse-filepath>map-cache": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>debug": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>extend-shallow": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>source-map": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>use": true,
+ "resolve-url-loader>rework>css>source-map-resolve": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>debug": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base": {
"builtin": {
- "fs.SyncWriteStream": true,
- "net.Socket": true,
- "tty.WriteStream": true,
- "tty.isatty": true,
- "util": true
+ "util.inherits": true
},
- "globals": {
- "chrome": true,
- "console": true,
- "document": true,
- "localStorage": true,
- "navigator": true,
- "process": true
+ "packages": {
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>component-emitter": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>define-property": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>mixin-deep": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>pascalcase": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base": {
+ "packages": {
+ "gulp>gulp-cli>array-sort>get-value": true,
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>set-value": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>to-object-path": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>union-value": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>component-emitter": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit": {
+ "packages": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>map-visit": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>object-visit": true
+ }
+ },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>map-visit": {
+ "builtin": {
+ "util.inspect": true
},
"packages": {
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>debug>ms": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>object-visit": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>collection-visit>object-visit": {
"packages": {
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor": true
+ "gulp>gulp-cli>isobject": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value": {
"packages": {
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": true,
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>kind-of": true
+ "gulp>gulp-cli>array-sort>get-value": true,
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values": {
"packages": {
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor>kind-of": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values>is-number": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values>kind-of": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-accessor-descriptor>kind-of": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values>is-number": {
"packages": {
- "browserify>insert-module-globals>is-buffer": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values>is-number>kind-of": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values>is-number>kind-of": {
"packages": {
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": true
+ "browserify>insert-module-globals>is-buffer": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>define-property>is-descriptor>is-data-descriptor>kind-of": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>has-value>has-values>kind-of": {
"packages": {
"browserify>insert-module-globals>is-buffer": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>expand-brackets>extend-shallow": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>set-value": {
"packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ "@babel/register>clone-deep>is-plain-object": true,
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true,
+ "gulp>gulp-cli>matchdep>micromatch>braces>split-string": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>set-value>extend-shallow": true
}
},
- "gulp>glob-watcher>anymatch>micromatch>extglob>extend-shallow": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>set-value>extend-shallow": {
"packages": {
"gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
}
},
- "gulp>glob-watcher>anymatch>normalize-path": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>to-object-path": {
"packages": {
- "vinyl>remove-trailing-separator": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>to-object-path>kind-of": true
}
},
- "gulp>glob-watcher>async-done": {
- "globals": {
- "process.nextTick": true
- },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>to-object-path>kind-of": {
"packages": {
- "@metamask/object-multiplex>once": true,
- "duplexify>end-of-stream": true,
- "gulp>glob-watcher>async-done>stream-exhaust": true,
- "readable-stream-2>process-nextick-args": true
+ "browserify>insert-module-globals>is-buffer": true
}
},
- "gulp>glob-watcher>async-done>stream-exhaust": {
- "builtin": {
- "stream.Writable": true,
- "util.inherits": true
- },
- "globals": {
- "setImmediate": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>union-value": {
+ "packages": {
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true,
+ "gulp-zip>plugin-error>arr-union": true,
+ "gulp>gulp-cli>array-sort>get-value": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>set-value": 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,
- "path.resolve": true,
- "path.sep": 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.nextTick": true,
- "process.platform": true,
- "setTimeout": true
- },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value": {
"packages": {
- "chokidar>normalize-path": true,
- "del>is-glob": true,
- "eslint>glob-parent": true,
- "gulp-watch>chokidar>async-each": 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>readdirp": true,
- "gulp>glob-watcher>chokidar>upath": true,
- "pumpify>inherits": true
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value>has-value": true
}
},
- "gulp>glob-watcher>chokidar>braces": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value>has-value": {
"packages": {
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>repeat-element": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>snapdragon-node": true,
- "gulp-watch>chokidar>braces>split-string": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp>glob-watcher>chokidar>braces>extend-shallow": true,
- "gulp>glob-watcher>chokidar>braces>fill-range": true,
- "gulp>gulp-cli>isobject": true,
- "gulp>undertaker>arr-flatten": true
+ "gulp>gulp-cli>array-sort>get-value": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value>has-value>has-values": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value>has-value>isobject": true
}
},
- "gulp>glob-watcher>chokidar>braces>extend-shallow": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value>has-value>isobject": {
"packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>cache-base>unset-value>has-value>isobject>isarray": true
}
},
- "gulp>glob-watcher>chokidar>braces>fill-range": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils": {
"builtin": {
- "util.inspect": true
+ "util": true
},
"packages": {
- "gulp>glob-watcher>chokidar>braces>fill-range>extend-shallow": true,
- "gulp>glob-watcher>chokidar>braces>fill-range>is-number": true,
- "gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": true,
- "stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
+ "gulp-zip>plugin-error>arr-union": true,
+ "gulp>gulp-cli>isobject": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property": true
}
},
- "gulp>glob-watcher>chokidar>braces>fill-range>extend-shallow": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend": {
+ "builtin": {
+ "util.inherits": true
+ },
"packages": {
- "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend>object-copy": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property": true
}
},
- "gulp>glob-watcher>chokidar>braces>fill-range>is-number": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend>object-copy": {
"packages": {
- "gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend>object-copy>copy-descriptor": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend>object-copy>kind-of": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property": true
}
},
- "gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>class-utils>static-extend>object-copy>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,
- "stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
- }
- },
- "gulp>glob-watcher>chokidar>fsevents": {
- "builtin": {
- "events.EventEmitter": true,
- "fs.stat": true,
- "path.join": true,
- "util.inherits": true
- },
- "globals": {
- "__dirname": true,
- "console.assert": true,
- "process.nextTick": true,
- "process.platform": true,
- "setImmediate": true
- },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>define-property": {
"packages": {
- "gulp-watch>chokidar>fsevents>node-pre-gyp": true
+ "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
}
},
- "gulp>glob-watcher>chokidar>is-binary-path": {
- "builtin": {
- "path.extname": true
- },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>mixin-deep": {
"packages": {
- "gulp>glob-watcher>chokidar>is-binary-path>binary-extensions": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>mixin-deep>is-extendable": true,
+ "gulp>undertaker>object.reduce>for-own>for-in": true
}
},
- "gulp>glob-watcher>chokidar>readdirp": {
- "builtin": {
- "path.join": true,
- "path.relative": true,
- "util.inherits": true
- },
- "globals": {
- "setImmediate": true
- },
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>base>mixin-deep>is-extendable": {
"packages": {
- "del>graceful-fs": true,
- "gulp>glob-watcher>anymatch>micromatch": true,
- "gulp>glob-watcher>chokidar>readdirp>readable-stream": true
+ "@babel/register>clone-deep>is-plain-object": true
}
},
- "gulp>glob-watcher>chokidar>readdirp>readable-stream": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>debug": {
"builtin": {
- "events.EventEmitter": true,
- "stream": true,
+ "fs.SyncWriteStream": true,
+ "net.Socket": true,
+ "tty.WriteStream": true,
+ "tty.isatty": true,
"util": true
},
"globals": {
- "process.browser": true,
- "process.env.READABLE_STREAM": true,
- "process.stderr": true,
- "process.stdout": true,
- "process.version.slice": true,
- "setImmediate": true
+ "chrome": true,
+ "console": true,
+ "document": true,
+ "localStorage": true,
+ "navigator": true,
+ "process": true
},
"packages": {
- "gulp>glob-watcher>chokidar>readdirp>readable-stream>isarray": true,
- "gulp>glob-watcher>chokidar>readdirp>readable-stream>safe-buffer": true,
- "gulp>glob-watcher>chokidar>readdirp>readable-stream>string_decoder": true,
- "pumpify>inherits": true,
- "readable-stream-2>core-util-is": true,
- "readable-stream-2>process-nextick-args": true,
- "readable-stream>util-deprecate": true
- }
- },
- "gulp>glob-watcher>chokidar>readdirp>readable-stream>safe-buffer": {
- "builtin": {
- "buffer": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>debug>ms": true
}
},
- "gulp>glob-watcher>chokidar>readdirp>readable-stream>string_decoder": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property": {
"packages": {
- "gulp>glob-watcher>chokidar>readdirp>readable-stream>safe-buffer": true
- }
- },
- "gulp>glob-watcher>chokidar>upath": {
- "builtin": {
- "path": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor": true
}
},
- "gulp>glob-watcher>just-debounce": {
- "globals": {
- "clearTimeout": true,
- "setTimeout": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor": {
+ "packages": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-accessor-descriptor": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor": true,
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>kind-of": true
}
},
- "gulp>gulp-cli>liftoff>fined>object.pick": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-accessor-descriptor": {
"packages": {
- "gulp>gulp-cli>isobject": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor": {
"packages": {
- "@babel/register>clone-deep>kind-of": true,
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-accessor-descriptor": true,
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-data-descriptor": true
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-accessor-descriptor": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>define-property>is-descriptor>is-data-descriptor>kind-of": {
"packages": {
- "@babel/register>clone-deep>kind-of": true
+ "browserify>insert-module-globals>is-buffer": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor>is-data-descriptor": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>extend-shallow": {
"packages": {
- "@babel/register>clone-deep>kind-of": true
+ "gulp-watch>anymatch>micromatch>object.omit>is-extendable": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>fragment-cache": {
+ "gulp>gulp-cli>matchdep>micromatch>snapdragon>use": {
"packages": {
- "gulp-watch>chokidar>braces>snapdragon>map-cache": true
+ "@babel/register>clone-deep>kind-of": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>nanomatch": {
- "builtin": {
- "path.basename": true,
- "path.sep": true,
- "util.inspect": true
- },
+ "gulp>gulp-cli>matchdep>micromatch>to-regex": {
"packages": {
- "@babel/register>clone-deep>kind-of": true,
- "gulp-watch>chokidar>braces>array-unique": true,
- "gulp-watch>chokidar>braces>snapdragon": true,
- "gulp-watch>chokidar>braces>to-regex": true,
- "gulp-zip>plugin-error>arr-diff": true,
"gulp-zip>plugin-error>extend-shallow": true,
- "gulp>gulp-cli>liftoff>fined>object.pick": true,
- "gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
- "gulp>gulp-cli>matchdep>micromatch>nanomatch>define-property": true,
"gulp>gulp-cli>matchdep>micromatch>regex-not": true,
- "gulp>gulp-cli>replace-homedir>is-absolute>is-windows": true
+ "gulp>gulp-cli>matchdep>micromatch>to-regex>define-property": true,
+ "gulp>gulp-cli>matchdep>micromatch>to-regex>safe-regex": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>nanomatch>define-property": {
+ "gulp>gulp-cli>matchdep>micromatch>to-regex>define-property": {
"packages": {
"gulp>gulp-cli>isobject": true,
"gulp>gulp-cli>matchdep>micromatch>define-property>is-descriptor": true
}
},
- "gulp>gulp-cli>matchdep>micromatch>regex-not": {
+ "gulp>gulp-cli>matchdep>micromatch>to-regex>safe-regex": {
"packages": {
- "gulp-watch>chokidar>braces>to-regex>safe-regex": true,
- "gulp-zip>plugin-error>extend-shallow": true
+ "gulp>gulp-cli>matchdep>micromatch>to-regex>safe-regex>ret": true
}
},
"gulp>gulp-cli>replace-homedir>is-absolute": {
@@ -6505,9 +6177,9 @@
"packages": {
"del>graceful-fs": true,
"gulp-sourcemaps>convert-source-map": true,
+ "gulp-watch>anymatch>normalize-path": true,
"gulp>vinyl-fs>remove-bom-buffer": true,
"gulp>vinyl-fs>vinyl-sourcemap>append-buffer": true,
- "gulp>vinyl-fs>vinyl-sourcemap>normalize-path": true,
"gulp>vinyl-fs>vinyl-sourcemap>now-and-later": true,
"vinyl": true
}
@@ -6528,11 +6200,6 @@
"buffer.Buffer.isBuffer": true
}
},
- "gulp>vinyl-fs>vinyl-sourcemap>normalize-path": {
- "packages": {
- "vinyl>remove-trailing-separator": true
- }
- },
"gulp>vinyl-fs>vinyl-sourcemap>now-and-later": {
"packages": {
"@metamask/object-multiplex>once": true
diff --git a/package.json b/package.json
index f1c90da5fc8a..0eb9a1d6fdc8 100644
--- a/package.json
+++ b/package.json
@@ -125,6 +125,7 @@
"attributions:generate": "./development/generate-attributions.sh"
},
"resolutions": {
+ "chokidar": "^3.6.0",
"simple-update-notifier@^1.0.0": "^2.0.0",
"@babel/core": "patch:@babel/core@npm%3A7.23.2#~/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch",
"@types/react": "^16.9.53",
@@ -528,7 +529,7 @@
"bify-module-groups": "^2.0.0",
"browserify": "^17.0.0",
"chalk": "^4.1.2",
- "chokidar": "^3.5.3",
+ "chokidar": "^3.6.0",
"concurrently": "^8.2.2",
"copy-webpack-plugin": "^12.0.2",
"cross-spawn": "^7.0.3",
diff --git a/yarn.lock b/yarn.lock
index 9ef60d6f70e3..6658ee69abfe 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11499,13 +11499,6 @@ __metadata:
languageName: node
linkType: hard
-"abbrev@npm:1":
- version: 1.1.1
- resolution: "abbrev@npm:1.1.1"
- checksum: 10/2d882941183c66aa665118bafdab82b7a177e9add5eb2776c33e960a4f3c89cff88a1b38aba13a456de01d0dd9d66a8bea7c903268b21ea91dd1097e1e2e8243
- languageName: node
- linkType: hard
-
"abbrev@npm:^2.0.0":
version: 2.0.0
resolution: "abbrev@npm:2.0.0"
@@ -12116,13 +12109,6 @@ __metadata:
languageName: node
linkType: hard
-"aproba@npm:^1.0.3":
- version: 1.2.0
- resolution: "aproba@npm:1.2.0"
- checksum: 10/48def777330afca699880126b555273cd9912525500edc5866b527da6fd6c54badd3ae6cc6039081e5bc22e9b349d8e65fd70f8499beb090f86aa6261e4242dd
- languageName: node
- linkType: hard
-
"archy@npm:^1.0.0":
version: 1.0.0
resolution: "archy@npm:1.0.0"
@@ -12137,16 +12123,6 @@ __metadata:
languageName: node
linkType: hard
-"are-we-there-yet@npm:~1.1.2":
- version: 1.1.4
- resolution: "are-we-there-yet@npm:1.1.4"
- dependencies:
- delegates: "npm:^1.0.0"
- readable-stream: "npm:^2.0.6"
- checksum: 10/86947c3ec0c8be746a0a8eb0871033bd89d8619ddbf6d51aae2d4a3a816fc06741df01307db8c0b44b9e9b20d46725fd023f1766bf586ccf1d4636cb1441452f
- languageName: node
- linkType: hard
-
"arg@npm:^4.1.0":
version: 4.1.3
resolution: "arg@npm:4.1.3"
@@ -12495,13 +12471,6 @@ __metadata:
languageName: node
linkType: hard
-"async-each@npm:^1.0.1":
- version: 1.0.1
- resolution: "async-each@npm:1.0.1"
- checksum: 10/9421203743e3379ce70defb94a78308c828e4e56d8e8bf4ba90b4c788b90a9d0759aabd327831e5a97bb6e484eccfee2f5496c1c2b239bd15f082544d919c60d
- languageName: node
- linkType: hard
-
"async-eventemitter@npm:0.2.4":
version: 0.2.4
resolution: "async-eventemitter@npm:0.2.4"
@@ -13092,13 +13061,6 @@ __metadata:
languageName: node
linkType: hard
-"binary-extensions@npm:^1.0.0":
- version: 1.11.0
- resolution: "binary-extensions@npm:1.11.0"
- checksum: 10/839f346e4a1e5d43a96c5e5b3cdc608840e97b178f27a1b2fdc7b670598a23fdec42ab70d736d03db752bef44e18a57c5471e7d3ac11163deff459d6bd580e6a
- languageName: node
- linkType: hard
-
"binary-extensions@npm:^2.0.0":
version: 2.0.0
resolution: "binary-extensions@npm:2.0.0"
@@ -13372,7 +13334,7 @@ __metadata:
languageName: node
linkType: hard
-"braces@npm:^2.3.1, braces@npm:^2.3.2":
+"braces@npm:^2.3.1":
version: 2.3.2
resolution: "braces@npm:2.3.2"
dependencies:
@@ -14219,26 +14181,7 @@ __metadata:
languageName: node
linkType: hard
-"chokidar@npm:3.5.3":
- version: 3.5.3
- resolution: "chokidar@npm:3.5.3"
- dependencies:
- anymatch: "npm:~3.1.2"
- braces: "npm:~3.0.2"
- fsevents: "npm:~2.3.2"
- glob-parent: "npm:~5.1.2"
- is-binary-path: "npm:~2.1.0"
- is-glob: "npm:~4.0.1"
- normalize-path: "npm:~3.0.0"
- readdirp: "npm:~3.6.0"
- dependenciesMeta:
- fsevents:
- optional: true
- checksum: 10/863e3ff78ee7a4a24513d2a416856e84c8e4f5e60efbe03e8ab791af1a183f569b62fc6f6b8044e2804966cb81277ddbbc1dc374fba3265bd609ea8efd62f5b3
- languageName: node
- linkType: hard
-
-"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.3.1, chokidar@npm:^3.4.0, chokidar@npm:^3.5.3":
+"chokidar@npm:^3.6.0":
version: 3.6.0
resolution: "chokidar@npm:3.6.0"
dependencies:
@@ -14257,30 +14200,7 @@ __metadata:
languageName: node
linkType: hard
-"chokidar@npm:^2.0.0":
- version: 2.1.8
- resolution: "chokidar@npm:2.1.8"
- dependencies:
- anymatch: "npm:^2.0.0"
- async-each: "npm:^1.0.1"
- braces: "npm:^2.3.2"
- fsevents: "npm:^1.2.7"
- glob-parent: "npm:^3.1.0"
- inherits: "npm:^2.0.3"
- is-binary-path: "npm:^1.0.0"
- is-glob: "npm:^4.0.0"
- normalize-path: "npm:^3.0.0"
- path-is-absolute: "npm:^1.0.0"
- readdirp: "npm:^2.2.1"
- upath: "npm:^1.1.1"
- dependenciesMeta:
- fsevents:
- optional: true
- checksum: 10/567c319dd2a9078fddb5a64df46163d87b104857c1b50c2ef6f9b41b3ab28867c48dbc5f0c6ddaafd3c338b147ea33a6498eb9b906c71006cba1e486a0e9350d
- languageName: node
- linkType: hard
-
-"chownr@npm:^1.1.1, chownr@npm:^1.1.4":
+"chownr@npm:^1.1.1":
version: 1.1.4
resolution: "chownr@npm:1.1.4"
checksum: 10/115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d
@@ -14970,13 +14890,6 @@ __metadata:
languageName: node
linkType: hard
-"console-control-strings@npm:^1.0.0, console-control-strings@npm:~1.1.0":
- version: 1.1.0
- resolution: "console-control-strings@npm:1.1.0"
- checksum: 10/27b5fa302bc8e9ae9e98c03c66d76ca289ad0c61ce2fe20ab288d288bee875d217512d2edb2363fc83165e88f1c405180cf3f5413a46e51b4fe1a004840c6cdb
- languageName: node
- linkType: hard
-
"consolidate@npm:^0.16.0":
version: 0.16.0
resolution: "consolidate@npm:0.16.0"
@@ -15702,7 +15615,7 @@ __metadata:
languageName: node
linkType: hard
-"debug@npm:3.X, debug@npm:^3.1.0, debug@npm:^3.1.1, debug@npm:^3.2.6, debug@npm:^3.2.7":
+"debug@npm:3.X, debug@npm:^3.1.0, debug@npm:^3.1.1, debug@npm:^3.2.7":
version: 3.2.7
resolution: "debug@npm:3.2.7"
dependencies:
@@ -16196,15 +16109,6 @@ __metadata:
languageName: node
linkType: hard
-"detect-libc@npm:^1.0.2":
- version: 1.0.3
- resolution: "detect-libc@npm:1.0.3"
- bin:
- detect-libc: ./bin/detect-libc.js
- checksum: 10/3849fe7720feb153e4ac9407086956e073f1ce1704488290ef0ca8aab9430a8d48c8a9f8351889e7cdc64e5b1128589501e4fef48f3a4a49ba92cd6d112d0757
- languageName: node
- linkType: hard
-
"detect-newline@npm:^2.0.0":
version: 2.1.0
resolution: "detect-newline@npm:2.1.0"
@@ -19387,15 +19291,6 @@ __metadata:
languageName: node
linkType: hard
-"fs-minipass@npm:^1.2.7":
- version: 1.2.7
- resolution: "fs-minipass@npm:1.2.7"
- dependencies:
- minipass: "npm:^2.6.0"
- checksum: 10/6a2d39963eaad748164530ffab49606d0f3462c7867748521af3b7039d13689be533636d50a04e8ba6bd327d4d2e899d0907f8830d1161fe2db467d59cc46dc3
- languageName: node
- linkType: hard
-
"fs-minipass@npm:^2.0.0":
version: 2.1.0
resolution: "fs-minipass@npm:2.1.0"
@@ -19448,17 +19343,6 @@ __metadata:
languageName: node
linkType: hard
-"fsevents@npm:^1.2.7":
- version: 1.2.9
- resolution: "fsevents@npm:1.2.9"
- dependencies:
- nan: "npm:^2.12.1"
- node-pre-gyp: "npm:^0.12.0"
- checksum: 10/1d8cb504a54837816badbbe7178fae84d82d2b50731571591404f13881ace67d395cc9cadda8106716f69e362b9888295fa1c3eee58c1041c2c43fc87204da36
- conditions: os=darwin
- languageName: node
- linkType: hard
-
"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
@@ -19478,16 +19362,6 @@ __metadata:
languageName: node
linkType: hard
-"fsevents@patch:fsevents@npm%3A^1.2.7#optional!builtin":
- version: 1.2.9
- resolution: "fsevents@patch:fsevents@npm%3A1.2.9#optional!builtin::version=1.2.9&hash=d11327"
- dependencies:
- nan: "npm:^2.12.1"
- node-pre-gyp: "npm:^0.12.0"
- conditions: os=darwin
- languageName: node
- linkType: hard
-
"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin":
version: 2.3.3
resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
@@ -19590,22 +19464,6 @@ __metadata:
languageName: node
linkType: hard
-"gauge@npm:~2.7.3":
- version: 2.7.4
- resolution: "gauge@npm:2.7.4"
- dependencies:
- aproba: "npm:^1.0.3"
- console-control-strings: "npm:^1.0.0"
- has-unicode: "npm:^2.0.0"
- object-assign: "npm:^4.1.0"
- signal-exit: "npm:^3.0.0"
- string-width: "npm:^1.0.1"
- strip-ansi: "npm:^3.0.1"
- wide-align: "npm:^1.1.0"
- checksum: 10/0db20a7def238f0e8eab50226247e1f94f1446ab24700eab0a56e5ccf23ce85ccf8f0c0c462112b89beb964431b1edabd3f7b31f1f6d5f62294c453594523993
- languageName: node
- linkType: hard
-
"generic-names@npm:^2.0.1":
version: 2.0.1
resolution: "generic-names@npm:2.0.1"
@@ -20619,13 +20477,6 @@ __metadata:
languageName: node
linkType: hard
-"has-unicode@npm:^2.0.0":
- version: 2.0.1
- resolution: "has-unicode@npm:2.0.1"
- checksum: 10/041b4293ad6bf391e21c5d85ed03f412506d6623786b801c4ab39e4e6ca54993f13201bceb544d92963f9e0024e6e7fbf0cb1d84c9d6b31cb9c79c8c990d13d8
- languageName: node
- linkType: hard
-
"has-value@npm:^0.3.1":
version: 0.3.1
resolution: "has-value@npm:0.3.1"
@@ -21162,7 +21013,7 @@ __metadata:
languageName: node
linkType: hard
-"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.4":
+"iconv-lite@npm:0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"
dependencies:
@@ -21219,15 +21070,6 @@ __metadata:
languageName: node
linkType: hard
-"ignore-walk@npm:^3.0.1":
- version: 3.0.1
- resolution: "ignore-walk@npm:3.0.1"
- dependencies:
- minimatch: "npm:^3.0.4"
- checksum: 10/93f871229dfd2f240ad8c9eaef2496675cc31a96f6dbd15a35ddebb0f7ae16b7ca2116f1f48ae3f749badc461103b5198ae2e65206a064bba31c32f681d38571
- languageName: node
- linkType: hard
-
"ignore@npm:^5.1.1, ignore@npm:^5.1.8, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1":
version: 5.3.1
resolution: "ignore@npm:5.3.1"
@@ -21575,15 +21417,6 @@ __metadata:
languageName: node
linkType: hard
-"is-binary-path@npm:^1.0.0":
- version: 1.0.1
- resolution: "is-binary-path@npm:1.0.1"
- dependencies:
- binary-extensions: "npm:^1.0.0"
- checksum: 10/a803c99e9d898170c3b44a86fbdc0736d3d7fcbe737345433fb78e810b9fe30c982657782ad0e676644ba4693ddf05601a7423b5611423218663d6b533341ac9
- languageName: node
- linkType: hard
-
"is-binary-path@npm:~2.1.0":
version: 2.1.0
resolution: "is-binary-path@npm:2.1.0"
@@ -25495,7 +25328,7 @@ __metadata:
browserify: "npm:^17.0.0"
chalk: "npm:^4.1.2"
chart.js: "npm:^4.4.1"
- chokidar: "npm:^3.5.3"
+ chokidar: "npm:^3.6.0"
classnames: "npm:^2.2.6"
concurrently: "npm:^8.2.2"
contentful: "npm:^10.8.7"
@@ -26045,7 +25878,7 @@ __metadata:
languageName: node
linkType: hard
-"micromatch@npm:^3.0.4, micromatch@npm:^3.1.10, micromatch@npm:^3.1.4":
+"micromatch@npm:^3.0.4, micromatch@npm:^3.1.4":
version: 3.1.10
resolution: "micromatch@npm:3.1.10"
dependencies:
@@ -26308,16 +26141,6 @@ __metadata:
languageName: node
linkType: hard
-"minipass@npm:^2.6.0, minipass@npm:^2.9.0":
- version: 2.9.0
- resolution: "minipass@npm:2.9.0"
- dependencies:
- safe-buffer: "npm:^5.1.2"
- yallist: "npm:^3.0.0"
- checksum: 10/fdd1a77996c184991f8d2ce7c5b3979bec624e2a3225e2e1e140c4038fd65873d7eb90fb29779f8733735a8827b2686f283871a0c74c908f4f7694c56fa8dadf
- languageName: node
- linkType: hard
-
"minipass@npm:^3.0.0":
version: 3.3.5
resolution: "minipass@npm:3.3.5"
@@ -26341,15 +26164,6 @@ __metadata:
languageName: node
linkType: hard
-"minizlib@npm:^1.3.3":
- version: 1.3.3
- resolution: "minizlib@npm:1.3.3"
- dependencies:
- minipass: "npm:^2.9.0"
- checksum: 10/9c2c47e5687d7f896431a9b5585988ef72f848b56c6a974c9489534e8f619388d500d986ef82e1c13aedd46f3a0e81b6a88110cb1b27de7524cc8dabe8885e17
- languageName: node
- linkType: hard
-
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
version: 2.1.2
resolution: "minizlib@npm:2.1.2"
@@ -26712,7 +26526,7 @@ __metadata:
languageName: node
linkType: hard
-"nan@npm:^2.12.1, nan@npm:^2.13.2":
+"nan@npm:^2.13.2":
version: 2.15.0
resolution: "nan@npm:2.15.0"
dependencies:
@@ -26795,19 +26609,6 @@ __metadata:
languageName: node
linkType: hard
-"needle@npm:^2.2.1":
- version: 2.4.0
- resolution: "needle@npm:2.4.0"
- dependencies:
- debug: "npm:^3.2.6"
- iconv-lite: "npm:^0.4.4"
- sax: "npm:^1.2.4"
- bin:
- needle: ./bin/needle
- checksum: 10/0f2de9406d07f05f89cae241594a2aa66ff0a371ead42c013942c4684f60c830d57066663a740ea6b524e7d031ec2fc8ebd9bf687c51b8936af12c5dc4f7e526
- languageName: node
- linkType: hard
-
"negotiator@npm:0.6.3, negotiator@npm:^0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
@@ -27024,26 +26825,6 @@ __metadata:
languageName: node
linkType: hard
-"node-pre-gyp@npm:^0.12.0":
- version: 0.12.0
- resolution: "node-pre-gyp@npm:0.12.0"
- dependencies:
- detect-libc: "npm:^1.0.2"
- mkdirp: "npm:^0.5.1"
- needle: "npm:^2.2.1"
- nopt: "npm:^4.0.1"
- npm-packlist: "npm:^1.1.6"
- npmlog: "npm:^4.0.2"
- rc: "npm:^1.2.7"
- rimraf: "npm:^2.6.1"
- semver: "npm:^5.3.0"
- tar: "npm:^4"
- bin:
- node-pre-gyp: ./bin/node-pre-gyp
- checksum: 10/15a25f5e4e8f9dad86b1e72ad939311923c18d270b6eec9122c81d61205227dbd6cc7dc9f0bd97101b8e6b5c88c2c154d741b9c8b017e414ae0f480782c597d0
- languageName: node
- linkType: hard
-
"node-preload@npm:^0.2.1":
version: 0.2.1
resolution: "node-preload@npm:0.2.1"
@@ -27069,18 +26850,6 @@ __metadata:
languageName: node
linkType: hard
-"nopt@npm:^4.0.1":
- version: 4.0.1
- resolution: "nopt@npm:4.0.1"
- dependencies:
- abbrev: "npm:1"
- osenv: "npm:^0.1.4"
- bin:
- nopt: ./bin/nopt.js
- checksum: 10/7e9bc2a1224fd41ec604ef8d420a5f855dd6612fa6362615f8e7e8fc44e985fb117c9acfa5cfbfd5efb081c9fb6380a4b1cf579c49d5d1cd98dd425c9c8be78b
- languageName: node
- linkType: hard
-
"nopt@npm:^7.0.0":
version: 7.2.1
resolution: "nopt@npm:7.2.1"
@@ -27162,13 +26931,6 @@ __metadata:
languageName: node
linkType: hard
-"npm-bundled@npm:^1.0.1":
- version: 1.0.6
- resolution: "npm-bundled@npm:1.0.6"
- checksum: 10/db96b54c3e27aff51055748a2e900a9808f5687037bb7247a86f9e91cbe2070426414688bf684feda24e2f20e223899fc9f754ac9c882eea9bfa32a482d589b0
- languageName: node
- linkType: hard
-
"npm-install-checks@npm:^6.0.0":
version: 6.3.0
resolution: "npm-install-checks@npm:6.3.0"
@@ -27204,16 +26966,6 @@ __metadata:
languageName: node
linkType: hard
-"npm-packlist@npm:^1.1.6":
- version: 1.4.1
- resolution: "npm-packlist@npm:1.4.1"
- dependencies:
- ignore-walk: "npm:^3.0.1"
- npm-bundled: "npm:^1.0.1"
- checksum: 10/a8891f330760517152dd0b9f82fe017028e32d925985cd54672fc80c3294d4438226347b09b7307fa43dfc022d0b87117de073a4a92f5a423297d88f71af23d6
- languageName: node
- linkType: hard
-
"npm-pick-manifest@npm:^9.0.0":
version: 9.0.1
resolution: "npm-pick-manifest@npm:9.0.1"
@@ -27244,18 +26996,6 @@ __metadata:
languageName: node
linkType: hard
-"npmlog@npm:^4.0.2":
- version: 4.1.2
- resolution: "npmlog@npm:4.1.2"
- dependencies:
- are-we-there-yet: "npm:~1.1.2"
- console-control-strings: "npm:~1.1.0"
- gauge: "npm:~2.7.3"
- set-blocking: "npm:~2.0.0"
- checksum: 10/b6b85c9f33da8f600f72564b6ec71136b1641b8b235fca7cc543d1041acb74c2d989d97fe443a0e65754f438d9a974a2fe1b4ff8723c78ef3f9b7a6d74b02079
- languageName: node
- linkType: hard
-
"nth-check@npm:^2.0.1":
version: 2.0.1
resolution: "nth-check@npm:2.0.1"
@@ -27649,7 +27389,7 @@ __metadata:
languageName: node
linkType: hard
-"os-homedir@npm:^1.0.0, os-homedir@npm:^1.0.1":
+"os-homedir@npm:^1.0.1":
version: 1.0.2
resolution: "os-homedir@npm:1.0.2"
checksum: 10/af609f5a7ab72de2f6ca9be6d6b91a599777afc122ac5cad47e126c1f67c176fe9b52516b9eeca1ff6ca0ab8587fe66208bc85e40a3940125f03cdb91408e9d2
@@ -27676,23 +27416,6 @@ __metadata:
languageName: node
linkType: hard
-"os-tmpdir@npm:^1.0.0":
- version: 1.0.2
- resolution: "os-tmpdir@npm:1.0.2"
- checksum: 10/5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d
- languageName: node
- linkType: hard
-
-"osenv@npm:^0.1.4":
- version: 0.1.5
- resolution: "osenv@npm:0.1.5"
- dependencies:
- os-homedir: "npm:^1.0.0"
- os-tmpdir: "npm:^1.0.0"
- checksum: 10/779d261920f2a13e5e18cf02446484f12747d3f2ff82280912f52b213162d43d312647a40c332373cbccd5e3fb8126915d3bfea8dde4827f70f82da76e52d359
- languageName: node
- linkType: hard
-
"outpipe@npm:^1.1.0":
version: 1.1.1
resolution: "outpipe@npm:1.1.1"
@@ -29552,7 +29275,7 @@ __metadata:
languageName: node
linkType: hard
-"rc@npm:^1.0.1, rc@npm:^1.1.6, rc@npm:^1.2.7, rc@npm:^1.2.8":
+"rc@npm:^1.0.1, rc@npm:^1.1.6, rc@npm:^1.2.8":
version: 1.2.8
resolution: "rc@npm:1.2.8"
dependencies:
@@ -30258,7 +29981,7 @@ __metadata:
languageName: node
linkType: hard
-"readable-stream-2@npm:readable-stream@^2.3.3, readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.4, readable-stream@npm:^2.0.5, readable-stream@npm:^2.0.6, readable-stream@npm:^2.1.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:~2.3.6":
+"readable-stream-2@npm:readable-stream@^2.3.3, readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.4, readable-stream@npm:^2.0.5, readable-stream@npm:^2.1.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:~2.3.6":
version: 2.3.7
resolution: "readable-stream@npm:2.3.7"
dependencies:
@@ -30318,17 +30041,6 @@ __metadata:
languageName: node
linkType: hard
-"readdirp@npm:^2.2.1":
- version: 2.2.1
- resolution: "readdirp@npm:2.2.1"
- dependencies:
- graceful-fs: "npm:^4.1.11"
- micromatch: "npm:^3.1.10"
- readable-stream: "npm:^2.0.2"
- checksum: 10/14af3408ac2afa4e72e72a27e2c800d80c03e80bdef7ae4bd4b7907e98dddbeaa1ba37d4788959d9ce1131fc262cc823ce41ca9f024a91d80538241eea112c3c
- languageName: node
- linkType: hard
-
"readdirp@npm:^3.5.0, readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
@@ -31390,7 +31102,7 @@ __metadata:
languageName: node
linkType: hard
-"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0":
+"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451
@@ -31686,7 +31398,7 @@ __metadata:
languageName: node
linkType: hard
-"sax@npm:>=0.6.0, sax@npm:^1.2.4":
+"sax@npm:>=0.6.0":
version: 1.3.0
resolution: "sax@npm:1.3.0"
checksum: 10/bb571b31d30ecb0353c2ff5f87b117a03e5fb9eb4c1519141854c1a8fbee0a77ddbe8045f413259e711833aa03da210887df8527d19cdc55f299822dbf4b34de
@@ -31864,7 +31576,7 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.0.3, semver@npm:^5.1.0, semver@npm:^5.3.0, semver@npm:^5.6.0":
+"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.0.3, semver@npm:^5.1.0, semver@npm:^5.6.0":
version: 5.7.2
resolution: "semver@npm:5.7.2"
bin:
@@ -31987,7 +31699,7 @@ __metadata:
languageName: node
linkType: hard
-"set-blocking@npm:^2.0.0, set-blocking@npm:~2.0.0":
+"set-blocking@npm:^2.0.0":
version: 2.0.0
resolution: "set-blocking@npm:2.0.0"
checksum: 10/8980ebf7ae9eb945bb036b6e283c547ee783a1ad557a82babf758a065e2fb6ea337fd82cac30dd565c1e606e423f30024a19fff7afbf4977d784720c4026a8ef
@@ -32872,7 +32584,7 @@ __metadata:
languageName: node
linkType: hard
-"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
+"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
version: 4.2.3
resolution: "string-width@npm:4.2.3"
dependencies:
@@ -33603,21 +33315,6 @@ __metadata:
languageName: node
linkType: hard
-"tar@npm:^4":
- version: 4.4.19
- resolution: "tar@npm:4.4.19"
- dependencies:
- chownr: "npm:^1.1.4"
- fs-minipass: "npm:^1.2.7"
- minipass: "npm:^2.9.0"
- minizlib: "npm:^1.3.3"
- mkdirp: "npm:^0.5.5"
- safe-buffer: "npm:^5.2.1"
- yallist: "npm:^3.1.1"
- checksum: 10/2715b5964578424ba5164632905a85e5a98c8dffeba657860aafa3a771b2602e6fd2a350bca891d78b8bda8cab5c53134c683ed2269b9925533477a24722e73b
- languageName: node
- linkType: hard
-
"tar@npm:^6.1.11, tar@npm:^6.1.13, tar@npm:^6.1.2":
version: 6.2.1
resolution: "tar@npm:6.2.1"
@@ -34949,13 +34646,6 @@ __metadata:
languageName: node
linkType: hard
-"upath@npm:^1.1.1":
- version: 1.2.0
- resolution: "upath@npm:1.2.0"
- checksum: 10/ac07351d9e913eb7bc9bc0a17ed7d033a52575f0f2959e19726956c3e96f5d4d75aa6a7a777c4c9506e72372f58e06215e581f8dbff35611fc0a7b68ab4a6ddb
- languageName: node
- linkType: hard
-
"update-browserslist-db@npm:^1.0.13":
version: 1.0.13
resolution: "update-browserslist-db@npm:1.0.13"
@@ -36080,15 +35770,6 @@ __metadata:
languageName: node
linkType: hard
-"wide-align@npm:^1.1.0":
- version: 1.1.5
- resolution: "wide-align@npm:1.1.5"
- dependencies:
- string-width: "npm:^1.0.2 || 2 || 3 || 4"
- checksum: 10/d5f8027b9a8255a493a94e4ec1b74a27bff6679d5ffe29316a3215e4712945c84ef73ca4045c7e20ae7d0c72f5f57f296e04a4928e773d4276a2f1222e4c2e99
- languageName: node
- linkType: hard
-
"widest-line@npm:^2.0.0":
version: 2.0.1
resolution: "widest-line@npm:2.0.1"
@@ -36400,7 +36081,7 @@ __metadata:
languageName: node
linkType: hard
-"yallist@npm:^3.0.0, yallist@npm:^3.0.2, yallist@npm:^3.1.1":
+"yallist@npm:^3.0.2":
version: 3.1.1
resolution: "yallist@npm:3.1.1"
checksum: 10/9af0a4329c3c6b779ac4736c69fae4190ac03029fa27c1aef4e6bcc92119b73dea6fe5db5fe881fb0ce2a0e9539a42cdf60c7c21eda04d1a0b8c082e38509efb
From b37e39d824d35f40b07630caf9a32a0395addf8d Mon Sep 17 00:00:00 2001
From: Charly Chevalier
Date: Fri, 28 Jun 2024 07:59:59 +0200
Subject: [PATCH 06/15] feat: account overview for bip122:* account types
(#25200)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## **Description**
This adds a new `AccountOverview` component for the BIP122 account
types.
[](https://codespaces.new/MetaMask/metamask-extension/pull/25200?quickstart=1)
## **Related issues**
Fixes: https://github.com/MetaMask/accounts-planning/issues/423
## **Manual testing steps**
1. Start your extension: `yarn start:flask`
2. Run the bitcoin Snap + dapp: https://github.com/MetaMask/bitcoin
3. Go to: http://localhost:8000/
4. Install the Snap and create an account
5. You should see it in the account list and you should be able to see
the overview of your account
## **Screenshots/Recordings**
### **Before**
### **After**



## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
---------
Co-authored-by: Gustavo Antunes <17601467+gantunesr@users.noreply.github.com>
---
app/_locales/en/messages.json | 3 +
.../lib/accounts/BalancesController.test.ts | 16 +-
.../lib/accounts/BalancesController.ts | 52 +++--
app/scripts/metamask-controller.js | 2 +-
shared/constants/multichain/assets.ts | 5 +
shared/constants/multichain/networks.ts | 18 ++
shared/constants/network.ts | 1 +
shared/lib/multichain.test.ts | 49 +++++
shared/lib/multichain.ts | 31 +++
test/data/mock-accounts.ts | 17 +-
test/setup.js | 7 +
ui/components/app/asset-list/asset-list.js | 42 +++--
.../wallet-overview/btc-overview.stories.tsx | 19 ++
.../app/wallet-overview/btc-overview.test.tsx | 177 ++++++++++++++++++
.../app/wallet-overview/btc-overview.tsx | 37 ++++
.../app/wallet-overview/coin-buttons.tsx | 53 ++++--
.../app/wallet-overview/coin-overview.tsx | 33 ++--
ui/components/app/wallet-overview/index.js | 1 +
.../account-list-item.test.js.snap | 58 +++---
.../account-list-item/account-list-item.js | 2 +-
.../account-list-item.test.js | 6 +-
.../account-overview-btc.stories.tsx | 12 ++
.../account-overview-btc.test.tsx | 43 +++++
.../account-overview/account-overview-btc.tsx | 24 +++
.../account-overview-tabs.tsx | 5 +-
.../account-overview/account-overview.tsx | 23 ++-
.../multichain/ramps-card/ramps-card.js | 2 +-
.../receive-token-link/receive-token-link.tsx | 1 +
.../token-list-item/token-list-item.js | 56 ++++--
ui/hooks/ramps/useRamps/useRamps.ts | 23 ++-
ui/hooks/useCurrencyDisplay.js | 149 ++++++++++-----
ui/hooks/useIsOriginalNativeTokenSymbol.js | 14 +-
.../useIsOriginalNativeTokenSymbol.test.js | 8 +-
...MultichainAccountTotalFiatBalance.test.tsx | 4 +-
.../useMultichainAccountTotalFiatBalance.ts | 14 +-
ui/selectors/multichain.test.ts | 89 ++++++++-
ui/selectors/multichain.ts | 59 ++++--
37 files changed, 935 insertions(+), 220 deletions(-)
create mode 100644 shared/lib/multichain.test.ts
create mode 100644 shared/lib/multichain.ts
create mode 100644 ui/components/app/wallet-overview/btc-overview.stories.tsx
create mode 100644 ui/components/app/wallet-overview/btc-overview.test.tsx
create mode 100644 ui/components/app/wallet-overview/btc-overview.tsx
create mode 100644 ui/components/multichain/account-overview/account-overview-btc.stories.tsx
create mode 100644 ui/components/multichain/account-overview/account-overview-btc.test.tsx
create mode 100644 ui/components/multichain/account-overview/account-overview-btc.tsx
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 6590fea16133..f50a5fa84362 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -2936,6 +2936,9 @@
"networkNameBase": {
"message": "Base"
},
+ "networkNameBitcoin": {
+ "message": "Bitcoin"
+ },
"networkNameDefinition": {
"message": "The name associated with this network."
},
diff --git a/app/scripts/lib/accounts/BalancesController.test.ts b/app/scripts/lib/accounts/BalancesController.test.ts
index 01ce1f88c608..02627c6aa201 100644
--- a/app/scripts/lib/accounts/BalancesController.test.ts
+++ b/app/scripts/lib/accounts/BalancesController.test.ts
@@ -9,9 +9,10 @@ import { createMockInternalAccount } from '../../../../test/jest/mocks';
import {
BalancesController,
AllowedActions,
- BalancesControllerEvents,
+ AllowedEvents,
BalancesControllerState,
defaultState,
+ BalancesControllerMessenger,
} from './BalancesController';
import { Poller } from './Poller';
@@ -46,14 +47,15 @@ const setupController = ({
} = {}) => {
const controllerMessenger = new ControllerMessenger<
AllowedActions,
- BalancesControllerEvents
+ AllowedEvents
>();
- const balancesControllerMessenger = controllerMessenger.getRestricted({
- name: 'BalancesController',
- allowedActions: ['SnapController:handleRequest'],
- allowedEvents: [],
- });
+ const balancesControllerMessenger: BalancesControllerMessenger =
+ controllerMessenger.getRestricted({
+ name: 'BalancesController',
+ allowedActions: ['SnapController:handleRequest'],
+ allowedEvents: ['AccountsController:stateChange'],
+ });
const mockSnapHandleRequest = jest.fn();
controllerMessenger.registerActionHandler(
diff --git a/app/scripts/lib/accounts/BalancesController.ts b/app/scripts/lib/accounts/BalancesController.ts
index eee4ac11889a..ab1eb8c6cfe6 100644
--- a/app/scripts/lib/accounts/BalancesController.ts
+++ b/app/scripts/lib/accounts/BalancesController.ts
@@ -12,11 +12,17 @@ import {
type Balance,
type CaipAssetType,
type InternalAccount,
+ isEvmAccountType,
} from '@metamask/keyring-api';
import type { HandleSnapRequest } from '@metamask/snaps-controllers';
import type { SnapId } from '@metamask/snaps-sdk';
import { HandlerType } from '@metamask/snaps-utils';
import type { Draft } from 'immer';
+import type {
+ AccountsControllerChangeEvent,
+ AccountsControllerState,
+} from '@metamask/accounts-controller';
+import { isBtcMainnetAddress } from '../../../../shared/lib/multichain';
import { Poller } from './Poller';
const controllerName = 'BalancesController';
@@ -81,15 +87,20 @@ export type BalancesControllerEvents = BalancesControllerStateChange;
*/
export type AllowedActions = HandleSnapRequest;
+/**
+ * Events that this controller is allowed to subscribe.
+ */
+export type AllowedEvents = AccountsControllerChangeEvent;
+
/**
* Messenger type for the BalancesController.
*/
export type BalancesControllerMessenger = RestrictedControllerMessenger<
typeof controllerName,
BalancesControllerActions | AllowedActions,
- BalancesControllerEvents,
+ BalancesControllerEvents | AllowedEvents,
AllowedActions['type'],
- never
+ AllowedEvents['type']
>;
/**
@@ -110,21 +121,6 @@ const BTC_TESTNET_ASSETS = ['bip122:000000000933ea01ad0ee984209779ba/slip44:0'];
const BTC_MAINNET_ASSETS = ['bip122:000000000019d6689c085ae165831e93/slip44:0'];
export const BTC_AVG_BLOCK_TIME = 600000; // 10 minutes in milliseconds
-/**
- * Returns whether an address is on the Bitcoin mainnet.
- *
- * This function only checks the prefix of the address to determine if it's on
- * the mainnet or not. It doesn't validate the address itself, and should only
- * be used as a temporary solution until this information is included in the
- * account object.
- *
- * @param address - The address to check.
- * @returns `true` if the address is on the Bitcoin mainnet, `false` otherwise.
- */
-function isBtcMainnet(address: string): boolean {
- return address.startsWith('bc1') || address.startsWith('1');
-}
-
/**
* The BalancesController is responsible for fetching and caching account
* balances.
@@ -158,6 +154,11 @@ export class BalancesController extends BaseController<
},
});
+ this.messagingSystem.subscribe(
+ 'AccountsController:stateChange',
+ (newState) => this.#handleOnAccountsControllerChange(newState),
+ );
+
this.#listMultichainAccounts = listMultichainAccounts;
this.#poller = new Poller(() => this.updateBalances(), BTC_AVG_BLOCK_TIME);
}
@@ -203,7 +204,7 @@ export class BalancesController extends BaseController<
partialState.balances[account.id] = await this.#getBalances(
account.id,
account.metadata.snap.id,
- isBtcMainnet(account.address)
+ isBtcMainnetAddress(account.address)
? BTC_MAINNET_ASSETS
: BTC_TESTNET_ASSETS,
);
@@ -216,6 +217,21 @@ export class BalancesController extends BaseController<
}));
}
+ /**
+ * Handles changes in the accounts state, specifically when new non-EVM accounts are added.
+ *
+ * @param newState - The new state of the accounts controller.
+ */
+ #handleOnAccountsControllerChange(newState: AccountsControllerState) {
+ // If we have any new non-EVM accounts, we just update non-EVM balances
+ const newNonEvmAccounts = Object.values(
+ newState.internalAccounts.accounts,
+ ).filter((account) => !isEvmAccountType(account.type));
+ if (newNonEvmAccounts.length) {
+ this.updateBalances();
+ }
+ }
+
/**
* Get the balances for an account.
*
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 1799b36e6538..1be7493eca72 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -921,7 +921,7 @@ export default class MetamaskController extends EventEmitter {
const multichainBalancesControllerMessenger =
this.controllerMessenger.getRestricted({
name: 'BalancesController',
- allowedEvents: [],
+ allowedEvents: ['AccountsController:stateChange'],
allowedActions: ['SnapController:handleRequest'],
});
diff --git a/shared/constants/multichain/assets.ts b/shared/constants/multichain/assets.ts
index b4b2a74f6c22..988a9fbad624 100644
--- a/shared/constants/multichain/assets.ts
+++ b/shared/constants/multichain/assets.ts
@@ -3,3 +3,8 @@ import { MultichainNetworks } from './networks';
export const MULTICHAIN_NATIVE_CURRENCY_TO_CAIP19 = {
BTC: `${MultichainNetworks.BITCOIN}/slip44:0`,
} as const;
+
+export enum MultichainNativeAssets {
+ BITCOIN = `${MultichainNetworks.BITCOIN}/slip44:0`,
+ BITCOIN_TESTNET = `${MultichainNetworks.BITCOIN_TESTNET}/slip44:0`,
+}
diff --git a/shared/constants/multichain/networks.ts b/shared/constants/multichain/networks.ts
index de5e50639374..7f629ca49c6b 100644
--- a/shared/constants/multichain/networks.ts
+++ b/shared/constants/multichain/networks.ts
@@ -1,12 +1,17 @@
import { ProviderConfig } from '@metamask/network-controller';
import { CaipChainId } from '@metamask/utils';
+import { isBtcMainnetAddress, isBtcTestnetAddress } from '../../lib/multichain';
export type ProviderConfigWithImageUrl = Omit & {
rpcPrefs?: { imageUrl?: string };
};
export type MultichainProviderConfig = ProviderConfigWithImageUrl & {
+ nickname: string;
chainId: CaipChainId;
+ // NOTE: For now we use a callback to check if the address is compatible with
+ // the given network or not
+ isAddressCompatible: (address: string) => boolean;
};
export enum MultichainNetworks {
@@ -34,5 +39,18 @@ export const MULTICHAIN_PROVIDER_CONFIGS: Record<
rpcPrefs: {
imageUrl: MULTICHAIN_TOKEN_IMAGE_MAP[MultichainNetworks.BITCOIN],
},
+ isAddressCompatible: isBtcMainnetAddress,
+ },
+ [MultichainNetworks.BITCOIN_TESTNET]: {
+ chainId: MultichainNetworks.BITCOIN_TESTNET,
+ rpcUrl: '', // not used
+ ticker: 'BTC',
+ nickname: 'Bitcoin (testnet)',
+ id: 'btc-testnet',
+ type: 'rpc',
+ rpcPrefs: {
+ imageUrl: MULTICHAIN_TOKEN_IMAGE_MAP[MultichainNetworks.BITCOIN],
+ },
+ isAddressCompatible: isBtcTestnetAddress,
},
};
diff --git a/shared/constants/network.ts b/shared/constants/network.ts
index 7754b2a0f16f..955c9b2decc2 100644
--- a/shared/constants/network.ts
+++ b/shared/constants/network.ts
@@ -280,6 +280,7 @@ export const CURRENCY_SYMBOLS = {
AVALANCHE: 'AVAX',
BNB: 'BNB',
BUSD: 'BUSD',
+ BTC: 'BTC', // Do we wanna mix EVM and non-EVM here?
CELO: 'CELO',
DAI: 'DAI',
GNOSIS: 'XDAI',
diff --git a/shared/lib/multichain.test.ts b/shared/lib/multichain.test.ts
new file mode 100644
index 000000000000..6c59f506e721
--- /dev/null
+++ b/shared/lib/multichain.test.ts
@@ -0,0 +1,49 @@
+import { isBtcMainnetAddress, isBtcTestnetAddress } from './multichain';
+
+const MAINNET_ADDRESSES = [
+ // P2WPKH
+ 'bc1qwl8399fz829uqvqly9tcatgrgtwp3udnhxfq4k',
+ // P2PKH
+ '1P5ZEDWTKTFGxQjZphgWPQUpe554WKDfHQ',
+];
+
+const TESTNET_ADDRESSES = [
+ // P2WPKH
+ 'tb1q6rmsq3vlfdhjdhtkxlqtuhhlr6pmj09y6w43g8',
+];
+
+const ETH_ADDRESSES = ['0x6431726EEE67570BF6f0Cf892aE0a3988F03903F'];
+
+describe('multichain', () => {
+ // @ts-expect-error This is missing from the Mocha type definitions
+ it.each(MAINNET_ADDRESSES)(
+ 'returns true if address is compatible with BTC mainnet: %s',
+ (address: string) => {
+ expect(isBtcMainnetAddress(address)).toBe(true);
+ },
+ );
+
+ // @ts-expect-error This is missing from the Mocha type definitions
+ it.each([...TESTNET_ADDRESSES, ...ETH_ADDRESSES])(
+ 'returns false if address is not compatible with BTC mainnet: %s',
+ (address: string) => {
+ expect(isBtcMainnetAddress(address)).toBe(false);
+ },
+ );
+
+ // @ts-expect-error This is missing from the Mocha type definitions
+ it.each(TESTNET_ADDRESSES)(
+ 'returns true if address is compatible with BTC testnet: %s',
+ (address: string) => {
+ expect(isBtcTestnetAddress(address)).toBe(true);
+ },
+ );
+
+ // @ts-expect-error This is missing from the Mocha type definitions
+ it.each([...MAINNET_ADDRESSES, ...ETH_ADDRESSES])(
+ 'returns false if address is compatible with BTC testnet: %s',
+ (address: string) => {
+ expect(isBtcTestnetAddress(address)).toBe(false);
+ },
+ );
+});
diff --git a/shared/lib/multichain.ts b/shared/lib/multichain.ts
new file mode 100644
index 000000000000..07466e439266
--- /dev/null
+++ b/shared/lib/multichain.ts
@@ -0,0 +1,31 @@
+import { isEthAddress } from '../../app/scripts/lib/multichain/address';
+
+/**
+ * Returns whether an address is on the Bitcoin mainnet.
+ *
+ * This function only checks the prefix of the address to determine if it's on
+ * the mainnet or not. It doesn't validate the address itself, and should only
+ * be used as a temporary solution until this information is included in the
+ * account object.
+ *
+ * @param address - The address to check.
+ * @returns `true` if the address is on the Bitcoin mainnet, `false` otherwise.
+ */
+export function isBtcMainnetAddress(address: string): boolean {
+ return (
+ !isEthAddress(address) &&
+ (address.startsWith('bc1') || address.startsWith('1'))
+ );
+}
+
+/**
+ * Returns whether an address is on the Bitcoin testnet.
+ *
+ * See {@link isBtcMainnetAddress} for implementation details.
+ *
+ * @param address - The address to check.
+ * @returns `true` if the address is on the Bitcoin testnet, `false` otherwise.
+ */
+export function isBtcTestnetAddress(address: string): boolean {
+ return !isEthAddress(address) && !isBtcMainnetAddress(address);
+}
diff --git a/test/data/mock-accounts.ts b/test/data/mock-accounts.ts
index ff6009ebd555..2273915f7a5f 100644
--- a/test/data/mock-accounts.ts
+++ b/test/data/mock-accounts.ts
@@ -40,7 +40,7 @@ export const MOCK_ACCOUNT_ERC4337: InternalAccount = {
export const MOCK_ACCOUNT_BIP122_P2WPKH: InternalAccount = {
id: 'ae247df6-3911-47f7-9e36-28e6a7d96078',
- address: 'bc1qaabb',
+ address: 'bc1qwl8399fz829uqvqly9tcatgrgtwp3udnhxfq4k',
options: {},
methods: [BtcMethod.SendMany],
type: BtcAccountType.P2wpkh,
@@ -52,8 +52,23 @@ export const MOCK_ACCOUNT_BIP122_P2WPKH: InternalAccount = {
},
};
+export const MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET: InternalAccount = {
+ id: 'fcdafe8b-4bdf-4e25-9051-e255b2a0af5f',
+ address: 'tb1q6rmsq3vlfdhjdhtkxlqtuhhlr6pmj09y6w43g8',
+ options: {},
+ methods: [BtcMethod.SendMany],
+ type: BtcAccountType.P2wpkh,
+ metadata: {
+ name: 'Bitcoin Testnet Account',
+ keyring: { type: KeyringTypes.snap },
+ importTime: 1691565967600,
+ lastSelected: 1955565967656,
+ },
+};
+
export const MOCK_ACCOUNTS = {
[MOCK_ACCOUNT_EOA.id]: MOCK_ACCOUNT_EOA,
[MOCK_ACCOUNT_ERC4337.id]: MOCK_ACCOUNT_ERC4337,
[MOCK_ACCOUNT_BIP122_P2WPKH.id]: MOCK_ACCOUNT_BIP122_P2WPKH,
+ [MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET.id]: MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET,
};
diff --git a/test/setup.js b/test/setup.js
index f96e744a917a..17f9c45249ab 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -7,3 +7,10 @@ window.SVGPathElement = window.SVGPathElement || { prototype: {} };
global.indexedDB = {};
// scrollIntoView is not available in JSDOM
window.HTMLElement.prototype.scrollIntoView = () => undefined
+
+global.platform = {
+ // Required for: coin overviews components
+ openTab: () => undefined,
+ // Required for: settings info tab
+ getVersion: () => '',
+};
diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js
index 959789b8eebc..8e493037da44 100644
--- a/ui/components/app/asset-list/asset-list.js
+++ b/ui/components/app/asset-list/asset-list.js
@@ -5,7 +5,6 @@ import TokenList from '../token-list';
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
import { useUserPreferencedCurrency } from '../../../hooks/useUserPreferencedCurrency';
import {
- getSelectedAccountCachedBalance,
getDetectedTokensInCurrentNetwork,
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
getShouldHideZeroBalanceTokens,
@@ -19,6 +18,7 @@ import {
getMultichainShouldShowFiat,
getMultichainCurrencyImage,
getMultichainIsMainnet,
+ getMultichainSelectedAccountCachedBalance,
} from '../../../selectors/multichain';
import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay';
import { MetaMetricsContext } from '../../../contexts/metametrics';
@@ -48,9 +48,8 @@ import {
import { getIsNativeTokenBuyable } from '../../../ducks/ramps';
///: END:ONLY_INCLUDE_IF
-const AssetList = ({ onClickAsset }) => {
+const AssetList = ({ onClickAsset, showTokensLinks }) => {
const [showDetectedTokens, setShowDetectedTokens] = useState(false);
- const selectedAccountBalance = useSelector(getSelectedAccountCachedBalance);
const nativeCurrency = useSelector(getMultichainNativeCurrency);
const showFiat = useSelector(getMultichainShouldShowFiat);
const isMainnet = useSelector(getMultichainIsMainnet);
@@ -65,7 +64,7 @@ const AssetList = ({ onClickAsset }) => {
rpcUrl,
);
const trackEvent = useContext(MetaMetricsContext);
- const balance = useSelector(getSelectedAccountCachedBalance);
+ const balance = useSelector(getMultichainSelectedAccountCachedBalance);
const balanceIsLoading = !balance;
const selectedAccount = useSelector(getSelectedAccount);
const shouldHideZeroBalanceTokens = useSelector(
@@ -82,13 +81,13 @@ const AssetList = ({ onClickAsset }) => {
} = useUserPreferencedCurrency(SECONDARY, { ethNumberOfDecimals: 4 });
const [primaryCurrencyDisplay, primaryCurrencyProperties] =
- useCurrencyDisplay(selectedAccountBalance, {
+ useCurrencyDisplay(balance, {
numberOfDecimals: primaryNumberOfDecimals,
currency: primaryCurrency,
});
const [secondaryCurrencyDisplay, secondaryCurrencyProperties] =
- useCurrencyDisplay(selectedAccountBalance, {
+ useCurrencyDisplay(balance, {
numberOfDecimals: secondaryNumberOfDecimals,
currency: secondaryCurrency,
});
@@ -113,6 +112,10 @@ const AssetList = ({ onClickAsset }) => {
const isEvm = useSelector(getMultichainIsEvm);
+ // NOTE: Since we can parametrize it now, we keep the original behavior
+ // for EVM assets
+ const shouldShowTokensLinks = showTokensLinks ?? isEvm;
+
let isStakeable = isMainnet && isEvm;
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
isStakeable = false;
@@ -181,18 +184,22 @@ const AssetList = ({ onClickAsset }) => {
});
}}
/>
- {balanceIsZero && (
- 0 ? 0 : 4}
- />
+ {shouldShowTokensLinks && (
+ <>
+ {balanceIsZero && (
+ 0 ? 0 : 4}
+ />
+ )}
+ 0 && !balanceIsZero ? 0 : 2}
+ />
+ >
)}
- 0 && !balanceIsZero ? 0 : 2}
- />
{showDetectedTokens && (
)}
@@ -202,6 +209,7 @@ const AssetList = ({ onClickAsset }) => {
AssetList.propTypes = {
onClickAsset: PropTypes.func.isRequired,
+ showTokensLinks: PropTypes.bool,
};
export default AssetList;
diff --git a/ui/components/app/wallet-overview/btc-overview.stories.tsx b/ui/components/app/wallet-overview/btc-overview.stories.tsx
new file mode 100644
index 000000000000..43dff2554bef
--- /dev/null
+++ b/ui/components/app/wallet-overview/btc-overview.stories.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import BtcOverview from './btc-overview';
+
+export default {
+ title: 'Components/App/WalletOverview/BtcOverview',
+ component: BtcOverview,
+ parameters: {
+ docs: {
+ description: {
+ component:
+ 'A component that displays an overview of Bitcoin wallet information.',
+ },
+ },
+ },
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
diff --git a/ui/components/app/wallet-overview/btc-overview.test.tsx b/ui/components/app/wallet-overview/btc-overview.test.tsx
new file mode 100644
index 000000000000..233096f0918c
--- /dev/null
+++ b/ui/components/app/wallet-overview/btc-overview.test.tsx
@@ -0,0 +1,177 @@
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+import { fireEvent } from '@testing-library/react';
+import thunk from 'redux-thunk';
+import { Cryptocurrency } from '@metamask/assets-controllers';
+import { BtcAccountType, BtcMethod } from '@metamask/keyring-api';
+import { MultichainNativeAssets } from '../../../../shared/constants/multichain/assets';
+import mockState from '../../../../test/data/mock-state.json';
+import { renderWithProvider } from '../../../../test/jest/rendering';
+import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
+import { RampsMetaMaskEntry } from '../../../hooks/ramps/useRamps/useRamps';
+import BtcOverview from './btc-overview';
+
+const PORTOFOLIO_URL = 'https://portfolio.test';
+
+const BTC_OVERVIEW_BUY = 'coin-overview-buy';
+const BTC_OVERVIEW_BRIDGE = 'coin-overview-bridge';
+const BTC_OVERVIEW_PORTFOLIO = 'coin-overview-portfolio';
+const BTC_OVERVIEW_SWAP = 'token-overview-button-swap';
+const BTC_OVERVIEW_SEND = 'coin-overview-send';
+const BTC_OVERVIEW_PRIMARY_CURRENCY = 'coin-overview__primary-currency';
+
+const mockMetaMetricsId = 'deadbeef';
+const mockNonEvmBalance = '1';
+const mockNonEvmAccount = {
+ address: 'bc1qwl8399fz829uqvqly9tcatgrgtwp3udnhxfq4k',
+ id: '542490c8-d178-433b-9f31-f680b11f45a5',
+ metadata: {
+ name: 'Bitcoin Account',
+ keyring: {
+ type: 'Snap Keyring',
+ },
+ snap: {
+ id: 'btc-snap-id',
+ name: 'btc-snap-name',
+ },
+ },
+ options: {},
+ methods: [BtcMethod.SendMany],
+ type: BtcAccountType.P2wpkh,
+};
+
+function getStore(state?: Record) {
+ return configureMockStore([thunk])({
+ metamask: {
+ ...mockState.metamask,
+ internalAccounts: {
+ accounts: {
+ [mockNonEvmAccount.id]: mockNonEvmAccount,
+ },
+ selectedAccount: mockNonEvmAccount.id,
+ },
+ // (Multichain) BalancesController
+ balances: {
+ [mockNonEvmAccount.id]: {
+ [MultichainNativeAssets.BITCOIN]: {
+ amount: mockNonEvmBalance,
+ unit: 'BTC',
+ },
+ },
+ },
+ // (Multichain) RatesController
+ fiatCurrency: 'usd',
+ rates: {
+ [Cryptocurrency.Btc]: {
+ conversionRate: '1.000',
+ conversionDate: 0,
+ },
+ },
+ cryptocurrencies: [Cryptocurrency.Btc],
+ // Required, during onboarding, the extension will assume we're in an "EVM context", meaning
+ // most multichain selectors will not use non-EVM logic despite having a non-EVM
+ // selected account
+ completedOnboarding: true,
+ // Used when clicking on some buttons
+ metaMetricsId: mockMetaMetricsId,
+ // Override state if provided
+ ...state,
+ },
+ });
+}
+
+function makePortfolioUrl(path: string, getParams: Record) {
+ const params = new URLSearchParams(getParams);
+ return `${PORTOFOLIO_URL}/${path}?${params.toString()}`;
+}
+
+describe('BtcOverview', () => {
+ it('shows the primary balance', async () => {
+ const { queryByTestId, queryByText } = renderWithProvider(
+ ,
+ getStore(),
+ );
+
+ const primaryBalance = queryByTestId(BTC_OVERVIEW_PRIMARY_CURRENCY);
+ expect(primaryBalance).toBeInTheDocument();
+ expect(primaryBalance).toHaveTextContent(`${mockNonEvmBalance}BTC`);
+ // For now we consider balance to be always cached
+ expect(queryByText('*')).toBeInTheDocument();
+ });
+
+ it('shows a spinner if balance is not available', async () => {
+ const { container } = renderWithProvider(
+ ,
+ getStore({
+ // The balances won't be available
+ balances: {},
+ }),
+ );
+
+ const spinner = container.querySelector(
+ '.coin-overview__balance .coin-overview__primary-container .spinner',
+ );
+ expect(spinner).toBeInTheDocument();
+ });
+
+ it('buttons Send/Swap/Bridge are disabled', () => {
+ const { queryByTestId } = renderWithProvider(, getStore());
+
+ for (const buttonTestId of [
+ BTC_OVERVIEW_SEND,
+ BTC_OVERVIEW_SWAP,
+ BTC_OVERVIEW_BRIDGE,
+ ]) {
+ const button = queryByTestId(buttonTestId);
+ expect(button).toBeInTheDocument();
+ expect(button).toBeDisabled();
+ }
+ });
+
+ it('shows the "Buy & Sell" button', () => {
+ const { queryByTestId } = renderWithProvider(, getStore());
+ const buyButton = queryByTestId(BTC_OVERVIEW_BUY);
+ expect(buyButton).toBeInTheDocument();
+ });
+
+ it('opens the Portfolio "Buy & Sell" URI when clicking on "Buy & Sell" button', async () => {
+ const { queryByTestId } = renderWithProvider(, getStore());
+ const openTabSpy = jest.spyOn(global.platform, 'openTab');
+
+ const buyButton = queryByTestId(BTC_OVERVIEW_BUY);
+ expect(buyButton).toBeInTheDocument();
+ fireEvent.click(buyButton as HTMLElement);
+
+ expect(openTabSpy).toHaveBeenCalledTimes(1);
+ expect(openTabSpy).toHaveBeenCalledWith({
+ url: makePortfolioUrl('buy', {
+ metamaskEntry: RampsMetaMaskEntry.BuySellButton,
+ chainId: MultichainNetworks.BITCOIN,
+ metametricsId: mockMetaMetricsId,
+ }),
+ });
+ });
+
+ it('always show the Portfolio button', () => {
+ const { queryByTestId } = renderWithProvider(, getStore());
+ const portfolioButton = queryByTestId(BTC_OVERVIEW_PORTFOLIO);
+ expect(portfolioButton).toBeInTheDocument();
+ });
+
+ it('open the Portfolio URI when clicking on Portfolio button', async () => {
+ const { queryByTestId } = renderWithProvider(, getStore());
+ const openTabSpy = jest.spyOn(global.platform, 'openTab');
+
+ const portfolioButton = queryByTestId(BTC_OVERVIEW_PORTFOLIO);
+ expect(portfolioButton).toBeInTheDocument();
+ fireEvent.click(portfolioButton as HTMLElement);
+
+ expect(openTabSpy).toHaveBeenCalledTimes(1);
+ expect(openTabSpy).toHaveBeenCalledWith({
+ url: makePortfolioUrl('', {
+ metamaskEntry: 'ext_portfolio_button',
+ metametricsId: mockMetaMetricsId,
+ }),
+ });
+ });
+});
diff --git a/ui/components/app/wallet-overview/btc-overview.tsx b/ui/components/app/wallet-overview/btc-overview.tsx
new file mode 100644
index 000000000000..3703252f205a
--- /dev/null
+++ b/ui/components/app/wallet-overview/btc-overview.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+
+import { useSelector } from 'react-redux';
+import {
+ getMultichainProviderConfig,
+ getMultichainSelectedAccountCachedBalance,
+} from '../../../selectors/multichain';
+import { CoinOverview } from './coin-overview';
+
+type BtcOverviewProps = {
+ className?: string;
+};
+
+const BtcOverview = ({ className }: BtcOverviewProps) => {
+ const { chainId } = useSelector(getMultichainProviderConfig);
+ const balance = useSelector(getMultichainSelectedAccountCachedBalance);
+
+ return (
+
+ );
+};
+
+export default BtcOverview;
diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx
index 8eb24128b60e..b1774897edc4 100644
--- a/ui/components/app/wallet-overview/coin-buttons.tsx
+++ b/ui/components/app/wallet-overview/coin-buttons.tsx
@@ -6,9 +6,20 @@ import {
useLocation,
///: END:ONLY_INCLUDE_IF
} from 'react-router-dom';
+///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+import { toHex } from '@metamask/controller-utils';
+///: END:ONLY_INCLUDE_IF
+import {
+ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+ isCaipChainId,
+ ///: END:ONLY_INCLUDE_IF
+ CaipChainId,
+} from '@metamask/utils';
+///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+import { ChainId } from '../../../../shared/constants/network';
+///: END:ONLY_INCLUDE_IF
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
-import { CaipChainId } from '@metamask/utils';
import {
getMmiPortfolioEnabled,
getMmiPortfolioUrl,
@@ -22,11 +33,9 @@ import {
SEND_ROUTE,
} from '../../../helpers/constants/routes';
import {
- SwapsEthToken,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+ SwapsEthToken,
getCurrentKeyring,
- ///: END:ONLY_INCLUDE_IF
- ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
getMetaMetricsId,
///: END:ONLY_INCLUDE_IF
getUseExternalServices,
@@ -66,17 +75,23 @@ const CoinButtons = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain,
isBuyableChain,
+ // TODO: Remove this logic once `isNativeTokenBuyable` has been
+ // merged (see: https://github.com/MetaMask/metamask-extension/pull/24041)
+ isBuyableChainWithoutSigning = false,
defaultSwapsToken,
///: END:ONLY_INCLUDE_IF
classPrefix = 'coin',
}: {
- classPrefix?: string;
- isBuyableChain: boolean;
- isSigningEnabled: boolean;
+ chainId: `0x${string}` | CaipChainId | number;
isSwapsChain: boolean;
+ isSigningEnabled: boolean;
+ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain: boolean;
- chainId: `0x${string}` | CaipChainId | number;
+ isBuyableChain: boolean;
+ isBuyableChainWithoutSigning?: boolean;
defaultSwapsToken?: SwapsEthToken;
+ ///: END:ONLY_INCLUDE_IF
+ classPrefix?: string;
}) => {
const t = useContext(I18nContext);
const dispatch = useDispatch();
@@ -97,7 +112,10 @@ const CoinButtons = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
{ condition: !isBuyableChain, message: '' },
///: END:ONLY_INCLUDE_IF
- { condition: !isSigningEnabled, message: 'methodNotSupported' },
+ {
+ condition: !(isSigningEnabled || isBuyableChainWithoutSigning),
+ message: 'methodNotSupported',
+ },
],
sendButton: [
{ condition: !isSigningEnabled, message: 'methodNotSupported' },
@@ -130,6 +148,16 @@ const CoinButtons = ({
return contents;
};
+ ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
+ const getChainId = (): CaipChainId | ChainId => {
+ if (isCaipChainId(chainId)) {
+ return chainId as CaipChainId;
+ }
+ // Otherwise we assume that's an EVM chain ID, so use the usual 0x prefix
+ return toHex(chainId) as ChainId;
+ };
+ ///: END:ONLY_INCLUDE_IF
+
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
const mmiPortfolioEnabled = useSelector(getMmiPortfolioEnabled);
const mmiPortfolioUrl = useSelector(getMmiPortfolioUrl);
@@ -248,7 +276,7 @@ const CoinButtons = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const handleBuyAndSellOnClick = useCallback(() => {
- openBuyCryptoInPdapp();
+ openBuyCryptoInPdapp(getChainId());
trackEvent({
event: MetaMetricsEventName.NavBuyButtonClicked,
category: MetaMetricsEventCategory.Navigation,
@@ -311,7 +339,10 @@ const CoinButtons = ({
Icon={
}
- disabled={!isBuyableChain || !isSigningEnabled}
+ disabled={
+ !isBuyableChain ||
+ !(isSigningEnabled || isBuyableChainWithoutSigning)
+ }
data-testid={`${classPrefix}-overview-buy`}
label={t('buyAndSell')}
onClick={handleBuyAndSellOnClick}
diff --git a/ui/components/app/wallet-overview/coin-overview.tsx b/ui/components/app/wallet-overview/coin-overview.tsx
index abf1f6ca7158..6364b0231e82 100644
--- a/ui/components/app/wallet-overview/coin-overview.tsx
+++ b/ui/components/app/wallet-overview/coin-overview.tsx
@@ -3,15 +3,13 @@ import { useSelector } from 'react-redux';
import classnames from 'classnames';
import { zeroAddress } from 'ethereumjs-util';
-///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
import { CaipChainId } from '@metamask/utils';
-///: END:ONLY_INCLUDE_IF
+import type { Hex } from '@metamask/utils';
import { I18nContext } from '../../../contexts/i18n';
import Tooltip from '../../ui/tooltip';
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
import {
- getShouldShowFiat,
getPreferences,
getTokensMarketData,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
@@ -20,24 +18,28 @@ import {
} from '../../../selectors';
import Spinner from '../../ui/spinner';
import { useIsOriginalNativeTokenSymbol } from '../../../hooks/useIsOriginalNativeTokenSymbol';
-import { getProviderConfig } from '../../../ducks/metamask/metamask';
import { showPrimaryCurrency } from '../../../../shared/modules/currency-display.utils';
import { PercentageAndAmountChange } from '../../multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change';
+import {
+ getMultichainIsEvm,
+ getMultichainProviderConfig,
+ getMultichainShouldShowFiat,
+} from '../../../selectors/multichain';
import WalletOverview from './wallet-overview';
import CoinButtons from './coin-buttons';
export type CoinOverviewProps = {
balance: string;
balanceIsCached: boolean;
- className: string;
- classPrefix: string;
- chainId: CaipChainId | number;
- showAddress: boolean;
+ className?: string;
+ classPrefix?: string;
+ chainId: CaipChainId | Hex;
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
// FIXME: This seems to be for Ethereum only
defaultSwapsToken?: SwapsEthToken;
isBridgeChain: boolean;
isBuyableChain: boolean;
+ isBuyableChainWithoutSigning: boolean;
///: END:ONLY_INCLUDE_IF
isSwapsChain: boolean;
isSigningEnabled: boolean;
@@ -53,6 +55,7 @@ export const CoinOverview = ({
defaultSwapsToken,
isBridgeChain,
isBuyableChain,
+ isBuyableChainWithoutSigning,
///: END:ONLY_INCLUDE_IF
isSwapsChain,
isSigningEnabled,
@@ -65,9 +68,10 @@ export const CoinOverview = ({
///: END:ONLY_INCLUDE_IF
const t = useContext(I18nContext);
- const showFiat = useSelector(getShouldShowFiat);
+ const isEvm = useSelector(getMultichainIsEvm);
+ const showFiat = useSelector(getMultichainShouldShowFiat);
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
- const { ticker, type, rpcUrl } = useSelector(getProviderConfig);
+ const { ticker, type, rpcUrl } = useSelector(getMultichainProviderConfig);
const isOriginalNativeSymbol = useIsOriginalNativeTokenSymbol(
chainId,
ticker,
@@ -131,9 +135,11 @@ export const CoinOverview = ({
hideTitle
/>
)}
-
+ {isEvm && (
+
+ )}
}
@@ -146,6 +152,7 @@ export const CoinOverview = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain,
isBuyableChain,
+ isBuyableChainWithoutSigning,
defaultSwapsToken,
///: END:ONLY_INCLUDE_IF
classPrefix,
diff --git a/ui/components/app/wallet-overview/index.js b/ui/components/app/wallet-overview/index.js
index 2eb058f81afd..54536007bc41 100644
--- a/ui/components/app/wallet-overview/index.js
+++ b/ui/components/app/wallet-overview/index.js
@@ -1 +1,2 @@
export { default as EthOverview } from './eth-overview';
+export { default as BtcOverview } from './btc-overview';
diff --git a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap
index d97d0fd70fbe..0b2b13a29277 100644
--- a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap
+++ b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap
@@ -32,7 +32,7 @@ exports[`AccountListItem renders AccountListItem component and shows account nam
class="mm-avatar-account__jazzicon"
>