From 1b563188bf777d87351baad5b6dda8b65e59b27d Mon Sep 17 00:00:00 2001 From: Nicolas Ferro Date: Mon, 19 Sep 2022 17:00:57 +0200 Subject: [PATCH] Ability to buy tokens with Coinbase Pay and Transak (#15551) Co-authored-by: Ariella Vu <20778143+digiwand@users.noreply.github.com> Co-authored-by: Brad Decker Co-authored-by: Brad Decker --- app/scripts/lib/buy-url.js | 24 +- app/scripts/lib/buy-url.test.js | 15 +- .../files-to-convert.json | 6 +- shared/constants/network.ts | 282 +++++++++++++++++- .../app/deposit-popover/deposit-popover.js | 221 ++++++++++++++ ui/components/app/deposit-popover/index.js | 1 + .../app/deposit-popover/on-ramp-item.js | 67 +++++ .../deposit-ether-modal.component.js | 243 --------------- .../deposit-ether-modal.container.js | 60 ---- .../app/modals/deposit-ether-modal/index.js | 1 - .../app/modals/deposit-ether-modal/index.scss | 118 -------- ui/components/app/modals/index.scss | 1 - ui/components/app/modals/modal.js | 34 --- .../app/wallet-overview/eth-overview.js | 240 +++++++-------- .../app/wallet-overview/token-overview.js | 229 ++++++++------ ui/selectors/selectors.js | 14 + ui/store/actionConstants.js | 2 +- ui/store/actions.js | 4 +- 18 files changed, 854 insertions(+), 708 deletions(-) create mode 100644 ui/components/app/deposit-popover/deposit-popover.js create mode 100644 ui/components/app/deposit-popover/index.js create mode 100644 ui/components/app/deposit-popover/on-ramp-item.js delete mode 100644 ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js delete mode 100644 ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js delete mode 100644 ui/components/app/modals/deposit-ether-modal/index.js delete mode 100644 ui/components/app/modals/deposit-ether-modal/index.scss diff --git a/app/scripts/lib/buy-url.js b/app/scripts/lib/buy-url.js index 707c95ac8..5a4f2dadf 100644 --- a/app/scripts/lib/buy-url.js +++ b/app/scripts/lib/buy-url.js @@ -53,16 +53,16 @@ const createWyrePurchaseUrl = async (walletAddress, chainId) => { * * @param {string} walletAddress - Ethereum destination address * @param {string} chainId - Current chain ID + * @param {string|undefined} symbol - Token symbol to buy * @returns String */ -const createTransakUrl = (walletAddress, chainId) => { - const { transakCurrencies, network } = BUYABLE_CHAINS_MAP[chainId]; +const createTransakUrl = (walletAddress, chainId, symbol) => { + const { nativeCurrency, network } = BUYABLE_CHAINS_MAP[chainId]; const queryParams = new URLSearchParams({ apiKey: TRANSAK_API_KEY, hostURL: 'https://metamask.io', - cryptoCurrencyList: transakCurrencies.join(','), - defaultCryptoCurrency: transakCurrencies[0], + defaultCryptoCurrency: symbol || nativeCurrency, networks: network, walletAddress, }); @@ -115,17 +115,20 @@ const createMoonPayUrl = async (walletAddress, chainId) => { * * @param {string} walletAddress - Ethereum destination address * @param {string} chainId - Current chain ID + * @param {string|undefined} symbol - Token symbol to buy * @returns String */ -const createCoinbasePayUrl = (walletAddress, chainId) => { - const { coinbasePayCurrencies } = BUYABLE_CHAINS_MAP[chainId]; +const createCoinbasePayUrl = (walletAddress, chainId, symbol) => { + // since coinbasePayCurrencies is going to be extended to include all tokens supported + // we now default to nativeCurrency instead of the 2 previous tokens + eth that we had before + const { nativeCurrency } = BUYABLE_CHAINS_MAP[chainId]; const queryParams = new URLSearchParams({ appId: COINBASEPAY_API_KEY, attribution: 'extension', destinationWallets: JSON.stringify([ { address: walletAddress, - assets: coinbasePayCurrencies, + assets: symbol ? [symbol] : [nativeCurrency], }, ]), }); @@ -139,10 +142,11 @@ const createCoinbasePayUrl = (walletAddress, chainId) => { * @param {string} opts.chainId - The chainId for which to return a url * @param {string} opts.address - The address the bought ETH should be sent to. Only relevant if chainId === '0x1'. * @param opts.service + * @param {string|undefined} opts.symbol - The symbol of the token to buy. Only relevant if buying a token. * @returns {string|undefined} The url at which the user can access ETH, while in the given chain. If the passed * chainId does not match any of the specified cases, or if no chainId is given, returns undefined. */ -export default async function getBuyUrl({ chainId, address, service }) { +export default async function getBuyUrl({ chainId, address, service, symbol }) { // default service by network if not specified if (!service) { // eslint-disable-next-line no-param-reassign @@ -153,11 +157,11 @@ export default async function getBuyUrl({ chainId, address, service }) { case 'wyre': return await createWyrePurchaseUrl(address, chainId); case 'transak': - return createTransakUrl(address, chainId); + return createTransakUrl(address, chainId, symbol); case 'moonpay': return createMoonPayUrl(address, chainId); case 'coinbase': - return createCoinbasePayUrl(address, chainId); + return createCoinbasePayUrl(address, chainId, symbol); case 'metamask-faucet': return 'https://faucet.metamask.io/'; case 'rinkeby-faucet': diff --git a/app/scripts/lib/buy-url.test.js b/app/scripts/lib/buy-url.test.js index 813bb193c..188153f6c 100644 --- a/app/scripts/lib/buy-url.test.js +++ b/app/scripts/lib/buy-url.test.js @@ -62,36 +62,27 @@ describe('buy-url', () => { it('returns Transak url with an ETH address for Ethereum mainnet', async () => { const transakUrl = await getBuyUrl({ ...MAINNET, service: 'transak' }); const buyableChain = BUYABLE_CHAINS_MAP[MAINNET.chainId]; - const buyableCurrencies = encodeURIComponent( - buyableChain.transakCurrencies.join(','), - ); expect(transakUrl).toStrictEqual( - `https://global.transak.com/?apiKey=${TRANSAK_API_KEY}&hostURL=https%3A%2F%2Fmetamask.io&cryptoCurrencyList=${buyableCurrencies}&defaultCryptoCurrency=${buyableChain.transakCurrencies[0]}&networks=${buyableChain.network}&walletAddress=${ETH_ADDRESS}`, + `https://global.transak.com/?apiKey=${TRANSAK_API_KEY}&hostURL=https%3A%2F%2Fmetamask.io&defaultCryptoCurrency=${buyableChain.transakCurrencies[0]}&networks=${buyableChain.network}&walletAddress=${ETH_ADDRESS}`, ); }); it('returns Transak url with an BNB address for Binance Smart Chain', async () => { const transakUrl = await getBuyUrl({ ...BSC, service: 'transak' }); const buyableChain = BUYABLE_CHAINS_MAP[BSC.chainId]; - const buyableCurrencies = encodeURIComponent( - buyableChain.transakCurrencies.join(','), - ); expect(transakUrl).toStrictEqual( - `https://global.transak.com/?apiKey=${TRANSAK_API_KEY}&hostURL=https%3A%2F%2Fmetamask.io&cryptoCurrencyList=${buyableCurrencies}&defaultCryptoCurrency=${buyableChain.transakCurrencies[0]}&networks=${buyableChain.network}&walletAddress=${ETH_ADDRESS}`, + `https://global.transak.com/?apiKey=${TRANSAK_API_KEY}&hostURL=https%3A%2F%2Fmetamask.io&defaultCryptoCurrency=${buyableChain.transakCurrencies[0]}&networks=${buyableChain.network}&walletAddress=${ETH_ADDRESS}`, ); }); it('returns Transak url with an MATIC address for Polygon', async () => { const transakUrl = await getBuyUrl({ ...POLYGON, service: 'transak' }); const buyableChain = BUYABLE_CHAINS_MAP[POLYGON.chainId]; - const buyableCurrencies = encodeURIComponent( - buyableChain.transakCurrencies.join(','), - ); expect(transakUrl).toStrictEqual( - `https://global.transak.com/?apiKey=${TRANSAK_API_KEY}&hostURL=https%3A%2F%2Fmetamask.io&cryptoCurrencyList=${buyableCurrencies}&defaultCryptoCurrency=${buyableChain.transakCurrencies[0]}&networks=${buyableChain.network}&walletAddress=${ETH_ADDRESS}`, + `https://global.transak.com/?apiKey=${TRANSAK_API_KEY}&hostURL=https%3A%2F%2Fmetamask.io&defaultCryptoCurrency=${buyableChain.transakCurrencies[0]}&networks=${buyableChain.network}&walletAddress=${ETH_ADDRESS}`, ); }); diff --git a/development/ts-migration-dashboard/files-to-convert.json b/development/ts-migration-dashboard/files-to-convert.json index e8cc2681f..3a184644d 100644 --- a/development/ts-migration-dashboard/files-to-convert.json +++ b/development/ts-migration-dashboard/files-to-convert.json @@ -459,6 +459,9 @@ "ui/components/app/currency-input/currency-input.stories.js", "ui/components/app/currency-input/currency-input.test.js", "ui/components/app/currency-input/index.js", + "ui/components/app/deposit-popover/on-ramp-item.js", + "ui/components/app/deposit-popover/deposit-popover.js", + "ui/components/app/deposit-popover/index.js", "ui/components/app/detected-token/detected-token-address/detected-token-address.js", "ui/components/app/detected-token/detected-token-address/detected-token-address.stories.js", "ui/components/app/detected-token/detected-token-address/detected-token-address.test.js", @@ -624,9 +627,6 @@ "ui/components/app/modals/convert-token-to-nft-modal/convert-token-to-nft-modal.js", "ui/components/app/modals/customize-nonce/customize-nonce.component.js", "ui/components/app/modals/customize-nonce/index.js", - "ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js", - "ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js", - "ui/components/app/modals/deposit-ether-modal/index.js", "ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js", "ui/components/app/modals/edit-approval-permission/edit-approval-permission.container.js", "ui/components/app/modals/edit-approval-permission/index.js", diff --git a/shared/constants/network.ts b/shared/constants/network.ts index ab8e6d575..45a7d7d03 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -18,6 +18,11 @@ export type ChainId = typeof CHAIN_IDS[keyof typeof CHAIN_IDS]; * or dapp may supply their own symbol. */ type CurrencySymbol = typeof CURRENCY_SYMBOLS[keyof typeof CURRENCY_SYMBOLS]; +/** + * A type that is a union type for the supported symbols on different onramp providers. + */ +type SupportedCurrencySymbol = + typeof SUPPORTED_CURRENCY_SYMBOLS[keyof typeof SUPPORTED_CURRENCY_SYMBOLS]; /** * For certain specific situations we need the above type, but with all symbols * in lowercase format. @@ -137,7 +142,7 @@ type BuyableChainSettings = { /** * The list of supported currencies for the Transak onramp provider */ - transakCurrencies?: CurrencySymbol[]; + transakCurrencies?: SupportedCurrencySymbol[]; /** * A configuration object for the MoonPay onramp provider */ @@ -149,7 +154,7 @@ type BuyableChainSettings = { /** * The list of supported currencies for the CoinbasePay onramp provider */ - coinbasePayCurrencies?: CurrencySymbol[]; + coinbasePayCurrencies?: SupportedCurrencySymbol[]; }; /** @@ -293,6 +298,121 @@ export const CURRENCY_SYMBOLS = { WETH: 'WETH', } as const; +/** + * An object containing the token symbols for various tokens that are supported + * on different on ramp providers. This object is meant for internal consumption, + * hence why it is not exported. + */ +const SUPPORTED_CURRENCY_SYMBOLS = { + ...CURRENCY_SYMBOLS, + '1INCH': '1INCH', + AAVE: 'AAVE', + ABT: 'ABT', + ACH: 'ACH', + AGEUR: 'AGEUR', + AGLD: 'AGLD', + AMP: 'AMP', + ANKR: 'ANKR', + APE: 'APE', + ARPA: 'ARPA', + ASM: 'ASM', + AUCTION: 'AUCTION', + AXS: 'AXS', + BADGER: 'BADGER', + BAL: 'BAL', + BAND: 'BAND', + BAT: 'BAT', + BNT: 'BNT', + BOBA: 'BOBA', + BOND: 'BOND', + BTRST: 'BTRST', + CHAIN: 'CHAIN', + CHZ: 'CHZ', + CLV: 'CLV', + COMP: 'COMP', + COTI: 'COTI', + CRO: 'CRO', + CRV: 'CRV', + CTSI: 'CTSI', + CVC: 'CVC', + DAO: 'DAO', + DDX: 'DDX', + DNT: 'DNT', + ENJ: 'ENJ', + ENS: 'ENS', + EURT: 'EURT', + FARM: 'FARM', + FET: 'FET', + FORTH: 'FORTH', + FX: 'FX', + GNO: 'GNO', + GRT: 'GRT', + GTC: 'GTC', + GTH: 'GTH', + HEX: 'HEX', + IOTX: 'IOTX', + JASMY: 'JASMY', + KEEP: 'KEEP', + KNC: 'KNC', + KRL: 'KRL', + LCX: 'LCX', + LINK: 'LINK', + LPT: 'LPT', + LRC: 'LRC', + MANA: 'MANA', + MASK: 'MASK', + MINDS: 'MINDS', + MIR: 'MIR', + MKR: 'MKR', + MLN: 'MLN', + MTL: 'MTL', + NKN: 'NKN', + NMR: 'NMR', + NU: 'NU', + OGN: 'OGN', + OMG: 'OMG', + OXT: 'OXT', + PAX: 'PAX', + PERP: 'PERP', + PLA: 'PLA', + POLS: 'POLS', + POLY: 'POLY', + QNT: 'QNT', + QUICK: 'QUICK', + RAD: 'RAD', + RAI: 'RAI', + RARI: 'RARI', + REN: 'REN', + REP: 'REP', + REQ: 'REQ', + RLC: 'RLC', + RLY: 'RLY', + SAND: 'SAND', + SHIB: 'SHIB', + SKL: 'SKL', + SNX: 'SNX', + STETH: 'STETH', + STORJ: 'STORJ', + SUKU: 'SUKU', + SUSHI: 'SUSHI', + SWAP: 'SWAP', + SWFTC: 'SWFTC', + TRAC: 'TRAC', + TRB: 'TRB', + TRIBE: 'TRIBE', + TRU: 'TRU', + TXL: 'TXL', + UMA: 'UMA', + UNI: 'UNI', + VRA: 'VRA', + WBTC: 'WBTC', + WCFG: 'WCFG', + XYO: 'XYO', + YFII: 'YFII', + YLD: 'YLD', + ZRX: 'ZRX', +} as const; + export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.svg'; export const TEST_ETH_TOKEN_IMAGE_URL = './images/black-eth-logo.svg'; export const BNB_TOKEN_IMAGE_URL = './images/bnb.png'; @@ -529,7 +649,41 @@ export const BUYABLE_CHAINS_MAP: { [CHAIN_IDS.MAINNET]: { nativeCurrency: CURRENCY_SYMBOLS.ETH, network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME, - transakCurrencies: [CURRENCY_SYMBOLS.ETH, 'USDT', 'USDC', 'DAI'], + transakCurrencies: [ + SUPPORTED_CURRENCY_SYMBOLS.ETH, + SUPPORTED_CURRENCY_SYMBOLS['1INCH'], + SUPPORTED_CURRENCY_SYMBOLS.AAVE, + SUPPORTED_CURRENCY_SYMBOLS.AGEUR, + SUPPORTED_CURRENCY_SYMBOLS.BUSD, + SUPPORTED_CURRENCY_SYMBOLS.CHAIN, + SUPPORTED_CURRENCY_SYMBOLS.CLV, + SUPPORTED_CURRENCY_SYMBOLS.COMP, + SUPPORTED_CURRENCY_SYMBOLS.CTSI, + SUPPORTED_CURRENCY_SYMBOLS.DAI, + SUPPORTED_CURRENCY_SYMBOLS.DAO, + SUPPORTED_CURRENCY_SYMBOLS.ENJ, + SUPPORTED_CURRENCY_SYMBOLS.EURT, + SUPPORTED_CURRENCY_SYMBOLS.GTH, + SUPPORTED_CURRENCY_SYMBOLS.HEX, + SUPPORTED_CURRENCY_SYMBOLS.LINK, + SUPPORTED_CURRENCY_SYMBOLS.MANA, + SUPPORTED_CURRENCY_SYMBOLS.MASK, + SUPPORTED_CURRENCY_SYMBOLS.MINDS, + SUPPORTED_CURRENCY_SYMBOLS.MKR, + SUPPORTED_CURRENCY_SYMBOLS.PLA, + SUPPORTED_CURRENCY_SYMBOLS.POLS, + SUPPORTED_CURRENCY_SYMBOLS.SAND, + SUPPORTED_CURRENCY_SYMBOLS.STETH, + SUPPORTED_CURRENCY_SYMBOLS.SUSHI, + SUPPORTED_CURRENCY_SYMBOLS.SWAP, + SUPPORTED_CURRENCY_SYMBOLS.TXL, + SUPPORTED_CURRENCY_SYMBOLS.UNI, + SUPPORTED_CURRENCY_SYMBOLS.USDC, + SUPPORTED_CURRENCY_SYMBOLS.USDT, + SUPPORTED_CURRENCY_SYMBOLS.VRA, + SUPPORTED_CURRENCY_SYMBOLS.WBTC, + SUPPORTED_CURRENCY_SYMBOLS.YLD, + ], moonPay: { defaultCurrencyCode: 'eth', showOnlyCurrencies: 'eth,usdt,usdc,dai', @@ -538,7 +692,106 @@ export const BUYABLE_CHAINS_MAP: { srn: 'ethereum', currencyCode: CURRENCY_SYMBOLS.ETH, }, - coinbasePayCurrencies: [CURRENCY_SYMBOLS.ETH, 'USDC', 'DAI'], + coinbasePayCurrencies: [ + SUPPORTED_CURRENCY_SYMBOLS.ETH, + SUPPORTED_CURRENCY_SYMBOLS['1INCH'], + SUPPORTED_CURRENCY_SYMBOLS.AAVE, + SUPPORTED_CURRENCY_SYMBOLS.ABT, + SUPPORTED_CURRENCY_SYMBOLS.ACH, + SUPPORTED_CURRENCY_SYMBOLS.AGLD, + SUPPORTED_CURRENCY_SYMBOLS.AMP, + SUPPORTED_CURRENCY_SYMBOLS.ANKR, + SUPPORTED_CURRENCY_SYMBOLS.APE, + SUPPORTED_CURRENCY_SYMBOLS.ARPA, + SUPPORTED_CURRENCY_SYMBOLS.ASM, + SUPPORTED_CURRENCY_SYMBOLS.AUCTION, + SUPPORTED_CURRENCY_SYMBOLS.AXS, + SUPPORTED_CURRENCY_SYMBOLS.BADGER, + SUPPORTED_CURRENCY_SYMBOLS.BAL, + SUPPORTED_CURRENCY_SYMBOLS.BAND, + SUPPORTED_CURRENCY_SYMBOLS.BAT, + SUPPORTED_CURRENCY_SYMBOLS.BNT, + SUPPORTED_CURRENCY_SYMBOLS.BOBA, + SUPPORTED_CURRENCY_SYMBOLS.BOND, + SUPPORTED_CURRENCY_SYMBOLS.BTRST, + SUPPORTED_CURRENCY_SYMBOLS.CHZ, + SUPPORTED_CURRENCY_SYMBOLS.CLV, + SUPPORTED_CURRENCY_SYMBOLS.COMP, + SUPPORTED_CURRENCY_SYMBOLS.COTI, + SUPPORTED_CURRENCY_SYMBOLS.CRO, + SUPPORTED_CURRENCY_SYMBOLS.CRV, + SUPPORTED_CURRENCY_SYMBOLS.CTSI, + SUPPORTED_CURRENCY_SYMBOLS.CVC, + SUPPORTED_CURRENCY_SYMBOLS.DAI, + SUPPORTED_CURRENCY_SYMBOLS.DDX, + SUPPORTED_CURRENCY_SYMBOLS.DNT, + SUPPORTED_CURRENCY_SYMBOLS.ENJ, + SUPPORTED_CURRENCY_SYMBOLS.ENS, + SUPPORTED_CURRENCY_SYMBOLS.FARM, + SUPPORTED_CURRENCY_SYMBOLS.FET, + SUPPORTED_CURRENCY_SYMBOLS.FORTH, + SUPPORTED_CURRENCY_SYMBOLS.FX, + SUPPORTED_CURRENCY_SYMBOLS.GNO, + SUPPORTED_CURRENCY_SYMBOLS.GRT, + SUPPORTED_CURRENCY_SYMBOLS.GTC, + SUPPORTED_CURRENCY_SYMBOLS.IOTX, + SUPPORTED_CURRENCY_SYMBOLS.JASMY, + SUPPORTED_CURRENCY_SYMBOLS.KEEP, + SUPPORTED_CURRENCY_SYMBOLS.KNC, + SUPPORTED_CURRENCY_SYMBOLS.KRL, + SUPPORTED_CURRENCY_SYMBOLS.LCX, + SUPPORTED_CURRENCY_SYMBOLS.LINK, + SUPPORTED_CURRENCY_SYMBOLS.LPT, + SUPPORTED_CURRENCY_SYMBOLS.LRC, + SUPPORTED_CURRENCY_SYMBOLS.MANA, + SUPPORTED_CURRENCY_SYMBOLS.MASK, + SUPPORTED_CURRENCY_SYMBOLS.MATIC, + SUPPORTED_CURRENCY_SYMBOLS.MIR, + SUPPORTED_CURRENCY_SYMBOLS.MKR, + SUPPORTED_CURRENCY_SYMBOLS.MLN, + SUPPORTED_CURRENCY_SYMBOLS.MTL, + SUPPORTED_CURRENCY_SYMBOLS.NKN, + SUPPORTED_CURRENCY_SYMBOLS.NMR, + SUPPORTED_CURRENCY_SYMBOLS.NU, + SUPPORTED_CURRENCY_SYMBOLS.OGN, + SUPPORTED_CURRENCY_SYMBOLS.OMG, + SUPPORTED_CURRENCY_SYMBOLS.OXT, + SUPPORTED_CURRENCY_SYMBOLS.PAX, + SUPPORTED_CURRENCY_SYMBOLS.PERP, + SUPPORTED_CURRENCY_SYMBOLS.PLA, + SUPPORTED_CURRENCY_SYMBOLS.POLY, + SUPPORTED_CURRENCY_SYMBOLS.QNT, + SUPPORTED_CURRENCY_SYMBOLS.QUICK, + SUPPORTED_CURRENCY_SYMBOLS.RAD, + SUPPORTED_CURRENCY_SYMBOLS.RAI, + SUPPORTED_CURRENCY_SYMBOLS.RARI, + SUPPORTED_CURRENCY_SYMBOLS.REN, + SUPPORTED_CURRENCY_SYMBOLS.REP, + SUPPORTED_CURRENCY_SYMBOLS.REQ, + SUPPORTED_CURRENCY_SYMBOLS.RLC, + SUPPORTED_CURRENCY_SYMBOLS.RLY, + SUPPORTED_CURRENCY_SYMBOLS.SAND, + SUPPORTED_CURRENCY_SYMBOLS.SHIB, + SUPPORTED_CURRENCY_SYMBOLS.SKL, + SUPPORTED_CURRENCY_SYMBOLS.SNX, + SUPPORTED_CURRENCY_SYMBOLS.STORJ, + SUPPORTED_CURRENCY_SYMBOLS.SUKU, + SUPPORTED_CURRENCY_SYMBOLS.SUSHI, + SUPPORTED_CURRENCY_SYMBOLS.SWFTC, + SUPPORTED_CURRENCY_SYMBOLS.TRAC, + SUPPORTED_CURRENCY_SYMBOLS.TRB, + SUPPORTED_CURRENCY_SYMBOLS.TRIBE, + SUPPORTED_CURRENCY_SYMBOLS.TRU, + SUPPORTED_CURRENCY_SYMBOLS.UMA, + SUPPORTED_CURRENCY_SYMBOLS.UNI, + SUPPORTED_CURRENCY_SYMBOLS.USDC, + SUPPORTED_CURRENCY_SYMBOLS.USDT, + SUPPORTED_CURRENCY_SYMBOLS.WBTC, + SUPPORTED_CURRENCY_SYMBOLS.WCFG, + SUPPORTED_CURRENCY_SYMBOLS.XYO, + SUPPORTED_CURRENCY_SYMBOLS.YFII, + SUPPORTED_CURRENCY_SYMBOLS.ZRX, + ], }, [CHAIN_IDS.ROPSTEN]: { nativeCurrency: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.ROPSTEN], @@ -563,7 +816,10 @@ export const BUYABLE_CHAINS_MAP: { [CHAIN_IDS.BSC]: { nativeCurrency: CURRENCY_SYMBOLS.BNB, network: 'bsc', - transakCurrencies: [CURRENCY_SYMBOLS.BNB, CURRENCY_SYMBOLS.BUSD], + transakCurrencies: [ + SUPPORTED_CURRENCY_SYMBOLS.BNB, + SUPPORTED_CURRENCY_SYMBOLS.BUSD, + ], moonPay: { defaultCurrencyCode: 'bnb_bsc', showOnlyCurrencies: 'bnb_bsc,busd_bsc', @@ -573,10 +829,10 @@ export const BUYABLE_CHAINS_MAP: { nativeCurrency: CURRENCY_SYMBOLS.MATIC, network: 'polygon', transakCurrencies: [ - CURRENCY_SYMBOLS.MATIC, - CURRENCY_SYMBOLS.USDT, - CURRENCY_SYMBOLS.USDC, - CURRENCY_SYMBOLS.DAI, + SUPPORTED_CURRENCY_SYMBOLS.MATIC, + SUPPORTED_CURRENCY_SYMBOLS.USDT, + SUPPORTED_CURRENCY_SYMBOLS.USDC, + SUPPORTED_CURRENCY_SYMBOLS.DAI, ], moonPay: { defaultCurrencyCode: 'matic_polygon', @@ -590,7 +846,7 @@ export const BUYABLE_CHAINS_MAP: { [CHAIN_IDS.AVALANCHE]: { nativeCurrency: CURRENCY_SYMBOLS.AVALANCHE, network: 'avaxcchain', - transakCurrencies: [CURRENCY_SYMBOLS.AVALANCHE], + transakCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.AVALANCHE], moonPay: { defaultCurrencyCode: 'avax_cchain', showOnlyCurrencies: 'avax_cchain', @@ -599,17 +855,17 @@ export const BUYABLE_CHAINS_MAP: { srn: 'avalanche', currencyCode: CURRENCY_SYMBOLS.AVALANCHE, }, - coinbasePayCurrencies: [CURRENCY_SYMBOLS.AVALANCHE], + coinbasePayCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.AVALANCHE], }, [CHAIN_IDS.FANTOM]: { nativeCurrency: CURRENCY_SYMBOLS.FANTOM, network: 'fantom', - transakCurrencies: [CURRENCY_SYMBOLS.FANTOM], + transakCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.FANTOM], }, [CHAIN_IDS.CELO]: { nativeCurrency: CURRENCY_SYMBOLS.CELO, network: 'celo', - transakCurrencies: [CURRENCY_SYMBOLS.CELO], + transakCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.CELO], moonPay: { defaultCurrencyCode: 'celo', showOnlyCurrencies: 'celo', diff --git a/ui/components/app/deposit-popover/deposit-popover.js b/ui/components/app/deposit-popover/deposit-popover.js new file mode 100644 index 000000000..0a0a814ba --- /dev/null +++ b/ui/components/app/deposit-popover/deposit-popover.js @@ -0,0 +1,221 @@ +import PropTypes from 'prop-types'; +import React, { useContext } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; + +import { I18nContext } from '../../../contexts/i18n'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { + NETWORK_TO_NAME_MAP, + BUYABLE_CHAINS_MAP, +} from '../../../../shared/constants/network'; +import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics'; + +import LogoMoonPay from '../../ui/logo/logo-moonpay'; +import LogoWyre from '../../ui/logo/logo-wyre'; +import LogoTransak from '../../ui/logo/logo-transak'; +import LogoCoinbasePay from '../../ui/logo/logo-coinbasepay'; +import LogoDepositEth from '../../ui/logo/logo-deposit-eth'; +import Popover from '../../ui/popover'; + +import { buy, showModal, hideWarning } from '../../../store/actions'; +import { + getIsTestnet, + getCurrentChainId, + getSelectedAddress, + getIsBuyableTransakChain, + getIsBuyableMoonPayChain, + getIsBuyableWyreChain, + getIsBuyableCoinbasePayChain, + getIsBuyableCoinbasePayToken, + getIsBuyableTransakToken, +} from '../../../selectors/selectors'; + +import OnRampItem from './on-ramp-item'; + +const DepositPopover = ({ onClose, token }) => { + const isTokenDeposit = Boolean(token); + + const t = useContext(I18nContext); + const trackEvent = useContext(MetaMetricsContext); + const dispatch = useDispatch(); + + const chainId = useSelector(getCurrentChainId); + const isTestnet = useSelector(getIsTestnet); + const address = useSelector(getSelectedAddress); + const isBuyableTransakChain = useSelector(getIsBuyableTransakChain); + const isBuyableMoonPayChain = useSelector(getIsBuyableMoonPayChain); + const isBuyableWyreChain = useSelector(getIsBuyableWyreChain); + const isBuyableCoinbasePayChain = useSelector(getIsBuyableCoinbasePayChain); + + const isTokenBuyableCoinbasePay = useSelector((state) => + getIsBuyableCoinbasePayToken(state, token?.symbol), + ); + const isTokenBuyableTransak = useSelector((state) => + getIsBuyableTransakToken(state, token?.symbol), + ); + + const networkName = NETWORK_TO_NAME_MAP[chainId]; + const symbol = token + ? token.symbol + : BUYABLE_CHAINS_MAP[chainId].nativeCurrency; + + const showAccountDetailModal = () => { + dispatch(showModal({ name: 'ACCOUNT_DETAILS' })); + }; + const hideWarningMessage = () => { + dispatch(hideWarning()); + }; + + const toCoinbasePay = () => { + dispatch( + buy({ service: 'coinbase', address, chainId, symbol: token?.symbol }), + ); + }; + const toTransak = () => { + dispatch( + buy({ service: 'transak', address, chainId, symbol: token?.symbol }), + ); + }; + const toMoonPay = () => { + dispatch(buy({ service: 'moonpay', address, chainId })); + }; + const toWyre = () => { + dispatch(buy({ service: 'wyre', address, chainId })); + }; + const toFaucet = () => dispatch(buy({ chainId })); + + const goToAccountDetailsModal = () => { + hideWarningMessage(); + showAccountDetailModal(); + onClose(); + }; + + return ( + + } + title={t('buyCryptoWithCoinbasePay', [symbol])} + text={t('buyCryptoWithCoinbasePayDescription', [symbol])} + buttonLabel={t('continueToCoinbasePay')} + onButtonClick={() => { + trackEvent({ + category: EVENT.CATEGORIES.ACCOUNTS, + event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, + properties: { + onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.COINBASE, + }, + }); + toCoinbasePay(); + }} + hide={ + isTokenDeposit + ? !isBuyableCoinbasePayChain || !isTokenBuyableCoinbasePay + : !isBuyableCoinbasePayChain + } + /> + } + title={t('buyCryptoWithTransak', [symbol])} + text={t('buyCryptoWithTransakDescription', [symbol])} + buttonLabel={t('continueToTransak')} + onButtonClick={() => { + trackEvent({ + category: EVENT.CATEGORIES.ACCOUNTS, + event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, + properties: { + onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.TRANSAK, + }, + }); + toTransak(); + }} + hide={ + isTokenDeposit + ? !isBuyableTransakChain || !isTokenBuyableTransak + : !isBuyableTransakChain + } + /> + } + title={t('buyCryptoWithMoonPay', [symbol])} + text={t('buyCryptoWithMoonPayDescription', [symbol])} + buttonLabel={t('continueToMoonPay')} + onButtonClick={() => { + trackEvent({ + category: EVENT.CATEGORIES.ACCOUNTS, + event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, + properties: { + onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.MOONPAY, + }, + }); + toMoonPay(); + }} + hide={isTokenDeposit || !isBuyableMoonPayChain} + /> + + } + title={t('buyWithWyre', [symbol])} + text={t('buyWithWyreDescription', [symbol])} + buttonLabel={t('continueToWyre')} + onButtonClick={() => { + trackEvent({ + category: EVENT.CATEGORIES.ACCOUNTS, + event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, + properties: { + onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.WYRE, + }, + }); + toWyre(); + }} + hide={isTokenDeposit || !isBuyableWyreChain} + /> + + } + title={t('directDepositCrypto', [symbol])} + text={t('directDepositCryptoExplainer', [symbol])} + buttonLabel={t('viewAccount')} + onButtonClick={() => { + trackEvent({ + category: EVENT.CATEGORIES.ACCOUNTS, + event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, + properties: { + onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.SELF_DEPOSIT, + }, + }); + goToAccountDetailsModal(); + }} + hide={isTokenDeposit || !isBuyableWyreChain} + /> + + {networkName && ( + } + title={t('testFaucet')} + text={t('getEtherFromFaucet', [networkName])} + buttonLabel={t('getEther')} + onButtonClick={() => toFaucet()} + hide={!isTestnet} + /> + )} + + ); +}; + +DepositPopover.propTypes = { + onClose: PropTypes.func.isRequired, + token: PropTypes.shape({ + address: PropTypes.string.isRequired, + decimals: PropTypes.number, + symbol: PropTypes.string, + image: PropTypes.string, + aggregators: PropTypes.array, + isERC721: PropTypes.bool, + }), +}; + +export default DepositPopover; diff --git a/ui/components/app/deposit-popover/index.js b/ui/components/app/deposit-popover/index.js new file mode 100644 index 000000000..63c24927f --- /dev/null +++ b/ui/components/app/deposit-popover/index.js @@ -0,0 +1 @@ +export { default } from './deposit-popover'; diff --git a/ui/components/app/deposit-popover/on-ramp-item.js b/ui/components/app/deposit-popover/on-ramp-item.js new file mode 100644 index 000000000..505253592 --- /dev/null +++ b/ui/components/app/deposit-popover/on-ramp-item.js @@ -0,0 +1,67 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Button from '../../ui/button'; +import Box from '../../ui/box'; +import Typography from '../../ui/typography'; +import { COLORS, FRACTIONS } from '../../../helpers/constants/design-system'; + +const OnRampItem = ({ + logo, + title, + text, + buttonLabel, + onButtonClick, + hide = false, +}) => { + if (hide) { + return null; + } + return ( + + + {logo} + + {title} + + + {text} + + + + + + + ); +}; + +OnRampItem.propTypes = { + logo: PropTypes.node.isRequired, + title: PropTypes.string.isRequired, + text: PropTypes.string.isRequired, + buttonLabel: PropTypes.string.isRequired, + onButtonClick: PropTypes.func.isRequired, + hide: PropTypes.bool, +}; + +export default OnRampItem; diff --git a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js b/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js deleted file mode 100644 index 435ebf2a1..000000000 --- a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js +++ /dev/null @@ -1,243 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - NETWORK_TO_NAME_MAP, - BUYABLE_CHAINS_MAP, -} from '../../../../../shared/constants/network'; -import { - EVENT, - EVENT_NAMES, -} from '../../../../../shared/constants/metametrics'; -import Button from '../../../ui/button'; -import LogoMoonPay from '../../../ui/logo/logo-moonpay'; -import LogoWyre from '../../../ui/logo/logo-wyre'; -import LogoTransak from '../../../ui/logo/logo-transak'; -import LogoCoinbasePay from '../../../ui/logo/logo-coinbasepay'; -import LogoDepositEth from '../../../ui/logo/logo-deposit-eth'; - -export default class DepositEtherModal extends Component { - static contextTypes = { - t: PropTypes.func, - trackEvent: PropTypes.func.isRequired, - }; - - static propTypes = { - chainId: PropTypes.string.isRequired, - isTestnet: PropTypes.bool.isRequired, - isBuyableTransakChain: PropTypes.bool.isRequired, - isBuyableMoonPayChain: PropTypes.bool.isRequired, - isBuyableWyreChain: PropTypes.bool.isRequired, - isBuyableCoinbasePayChain: PropTypes.bool.isRequired, - toWyre: PropTypes.func.isRequired, - toTransak: PropTypes.func.isRequired, - toMoonPay: PropTypes.func.isRequired, - toCoinbasePay: PropTypes.func.isRequired, - address: PropTypes.string.isRequired, - toFaucet: PropTypes.func.isRequired, - hideWarning: PropTypes.func.isRequired, - hideModal: PropTypes.func.isRequired, - showAccountDetailModal: PropTypes.func.isRequired, - }; - - goToAccountDetailsModal = () => { - this.props.hideWarning(); - this.props.hideModal(); - this.props.showAccountDetailModal(); - }; - - renderRow({ - logo, - title, - text, - buttonLabel, - onButtonClick, - hide, - className, - hideButton, - hideTitle, - onBackClick, - showBackButton, - }) { - if (hide) { - return null; - } - - return ( -
- {onBackClick && showBackButton && ( -
- -
- )} -
- {logo} -
-
- {!hideTitle && ( -
- {title} -
- )} -
- {text} -
-
- {!hideButton && ( -
- -
- )} -
- ); - } - - render() { - const { - chainId, - toWyre, - toTransak, - toMoonPay, - toCoinbasePay, - address, - toFaucet, - isTestnet, - isBuyableTransakChain, - isBuyableMoonPayChain, - isBuyableWyreChain, - isBuyableCoinbasePayChain, - } = this.props; - const { t } = this.context; - const networkName = NETWORK_TO_NAME_MAP[chainId]; - const symbol = BUYABLE_CHAINS_MAP[chainId].nativeCurrency; - - return ( -
-
-
- {t('depositCrypto', [symbol])} -
-
- {t('needCryptoInWallet', [symbol])} -
-
{ - this.props.hideWarning(); - this.props.hideModal(); - }} - /> -
-
-
- {this.renderRow({ - logo: , - title: t('buyCryptoWithCoinbasePay', [symbol]), - text: t('buyCryptoWithCoinbasePayDescription', [symbol]), - buttonLabel: t('continueToCoinbasePay'), - onButtonClick: () => { - this.context.trackEvent({ - category: EVENT.CATEGORIES.ACCOUNTS, - event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, - properties: { - onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.COINBASE, - }, - }); - toCoinbasePay(address, chainId); - }, - hide: !isBuyableCoinbasePayChain, - })} - {this.renderRow({ - logo: , - title: t('buyCryptoWithTransak', [symbol]), - text: t('buyCryptoWithTransakDescription', [symbol]), - buttonLabel: t('continueToTransak'), - onButtonClick: () => { - this.context.trackEvent({ - category: EVENT.CATEGORIES.ACCOUNTS, - event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, - properties: { - onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.TRANSAK, - }, - }); - toTransak(address, chainId); - }, - hide: !isBuyableTransakChain, - })} - {this.renderRow({ - logo: , - title: t('buyCryptoWithMoonPay', [symbol]), - text: t('buyCryptoWithMoonPayDescription', [symbol]), - buttonLabel: t('continueToMoonPay'), - onButtonClick: () => { - this.context.trackEvent({ - category: EVENT.CATEGORIES.ACCOUNTS, - event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, - properties: { - onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.MOONPAY, - }, - }); - toMoonPay(address, chainId); - }, - hide: !isBuyableMoonPayChain, - })} - {this.renderRow({ - logo: , - title: t('buyWithWyre', [symbol]), - text: t('buyWithWyreDescription', [symbol]), - buttonLabel: t('continueToWyre'), - onButtonClick: () => { - this.context.trackEvent({ - category: EVENT.CATEGORIES.ACCOUNTS, - event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, - properties: { - onramp_provider_type: EVENT.ONRAMP_PROVIDER_TYPES.WYRE, - }, - }); - toWyre(address, chainId); - }, - hide: !isBuyableWyreChain, - })} - {this.renderRow({ - logo: ( - - ), - title: t('directDepositCrypto', [symbol]), - text: t('directDepositCryptoExplainer', [symbol]), - buttonLabel: t('viewAccount'), - onButtonClick: () => { - this.context.trackEvent({ - category: EVENT.CATEGORIES.ACCOUNTS, - event: EVENT_NAMES.ONRAMP_PROVIDER_SELECTED, - properties: { - onramp_provider_type: - EVENT.ONRAMP_PROVIDER_TYPES.SELF_DEPOSIT, - }, - }); - this.goToAccountDetailsModal(); - }, - })} - {networkName && - this.renderRow({ - logo: , - title: t('testFaucet'), - text: t('getEtherFromFaucet', [networkName]), - buttonLabel: t('getEther'), - onButtonClick: () => toFaucet(chainId), - hide: !isTestnet, - })} -
-
-
- ); - } -} diff --git a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js b/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js deleted file mode 100644 index 3b71d5740..000000000 --- a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js +++ /dev/null @@ -1,60 +0,0 @@ -import { connect } from 'react-redux'; -import { - buyEth, - hideModal, - showModal, - hideWarning, -} from '../../../../store/actions'; -import { - getIsTestnet, - getIsMainnet, - getCurrentChainId, - getSelectedAddress, - getIsBuyableTransakChain, - getIsBuyableMoonPayChain, - getIsBuyableWyreChain, - getIsBuyableCoinbasePayChain, -} from '../../../../selectors/selectors'; -import DepositEtherModal from './deposit-ether-modal.component'; - -function mapStateToProps(state) { - return { - chainId: getCurrentChainId(state), - isTestnet: getIsTestnet(state), - isMainnet: getIsMainnet(state), - address: getSelectedAddress(state), - isBuyableTransakChain: getIsBuyableTransakChain(state), - isBuyableMoonPayChain: getIsBuyableMoonPayChain(state), - isBuyableWyreChain: getIsBuyableWyreChain(state), - isBuyableCoinbasePayChain: getIsBuyableCoinbasePayChain(state), - }; -} - -function mapDispatchToProps(dispatch) { - return { - toWyre: (address, chainId) => { - dispatch(buyEth({ service: 'wyre', address, chainId })); - }, - toTransak: (address, chainId) => { - dispatch(buyEth({ service: 'transak', address, chainId })); - }, - toMoonPay: (address, chainId) => { - dispatch(buyEth({ service: 'moonpay', address, chainId })); - }, - toCoinbasePay: (address, chainId) => { - dispatch(buyEth({ service: 'coinbase', address, chainId })); - }, - hideModal: () => { - dispatch(hideModal()); - }, - hideWarning: () => { - dispatch(hideWarning()); - }, - showAccountDetailModal: () => { - dispatch(showModal({ name: 'ACCOUNT_DETAILS' })); - }, - toFaucet: (chainId) => dispatch(buyEth({ chainId })), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(DepositEtherModal); diff --git a/ui/components/app/modals/deposit-ether-modal/index.js b/ui/components/app/modals/deposit-ether-modal/index.js deleted file mode 100644 index 02f355cb0..000000000 --- a/ui/components/app/modals/deposit-ether-modal/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './deposit-ether-modal.container'; diff --git a/ui/components/app/modals/deposit-ether-modal/index.scss b/ui/components/app/modals/deposit-ether-modal/index.scss deleted file mode 100644 index 9c366d0b8..000000000 --- a/ui/components/app/modals/deposit-ether-modal/index.scss +++ /dev/null @@ -1,118 +0,0 @@ -.deposit-ether-modal { - border-radius: 8px; - display: flex; - flex-flow: column; - height: 100%; - - &__buy-rows { - width: 100%; - padding: 0 30px; - display: flex; - flex-flow: column nowrap; - flex: 1; - align-items: center; - - @include screen-sm-max { - height: 0; - } - } - - &__logo { - max-height: 40px; - background-repeat: no-repeat; - background-size: contain; - background-position: center; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - - &--lg { - max-height: 60px; - } - - @include screen-sm-min { - height: 60px; - } - } - - &__buy-row { - border-bottom: 1px solid var(--color-border-default); - display: flex; - justify-content: space-between; - align-items: center; - flex: 1 0 auto; - padding: 30px 0 20px; - min-height: 170px; - - @include screen-sm-max { - min-height: 270px; - flex-flow: column; - justify-content: flex-start; - } - - &__back { - position: absolute; - top: 10px; - left: 0; - } - - &__logo-container { - display: flex; - justify-content: center; - flex: 0 0 auto; - padding: 0 20px; - - @include screen-sm-min { - width: 12rem; - } - - @include screen-sm-max { - width: 100%; - max-height: 6rem; - padding-bottom: 20px; - } - } - - &__right { - display: flex; - } - - &__description { - color: var(--color-text-alternative); - padding-bottom: 20px; - align-self: flex-start; - - @include screen-sm-min { - width: 15rem; - } - - &__title { - @include H4; - } - - &__text { - @include H6; - - margin-top: 7px; - } - } - - &__button { - display: flex; - justify-content: flex-end; - - @include screen-sm-min { - min-width: 300px; - } - } - } - - &__buy-row:last-of-type { - border-bottom: 0; - } - - &__deposit-button { - width: 257px !important; - } -} diff --git a/ui/components/app/modals/index.scss b/ui/components/app/modals/index.scss index 49e0063ab..f4467b153 100644 --- a/ui/components/app/modals/index.scss +++ b/ui/components/app/modals/index.scss @@ -2,7 +2,6 @@ @import 'account-modal-container/index'; @import 'cancel-transaction/index'; @import 'confirm-remove-account/index'; -@import 'deposit-ether-modal/index'; @import 'edit-approval-permission/index'; @import 'export-private-key-modal/index'; @import 'hide-token-confirmation-modal/index'; diff --git a/ui/components/app/modals/modal.js b/ui/components/app/modals/modal.js index 4df937f2d..f8442e2f7 100644 --- a/ui/components/app/modals/modal.js +++ b/ui/components/app/modals/modal.js @@ -10,7 +10,6 @@ import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; // Modal Components import ConfirmCustomizeGasModal from '../gas-customization/gas-modal-page-container'; -import DepositEtherModal from './deposit-ether-modal'; import AccountDetailsModal from './account-details-modal'; import ExportPrivateKeyModal from './export-private-key-modal'; import HideTokenConfirmationModal from './hide-token-confirmation-modal'; @@ -79,39 +78,6 @@ const accountModalStyle = { }; const MODALS = { - DEPOSIT_ETHER: { - contents: , - onHide: (props) => props.hideWarning(), - mobileModalStyle: { - width: '100%', - height: '100%', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - boxShadow: 'var(--shadow-size-sm) var(--color-shadow-default)', - top: '0', - display: 'flex', - }, - laptopModalStyle: { - width: 'initial', - maxWidth: '850px', - top: 'calc(10% + 10px)', - left: '0', - right: '0', - margin: '0 auto', - boxShadow: 'var(--shadow-size-sm) var(--color-shadow-default)', - borderRadius: '7px', - transform: 'none', - height: 'calc(80% - 20px)', - overflowY: 'hidden', - }, - contentStyle: { - borderRadius: '7px', - height: '100%', - }, - }, - NEW_ACCOUNT: { contents: , mobileModalStyle: { diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js index 1c389cea4..5a8775720 100644 --- a/ui/components/app/wallet-overview/eth-overview.js +++ b/ui/components/app/wallet-overview/eth-overview.js @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useState } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import classnames from 'classnames'; @@ -13,7 +13,6 @@ import { import Tooltip from '../../ui/tooltip'; import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'; -import { showModal } from '../../../store/actions'; import { isBalanceCached, getShouldShowFiat, @@ -35,6 +34,7 @@ import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics'; import Spinner from '../../ui/spinner'; import { startNewDraftTransaction } from '../../../ducks/send'; import { ASSET_TYPES } from '../../../../shared/constants/transaction'; +import DepositPopover from '../deposit-popover'; import WalletOverview from './wallet-overview'; const EthOverview = ({ className }) => { @@ -42,6 +42,7 @@ const EthOverview = ({ className }) => { const t = useContext(I18nContext); const trackEvent = useContext(MetaMetricsContext); const history = useHistory(); + const [showDepositPopover, setShowDepositPopover] = useState(false); const keyring = useSelector(getCurrentKeyring); const usingHardwareWallet = isHardwareKeyring(keyring?.type); const balanceIsCached = useSelector(isBalanceCached); @@ -53,136 +54,141 @@ const EthOverview = ({ className }) => { const defaultSwapsToken = useSelector(getSwapsDefaultToken); return ( - -
-
- {balance ? ( + <> + {showDepositPopover && ( + setShowDepositPopover(false)} /> + )} + +
+
+ {balance ? ( + + ) : ( + + )} + {balanceIsCached ? ( + * + ) : null} +
+ {showFiat && balance && ( - ) : ( - )} - {balanceIsCached ? ( - * - ) : null}
- {showFiat && balance && ( - - )} -
- - } - buttons={ - <> - { - trackEvent({ - event: EVENT_NAMES.NAV_BUY_BUTTON_CLICKED, - category: EVENT.CATEGORIES.NAVIGATION, - properties: { - location: 'Home', - text: 'Buy', - }, - }); - dispatch(showModal({ name: 'DEPOSIT_ETHER' })); - }} - /> - { - trackEvent({ - event: EVENT_NAMES.NAV_SEND_BUTTON_CLICKED, - category: EVENT.CATEGORIES.NAVIGATION, - properties: { - token_symbol: 'ETH', - location: 'Home', - text: 'Send', - }, - }); - dispatch( - startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }), - ).then(() => { - history.push(SEND_ROUTE); - }); - }} - /> - { - if (isSwapsChain) { + + } + buttons={ + <> + { + trackEvent({ + event: EVENT_NAMES.NAV_BUY_BUTTON_CLICKED, + category: EVENT.CATEGORIES.NAVIGATION, + properties: { + location: 'Home', + text: 'Buy', + }, + }); + setShowDepositPopover(true); + }} + /> + { trackEvent({ - event: EVENT_NAMES.NAV_SWAP_BUTTON_CLICKED, - category: EVENT.CATEGORIES.SWAPS, + event: EVENT_NAMES.NAV_SEND_BUTTON_CLICKED, + category: EVENT.CATEGORIES.NAVIGATION, properties: { token_symbol: 'ETH', - location: EVENT.SOURCE.SWAPS.MAIN_VIEW, - text: 'Swap', + location: 'Home', + text: 'Send', }, }); - dispatch(setSwapsFromToken(defaultSwapsToken)); - if (usingHardwareWallet) { - global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE); - } else { - history.push(BUILD_QUOTE_ROUTE); + dispatch( + startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }), + ).then(() => { + history.push(SEND_ROUTE); + }); + }} + /> + { + if (isSwapsChain) { + trackEvent({ + event: EVENT_NAMES.NAV_SWAP_BUTTON_CLICKED, + category: EVENT.CATEGORIES.SWAPS, + properties: { + token_symbol: 'ETH', + location: EVENT.SOURCE.SWAPS.MAIN_VIEW, + text: 'Swap', + }, + }); + dispatch(setSwapsFromToken(defaultSwapsToken)); + if (usingHardwareWallet) { + global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE); + } else { + history.push(BUILD_QUOTE_ROUTE); + } } + }} + label={t('swap')} + tooltipRender={ + isSwapsChain + ? null + : (contents) => ( + + {contents} + + ) } - }} - label={t('swap')} - tooltipRender={ - isSwapsChain - ? null - : (contents) => ( - - {contents} - - ) - } - /> - - } - className={className} - icon={} - /> + /> + + } + className={className} + icon={} + /> + ); }; diff --git a/ui/components/app/wallet-overview/token-overview.js b/ui/components/app/wallet-overview/token-overview.js index 3bb6dda96..5142c5e8c 100644 --- a/ui/components/app/wallet-overview/token-overview.js +++ b/ui/components/app/wallet-overview/token-overview.js @@ -1,4 +1,4 @@ -import React, { useContext, useEffect } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; @@ -19,8 +19,11 @@ import { setSwapsFromToken } from '../../../ducks/swaps/swaps'; import { getCurrentKeyring, getIsSwapsChain, + getIsBuyableCoinbasePayToken, + getIsBuyableTransakToken, } from '../../../selectors/selectors'; +import BuyIcon from '../../ui/icon/overview-buy-icon.component'; import SwapIcon from '../../ui/icon/swap-icon.component'; import SendIcon from '../../ui/icon/overview-send-icon.component'; @@ -30,6 +33,7 @@ import { showModal } from '../../../store/actions'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics'; import { ASSET_TYPES } from '../../../../shared/constants/transaction'; +import DepositPopover from '../deposit-popover'; import WalletOverview from './wallet-overview'; const TokenOverview = ({ className, token }) => { @@ -37,6 +41,7 @@ const TokenOverview = ({ className, token }) => { const t = useContext(I18nContext); const trackEvent = useContext(MetaMetricsContext); const history = useHistory(); + const [showDepositPopover, setShowDepositPopover] = useState(false); const keyring = useSelector(getCurrentKeyring); const usingHardwareWallet = isHardwareKeyring(keyring.type); const { tokensWithBalances } = useTokenTracker([token]); @@ -48,6 +53,13 @@ const TokenOverview = ({ className, token }) => { token.symbol, ); const isSwapsChain = useSelector(getIsSwapsChain); + const isTokenBuyableCoinbasePay = useSelector((state) => + getIsBuyableCoinbasePayToken(state, token.symbol), + ); + const isTokenBuyableTransak = useSelector((state) => + getIsBuyableTransakToken(state, token.symbol), + ); + const isBuyable = isTokenBuyableCoinbasePay || isTokenBuyableTransak; useEffect(() => { if (token.isERC721 && process.env.COLLECTIBLES_V1) { @@ -61,109 +73,140 @@ const TokenOverview = ({ className, token }) => { }, [token.isERC721, token.address, dispatch]); return ( - - - {formattedFiatBalance ? ( + <> + {showDepositPopover && ( + setShowDepositPopover(false)} + token={token} + /> + )} + - ) : null} -
- } - buttons={ - <> - { - trackEvent({ - event: EVENT_NAMES.NAV_SEND_BUTTON_CLICKED, - category: EVENT.CATEGORIES.NAVIGATION, - properties: { - token_symbol: token.symbol, - location: EVENT.SOURCE.SWAPS.TOKEN_VIEW, - text: 'Send', - }, - }); - try { - await dispatch( - startNewDraftTransaction({ - type: ASSET_TYPES.TOKEN, - details: token, - }), - ); - history.push(SEND_ROUTE); - } catch (err) { - if (!err.message.includes(INVALID_ASSET_TYPE)) { - throw err; - } - } - }} - Icon={SendIcon} - label={t('send')} - data-testid="eth-overview-send" - disabled={token.isERC721} - /> - { - if (isSwapsChain) { + {formattedFiatBalance ? ( + + ) : null} +
+ } + buttons={ + <> + {isBuyable && ( + { + trackEvent({ + event: 'Clicked Deposit: Token', + category: EVENT.CATEGORIES.NAVIGATION, + properties: { + action: 'Home', + legacy_event: true, + }, + }); + setShowDepositPopover(true); + }} + disabled={token.isERC721} + /> + )} + { trackEvent({ - event: EVENT_NAMES.NAV_SWAP_BUTTON_CLICKED, - category: EVENT.CATEGORIES.SWAPS, + event: EVENT_NAMES.NAV_SEND_BUTTON_CLICKED, + category: EVENT.CATEGORIES.NAVIGATION, properties: { token_symbol: token.symbol, location: EVENT.SOURCE.SWAPS.TOKEN_VIEW, - text: 'Swap', + text: 'Send', }, }); - dispatch( - setSwapsFromToken({ - ...token, - address: token.address.toLowerCase(), - iconUrl: token.image, - balance, - string: balanceToRender, - }), - ); - if (usingHardwareWallet) { - global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE); - } else { - history.push(BUILD_QUOTE_ROUTE); + try { + await dispatch( + startNewDraftTransaction({ + type: ASSET_TYPES.TOKEN, + details: token, + }), + ); + history.push(SEND_ROUTE); + } catch (err) { + if (!err.message.includes(INVALID_ASSET_TYPE)) { + throw err; + } + } + }} + Icon={SendIcon} + label={t('send')} + data-testid="eth-overview-send" + disabled={token.isERC721} + /> + { + if (isSwapsChain) { + trackEvent({ + event: EVENT_NAMES.NAV_SWAP_BUTTON_CLICKED, + category: EVENT.CATEGORIES.SWAPS, + properties: { + token_symbol: token.symbol, + location: EVENT.SOURCE.SWAPS.TOKEN_VIEW, + text: 'Swap', + }, + }); + dispatch( + setSwapsFromToken({ + ...token, + address: token.address.toLowerCase(), + iconUrl: token.image, + balance, + string: balanceToRender, + }), + ); + if (usingHardwareWallet) { + global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE); + } else { + history.push(BUILD_QUOTE_ROUTE); + } } + }} + label={t('swap')} + tooltipRender={ + isSwapsChain + ? null + : (contents) => ( + + {contents} + + ) } - }} - label={t('swap')} - tooltipRender={ - isSwapsChain - ? null - : (contents) => ( - - {contents} - - ) - } + /> + + } + className={className} + icon={ + - - } - className={className} - icon={ - - } - /> + } + /> + ); }; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index f1ccc3c8f..3c18d649e 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -708,6 +708,13 @@ export function getIsBuyableTransakChain(state) { return Boolean(BUYABLE_CHAINS_MAP?.[chainId]?.transakCurrencies); } +export function getIsBuyableTransakToken(state, symbol) { + const chainId = getCurrentChainId(state); + return Boolean( + BUYABLE_CHAINS_MAP?.[chainId]?.transakCurrencies?.includes(symbol), + ); +} + export function getIsBuyableMoonPayChain(state) { const chainId = getCurrentChainId(state); return Boolean(BUYABLE_CHAINS_MAP?.[chainId]?.moonPay); @@ -722,6 +729,13 @@ export function getIsBuyableCoinbasePayChain(state) { return Boolean(BUYABLE_CHAINS_MAP?.[chainId]?.coinbasePayCurrencies); } +export function getIsBuyableCoinbasePayToken(state, symbol) { + const chainId = getCurrentChainId(state); + return Boolean( + BUYABLE_CHAINS_MAP?.[chainId]?.coinbasePayCurrencies?.includes(symbol), + ); +} + export function getNativeCurrencyImage(state) { const nativeCurrency = getNativeCurrency(state)?.toUpperCase(); return NATIVE_CURRENCY_TOKEN_IMAGE_MAP[nativeCurrency]; diff --git a/ui/store/actionConstants.js b/ui/store/actionConstants.js index 2ec9e212c..6d7bb77cf 100644 --- a/ui/store/actionConstants.js +++ b/ui/store/actionConstants.js @@ -51,7 +51,7 @@ export const SET_HARDWARE_WALLET_DEFAULT_HD_PATH = export const SHOW_LOADING = 'SHOW_LOADING_INDICATION'; export const HIDE_LOADING = 'HIDE_LOADING_INDICATION'; -export const BUY_ETH = 'BUY_ETH'; +export const BUY = 'BUY'; export const TOGGLE_ACCOUNT_MENU = 'TOGGLE_ACCOUNT_MENU'; diff --git a/ui/store/actions.js b/ui/store/actions.js index aa22390b6..9ad80ca8c 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -2489,13 +2489,13 @@ export function showSendTokenPage() { }; } -export function buyEth(opts) { +export function buy(opts) { return async (dispatch) => { const url = await getBuyUrl(opts); if (url) { global.platform.openTab({ url }); dispatch({ - type: actionConstants.BUY_ETH, + type: actionConstants.BUY, }); } };