mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Ability to buy tokens with Moonpay (#15924)
* Ability to buy tokens with Moonpay * fix for test cases failing * updated description for MoonPayChainSettings type * removed test results
This commit is contained in:
parent
d520fc57cb
commit
3f801e377d
3
.gitignore
vendored
3
.gitignore
vendored
@ -53,3 +53,6 @@ notes.txt
|
||||
|
||||
# TypeScript
|
||||
tsout/
|
||||
|
||||
# Test results
|
||||
test-results/
|
@ -11,6 +11,7 @@ import {
|
||||
MOONPAY_API_KEY,
|
||||
COINBASEPAY_API_KEY,
|
||||
} from '../constants/on-ramp';
|
||||
import { formatMoonpaySymbol } from '../../../ui/helpers/utils/moonpay';
|
||||
|
||||
const fetchWithTimeout = getFetchWithTimeout();
|
||||
|
||||
@ -75,15 +76,17 @@ const createTransakUrl = (walletAddress, chainId, symbol) => {
|
||||
*
|
||||
* @param {string} walletAddress - Destination address
|
||||
* @param {string} chainId - Current chain ID
|
||||
* @param {string|undefined} symbol - Token symbol to buy
|
||||
* @returns String
|
||||
*/
|
||||
const createMoonPayUrl = async (walletAddress, chainId) => {
|
||||
const createMoonPayUrl = async (walletAddress, chainId, symbol) => {
|
||||
const { moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {} } =
|
||||
BUYABLE_CHAINS_MAP[chainId];
|
||||
const moonPayQueryParams = new URLSearchParams({
|
||||
apiKey: MOONPAY_API_KEY,
|
||||
walletAddress,
|
||||
defaultCurrencyCode,
|
||||
defaultCurrencyCode:
|
||||
formatMoonpaySymbol(symbol, chainId) || defaultCurrencyCode,
|
||||
showOnlyCurrencies,
|
||||
});
|
||||
const queryParams = new URLSearchParams({
|
||||
@ -159,7 +162,7 @@ export default async function getBuyUrl({ chainId, address, service, symbol }) {
|
||||
case 'transak':
|
||||
return createTransakUrl(address, chainId, symbol);
|
||||
case 'moonpay':
|
||||
return createMoonPayUrl(address, chainId);
|
||||
return createMoonPayUrl(address, chainId, symbol);
|
||||
case 'coinbase':
|
||||
return createCoinbasePayUrl(address, chainId, symbol);
|
||||
case 'metamask-faucet':
|
||||
|
@ -117,11 +117,11 @@ describe('buy-url', () => {
|
||||
nock(SWAPS_API_V2_BASE_URL)
|
||||
.get(`/moonpaySign/?${queryParams}`)
|
||||
.reply(200, {
|
||||
url: `https://buy.moonpay.com/?apiKey=${MOONPAY_API_KEY}&walletAddress=${MAINNET.address}&defaultCurrencyCode=${defaultCurrencyCode}&showOnlyCurrencies=eth%2Cusdt%2Cusdc%2Cdai&signature=laefTlgkESEc2hv8AZEH9F25VjLEJUADY27D6MccE54%3D`,
|
||||
url: `https://buy.moonpay.com/?apiKey=${MOONPAY_API_KEY}&walletAddress=${MAINNET.address}&defaultCurrencyCode=${defaultCurrencyCode}&showOnlyCurrencies=${showOnlyCurrencies}&signature=laefTlgkESEc2hv8AZEH9F25VjLEJUADY27D6MccE54%3D`,
|
||||
});
|
||||
const moonPayUrl = await getBuyUrl({ ...MAINNET, service: 'moonpay' });
|
||||
expect(moonPayUrl).toStrictEqual(
|
||||
`https://buy.moonpay.com/?apiKey=${MOONPAY_API_KEY}&walletAddress=${MAINNET.address}&defaultCurrencyCode=${defaultCurrencyCode}&showOnlyCurrencies=eth%2Cusdt%2Cusdc%2Cdai&signature=laefTlgkESEc2hv8AZEH9F25VjLEJUADY27D6MccE54%3D`,
|
||||
`https://buy.moonpay.com/?apiKey=${MOONPAY_API_KEY}&walletAddress=${MAINNET.address}&defaultCurrencyCode=${defaultCurrencyCode}&showOnlyCurrencies=${showOnlyCurrencies}&signature=laefTlgkESEc2hv8AZEH9F25VjLEJUADY27D6MccE54%3D`,
|
||||
);
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
@ -23,11 +23,6 @@ type CurrencySymbol = typeof CURRENCY_SYMBOLS[keyof typeof CURRENCY_SYMBOLS];
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
type LowercaseCurrencySymbol = Lowercase<CurrencySymbol>;
|
||||
/**
|
||||
* Test networks have special symbols that combine the network name and 'ETH'
|
||||
* so that they are distinct from mainnet and other networks that use 'ETH'.
|
||||
@ -40,7 +35,7 @@ export type TestNetworkCurrencySymbol =
|
||||
* inform the MoonPay API which network the user is attempting to onramp into.
|
||||
* This type reflects those possible values.
|
||||
*/
|
||||
type MoonPayNetworkAbbreviation = 'bsc' | 'cchain' | 'polygon';
|
||||
type MoonPayNetworkAbbreviation = 'BSC' | 'CCHAIN' | 'POLYGON';
|
||||
|
||||
/**
|
||||
* MoonPay requires some settings that are configured per network that it is
|
||||
@ -49,25 +44,20 @@ type MoonPayNetworkAbbreviation = 'bsc' | 'cchain' | 'polygon';
|
||||
type MoonPayChainSettings = {
|
||||
/**
|
||||
* What should the default onramp currency be, for example 'eth' on 'mainnet'
|
||||
* This type matches a single LowercaseCurrencySymbol or a
|
||||
* LowercaseCurrencySymbol and a MoonPayNetworkAbbreviation joined by a '_'.
|
||||
* This type matches a single SupportedCurrencySymbol or a
|
||||
* SupportedCurrencySymbol and a MoonPayNetworkAbbreviation joined by a '_'.
|
||||
*/
|
||||
defaultCurrencyCode:
|
||||
| LowercaseCurrencySymbol
|
||||
| `${LowercaseCurrencySymbol}_${MoonPayNetworkAbbreviation}`;
|
||||
| SupportedCurrencySymbol
|
||||
| `${SupportedCurrencySymbol}_${MoonPayNetworkAbbreviation}`;
|
||||
/**
|
||||
* We must also configure all possible onramp currencies we wish to support.
|
||||
* This type matches 1 to 3 LowercaseCurrencySymbols, joined by ','. It also
|
||||
* matches 1 or 2 LowercaseCurrencySymbols with a
|
||||
* MoonPayNetworkAbbreviation joined by a '_', and concatenated with ','.
|
||||
* This type matches either an array of SupportedCurrencySymbol or
|
||||
* an array of SupportedCurrencySymbol and a MoonPayNetworkAbbreviation joined by a '_'.
|
||||
*/
|
||||
showOnlyCurrencies:
|
||||
| `${LowercaseCurrencySymbol}`
|
||||
| `${LowercaseCurrencySymbol},${LowercaseCurrencySymbol}`
|
||||
| `${LowercaseCurrencySymbol},${LowercaseCurrencySymbol},${LowercaseCurrencySymbol}`
|
||||
| `${LowercaseCurrencySymbol},${LowercaseCurrencySymbol},${LowercaseCurrencySymbol},${LowercaseCurrencySymbol}`
|
||||
| `${LowercaseCurrencySymbol}_${MoonPayNetworkAbbreviation}`
|
||||
| `${LowercaseCurrencySymbol}_${MoonPayNetworkAbbreviation},${LowercaseCurrencySymbol}_${MoonPayNetworkAbbreviation}`;
|
||||
| SupportedCurrencySymbol[]
|
||||
| `${SupportedCurrencySymbol}_${MoonPayNetworkAbbreviation}`[];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -318,6 +308,7 @@ const SUPPORTED_CURRENCY_SYMBOLS = {
|
||||
ASM: 'ASM',
|
||||
AUCTION: 'AUCTION',
|
||||
AXS: 'AXS',
|
||||
AVAX: 'AVAX',
|
||||
BADGER: 'BADGER',
|
||||
BAL: 'BAL',
|
||||
BAND: 'BAND',
|
||||
@ -351,6 +342,7 @@ const SUPPORTED_CURRENCY_SYMBOLS = {
|
||||
GTH: 'GTH',
|
||||
HEX: 'HEX',
|
||||
IOTX: 'IOTX',
|
||||
IMX: 'IMX',
|
||||
JASMY: 'JASMY',
|
||||
KEEP: 'KEEP',
|
||||
KNC: 'KNC',
|
||||
@ -371,6 +363,7 @@ const SUPPORTED_CURRENCY_SYMBOLS = {
|
||||
NU: 'NU',
|
||||
OGN: 'OGN',
|
||||
OMG: 'OMG',
|
||||
ORN: 'ORN',
|
||||
OXT: 'OXT',
|
||||
PAX: 'PAX',
|
||||
PERP: 'PERP',
|
||||
@ -685,8 +678,17 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
SUPPORTED_CURRENCY_SYMBOLS.YLD,
|
||||
],
|
||||
moonPay: {
|
||||
defaultCurrencyCode: 'eth',
|
||||
showOnlyCurrencies: 'eth,usdt,usdc,dai',
|
||||
defaultCurrencyCode: SUPPORTED_CURRENCY_SYMBOLS.ETH,
|
||||
showOnlyCurrencies: [
|
||||
SUPPORTED_CURRENCY_SYMBOLS.ETH,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.USDT,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.USDC,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.DAI,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.MATIC,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.ORN,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.WETH,
|
||||
SUPPORTED_CURRENCY_SYMBOLS.IMX,
|
||||
],
|
||||
},
|
||||
wyre: {
|
||||
srn: 'ethereum',
|
||||
@ -821,8 +823,11 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
SUPPORTED_CURRENCY_SYMBOLS.BUSD,
|
||||
],
|
||||
moonPay: {
|
||||
defaultCurrencyCode: 'bnb_bsc',
|
||||
showOnlyCurrencies: 'bnb_bsc,busd_bsc',
|
||||
defaultCurrencyCode: `${SUPPORTED_CURRENCY_SYMBOLS.BNB}_BSC`,
|
||||
showOnlyCurrencies: [
|
||||
`${SUPPORTED_CURRENCY_SYMBOLS.BNB}_BSC`,
|
||||
`${SUPPORTED_CURRENCY_SYMBOLS.BUSD}_BSC`,
|
||||
],
|
||||
},
|
||||
},
|
||||
[CHAIN_IDS.POLYGON]: {
|
||||
@ -835,8 +840,11 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
SUPPORTED_CURRENCY_SYMBOLS.DAI,
|
||||
],
|
||||
moonPay: {
|
||||
defaultCurrencyCode: 'matic_polygon',
|
||||
showOnlyCurrencies: 'matic_polygon,usdc_polygon',
|
||||
defaultCurrencyCode: `${SUPPORTED_CURRENCY_SYMBOLS.BNB}_POLYGON`,
|
||||
showOnlyCurrencies: [
|
||||
`${SUPPORTED_CURRENCY_SYMBOLS.MATIC}_POLYGON`,
|
||||
`${SUPPORTED_CURRENCY_SYMBOLS.USDC}_POLYGON`,
|
||||
],
|
||||
},
|
||||
wyre: {
|
||||
srn: 'matic',
|
||||
@ -848,8 +856,8 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
network: 'avaxcchain',
|
||||
transakCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.AVALANCHE],
|
||||
moonPay: {
|
||||
defaultCurrencyCode: 'avax_cchain',
|
||||
showOnlyCurrencies: 'avax_cchain',
|
||||
defaultCurrencyCode: `${SUPPORTED_CURRENCY_SYMBOLS.AVAX}_CCHAIN`,
|
||||
showOnlyCurrencies: [`${SUPPORTED_CURRENCY_SYMBOLS.AVAX}_CCHAIN`],
|
||||
},
|
||||
wyre: {
|
||||
srn: 'avalanche',
|
||||
@ -867,8 +875,8 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
network: 'celo',
|
||||
transakCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.CELO],
|
||||
moonPay: {
|
||||
defaultCurrencyCode: 'celo',
|
||||
showOnlyCurrencies: 'celo',
|
||||
defaultCurrencyCode: SUPPORTED_CURRENCY_SYMBOLS.CELO,
|
||||
showOnlyCurrencies: [SUPPORTED_CURRENCY_SYMBOLS.CELO],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
getIsBuyableCoinbasePayChain,
|
||||
getIsBuyableCoinbasePayToken,
|
||||
getIsBuyableTransakToken,
|
||||
getIsBuyableMoonpayToken,
|
||||
} from '../../../selectors/selectors';
|
||||
|
||||
import OnRampItem from './on-ramp-item';
|
||||
@ -53,6 +54,9 @@ const DepositPopover = ({ onClose, token }) => {
|
||||
const isTokenBuyableTransak = useSelector((state) =>
|
||||
getIsBuyableTransakToken(state, token?.symbol),
|
||||
);
|
||||
const isTokenBuyableMoonpay = useSelector((state) =>
|
||||
getIsBuyableMoonpayToken(state, token?.symbol),
|
||||
);
|
||||
|
||||
const networkName = NETWORK_TO_NAME_MAP[chainId];
|
||||
const symbol = token
|
||||
@ -77,7 +81,9 @@ const DepositPopover = ({ onClose, token }) => {
|
||||
);
|
||||
};
|
||||
const toMoonPay = () => {
|
||||
dispatch(buy({ service: 'moonpay', address, chainId }));
|
||||
dispatch(
|
||||
buy({ service: 'moonpay', address, chainId, symbol: token?.symbol }),
|
||||
);
|
||||
};
|
||||
const toWyre = () => {
|
||||
dispatch(buy({ service: 'wyre', address, chainId }));
|
||||
@ -153,7 +159,11 @@ const DepositPopover = ({ onClose, token }) => {
|
||||
});
|
||||
toMoonPay();
|
||||
}}
|
||||
hide={isTokenDeposit || !isBuyableMoonPayChain}
|
||||
hide={
|
||||
isTokenDeposit
|
||||
? !isBuyableMoonPayChain || !isTokenBuyableMoonpay
|
||||
: !isBuyableMoonPayChain
|
||||
}
|
||||
/>
|
||||
|
||||
<OnRampItem
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
getIsSwapsChain,
|
||||
getIsBuyableCoinbasePayToken,
|
||||
getIsBuyableTransakToken,
|
||||
getIsBuyableMoonpayToken,
|
||||
} from '../../../selectors/selectors';
|
||||
|
||||
import BuyIcon from '../../ui/icon/overview-buy-icon.component';
|
||||
@ -59,7 +60,11 @@ const TokenOverview = ({ className, token }) => {
|
||||
const isTokenBuyableTransak = useSelector((state) =>
|
||||
getIsBuyableTransakToken(state, token.symbol),
|
||||
);
|
||||
const isBuyable = isTokenBuyableCoinbasePay || isTokenBuyableTransak;
|
||||
const isTokenBuyableMoonpay = useSelector((state) =>
|
||||
getIsBuyableMoonpayToken(state, token.symbol),
|
||||
);
|
||||
const isBuyable =
|
||||
isTokenBuyableCoinbasePay || isTokenBuyableTransak || isTokenBuyableMoonpay;
|
||||
|
||||
useEffect(() => {
|
||||
if (token.isERC721 && process.env.COLLECTIBLES_V1) {
|
||||
|
19
ui/helpers/utils/moonpay.js
Normal file
19
ui/helpers/utils/moonpay.js
Normal file
@ -0,0 +1,19 @@
|
||||
import {
|
||||
BUYABLE_CHAINS_MAP,
|
||||
CHAIN_IDS,
|
||||
} from '../../../shared/constants/network';
|
||||
|
||||
export const formatMoonpaySymbol = (symbol, chainId = CHAIN_IDS.MAINNET) => {
|
||||
if (!symbol) {
|
||||
return symbol;
|
||||
}
|
||||
let _symbol = symbol;
|
||||
if (chainId === CHAIN_IDS.POLYGON || chainId === CHAIN_IDS.BSC) {
|
||||
_symbol = `${_symbol}_${BUYABLE_CHAINS_MAP?.[
|
||||
chainId
|
||||
]?.network.toUpperCase()}`;
|
||||
} else if (chainId === CHAIN_IDS.AVALANCHE) {
|
||||
_symbol = `${_symbol}_CCHAIN`;
|
||||
}
|
||||
return _symbol;
|
||||
};
|
33
ui/helpers/utils/moonpay.test.js
Normal file
33
ui/helpers/utils/moonpay.test.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||
import { formatMoonpaySymbol } from './moonpay';
|
||||
|
||||
describe('Moonpay Utils', () => {
|
||||
describe('formatMoonpaySymbol', () => {
|
||||
it('should return the same input if falsy input is provided', () => {
|
||||
expect(formatMoonpaySymbol()).toBe(undefined);
|
||||
expect(formatMoonpaySymbol(null)).toBe(null);
|
||||
expect(formatMoonpaySymbol('')).toBe('');
|
||||
});
|
||||
|
||||
it('should return the symbol in uppercase if no chainId is provided', () => {
|
||||
const result = formatMoonpaySymbol('ETH');
|
||||
expect(result).toStrictEqual('ETH');
|
||||
});
|
||||
|
||||
it('should return the symbol in uppercase if chainId is different than Avalanche/BSC/Polygon', () => {
|
||||
const result = formatMoonpaySymbol('ETH', CHAIN_IDS.MAINNET);
|
||||
expect(result).toStrictEqual('ETH');
|
||||
const result2 = formatMoonpaySymbol('CELO', CHAIN_IDS.CELO);
|
||||
expect(result2).toStrictEqual('CELO');
|
||||
});
|
||||
|
||||
it('should return the symbol in uppercase with the network name if chainId is Avalanche/BSC/Polygon', () => {
|
||||
const result = formatMoonpaySymbol('BNB', CHAIN_IDS.BSC);
|
||||
expect(result).toStrictEqual('BNB_BSC');
|
||||
const result2 = formatMoonpaySymbol('MATIC', CHAIN_IDS.POLYGON);
|
||||
expect(result2).toStrictEqual('MATIC_POLYGON');
|
||||
const result3 = formatMoonpaySymbol('AVAX', CHAIN_IDS.AVALANCHE);
|
||||
expect(result3).toStrictEqual('AVAX_CCHAIN');
|
||||
});
|
||||
});
|
||||
});
|
@ -63,6 +63,7 @@ import {
|
||||
} from '../ducks/app/app';
|
||||
import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
|
||||
import { hexToDecimal } from '../../shared/lib/metamask-controller-utils';
|
||||
import { formatMoonpaySymbol } from '../helpers/utils/moonpay';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
import { SNAPS_VIEW_ROUTE } from '../helpers/constants/routes';
|
||||
import { getPermissionSubjects } from './permissions';
|
||||
@ -716,6 +717,16 @@ export function getIsBuyableTransakToken(state, symbol) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getIsBuyableMoonpayToken(state, symbol) {
|
||||
const chainId = getCurrentChainId(state);
|
||||
const _symbol = formatMoonpaySymbol(symbol, chainId);
|
||||
return Boolean(
|
||||
BUYABLE_CHAINS_MAP?.[chainId]?.moonPay.showOnlyCurrencies?.includes(
|
||||
_symbol,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getIsBuyableMoonPayChain(state) {
|
||||
const chainId = getCurrentChainId(state);
|
||||
return Boolean(BUYABLE_CHAINS_MAP?.[chainId]?.moonPay);
|
||||
|
Loading…
Reference in New Issue
Block a user