feat: dynamic defaultCurrencyCode for moonpay (#7383)

This commit is contained in:
eddie 2023-10-02 09:48:55 -07:00 committed by GitHub
parent cbec108172
commit 524ce49fcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 187 additions and 20 deletions

@ -21,6 +21,7 @@ import { gqlToCurrency, logSentryErrorForUnsupportedChain, supportedChainIdFromG
import ms from 'ms'
import { useEffect, useState } from 'react'
import { isAddress } from 'utils'
import { isSameAddress } from 'utils/addresses'
import { NumberType, useFormatter } from 'utils/formatNumbers'
import { MOONPAY_SENDER_ADDRESSES, OrderStatusTable, OrderTextTable } from '../constants'
@ -77,10 +78,6 @@ const COMMON_CONTRACTS: { [key: string]: Partial<Activity> | undefined } = {
},
}
function isSameAddress(a?: string, b?: string) {
return a === b || a?.toLowerCase() === b?.toLowerCase() // Lazy-lowercases the addresses
}
function callsPositionManagerContract(assetActivity: TransactionActivity) {
const supportedChain = supportedChainIdFromGQLChain(assetActivity.chain)
if (!supportedChain) return false

@ -0,0 +1,17 @@
export const MOONPAY_SUPPORTED_CURRENCY_CODES = [
'eth',
'eth_arbitrum',
'eth_optimism',
'eth_polygon',
'weth',
'wbtc',
'matic_polygon',
'polygon',
'usdc_arbitrum',
'usdc_optimism',
'usdc_polygon',
'usdc',
'usdt',
] as const
export type MoonpaySupportedCurrencyCode = (typeof MOONPAY_SUPPORTED_CURRENCY_CODES)[number]

@ -10,6 +10,8 @@ import { useIsDarkMode } from 'theme/components/ThemeToggle'
import Circle from '../../assets/images/blue-loader.svg'
import Modal from '../Modal'
import { MOONPAY_SUPPORTED_CURRENCY_CODES } from './constants'
import { getDefaultCurrencyCode, parsePathParts } from './utils'
const MOONPAY_DARK_BACKGROUND = '#1c1c1e'
const Wrapper = styled.div<{ isDarkMode: boolean }>`
@ -55,20 +57,6 @@ const StyledSpinner = styled(CustomLightSpinner)`
top: 0;
`
const MOONPAY_SUPPORTED_CURRENCY_CODES = [
'eth',
'eth_arbitrum',
'eth_optimism',
'eth_polygon',
'weth',
'wbtc',
'matic_polygon',
'polygon',
'usdc_arbitrum',
'usdc_optimism',
'usdc_polygon',
]
export default function FiatOnrampModal() {
const { account } = useWeb3React()
const theme = useTheme()
@ -76,6 +64,8 @@ export default function FiatOnrampModal() {
const closeModal = useCloseModal()
const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP)
const { network, tokenAddress } = parsePathParts(location.pathname)
const [signedIframeUrl, setSignedIframeUrl] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
@ -100,7 +90,7 @@ export default function FiatOnrampModal() {
body: JSON.stringify({
theme: isDarkMode ? 'dark' : 'light',
colorCode: theme.accent1,
defaultCurrencyCode: 'eth',
defaultCurrencyCode: getDefaultCurrencyCode(tokenAddress, network),
redirectUrl: swapUrl,
walletAddresses: JSON.stringify(
MOONPAY_SUPPORTED_CURRENCY_CODES.reduce(
@ -121,7 +111,7 @@ export default function FiatOnrampModal() {
} finally {
setLoading(false)
}
}, [account, isDarkMode, swapUrl, theme.accent1])
}, [account, isDarkMode, network, swapUrl, theme.accent1, tokenAddress])
useEffect(() => {
fetchSignedIframeUrl()

@ -0,0 +1,78 @@
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import {
MATIC,
USDC_ARBITRUM,
USDC_MAINNET,
USDC_OPTIMISM,
USDC_POLYGON,
USDT,
WBTC,
WETH_POLYGON,
} from 'constants/tokens'
import { getDefaultCurrencyCode, parsePathParts } from './utils'
describe('getDefaultCurrencyCode', () => {
it('NATIVE/arbitrum should return the correct currency code', () => {
expect(getDefaultCurrencyCode('NATIVE', 'arbitrum')).toBe('eth_arbitrum')
})
it('NATIVE/optimism should return the correct currency code', () => {
expect(getDefaultCurrencyCode('NATIVE', 'optimism')).toBe('eth_optimism')
})
it('WETH/polygon should return the correct currency code', () => {
expect(getDefaultCurrencyCode(WETH_POLYGON.address, 'polygon')).toBe('eth_polygon')
})
it('WETH/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(WETH9[ChainId.MAINNET].address, 'ethereum')).toBe('weth')
})
it('WBTC/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(WBTC.address, 'ethereum')).toBe('wbtc')
})
it('NATIVE/polygon should return the correct currency code', () => {
expect(getDefaultCurrencyCode('NATIVE', 'polygon')).toBe('matic_polygon')
})
it('MATIC/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(MATIC.address, 'ethereum')).toBe('polygon')
})
it('USDC/arbitrum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_ARBITRUM.address, 'arbitrum')).toBe('usdc_arbitrum')
})
it('USDC/optimism should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_OPTIMISM.address, 'optimism')).toBe('usdc_optimism')
})
it('USDC/polygon should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_POLYGON.address, 'polygon')).toBe('usdc_polygon')
})
it('native/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode('NATIVE', 'ethereum')).toBe('eth')
})
it('usdc/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDC_MAINNET.address, 'ethereum')).toBe('usdc')
})
it('usdt/ethereum should return the correct currency code', () => {
expect(getDefaultCurrencyCode(USDT.address, 'ethereum')).toBe('usdt')
})
it('chain/token mismatch should default to eth', () => {
expect(getDefaultCurrencyCode(USDC_ARBITRUM.address, 'ethereum')).toBe('eth')
expect(getDefaultCurrencyCode(USDC_OPTIMISM.address, 'ethereum')).toBe('eth')
expect(getDefaultCurrencyCode(USDC_POLYGON.address, 'ethereum')).toBe('eth')
expect(getDefaultCurrencyCode(MATIC.address, 'arbitrum')).toBe('eth')
})
})
describe('parseLocation', () => {
it('should parse the URL correctly', () => {
expect(parsePathParts('/tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')).toEqual({
network: 'ethereum',
tokenAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
})
expect(parsePathParts('tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599')).toEqual({
network: 'ethereum',
tokenAddress: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
})
expect(parsePathParts('/swap')).toEqual({
network: undefined,
tokenAddress: undefined,
})
})
})

@ -0,0 +1,74 @@
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import {
BRIDGED_USDC_ARBITRUM,
MATIC,
USDC_ARBITRUM,
USDC_MAINNET,
USDC_OPTIMISM,
USDC_POLYGON,
USDT,
WBTC,
WETH_POLYGON,
} from 'constants/tokens'
import { Chain } from 'graphql/data/__generated__/types-and-hooks'
import { validateUrlChainParam } from 'graphql/data/util'
import { MoonpaySupportedCurrencyCode } from './constants'
type MoonpaySupportedChain = Chain.Ethereum | Chain.Polygon | Chain.Arbitrum | Chain.Optimism
const moonPaySupportedChains = [Chain.Ethereum, Chain.Polygon, Chain.Arbitrum, Chain.Optimism]
const CURRENCY_CODES: {
[K in MoonpaySupportedChain]: {
[key: string]: MoonpaySupportedCurrencyCode
native: MoonpaySupportedCurrencyCode
}
} = {
[Chain.Ethereum]: {
[WETH9[ChainId.MAINNET]?.address.toLowerCase()]: 'weth',
[USDC_MAINNET.address.toLowerCase()]: 'usdc',
[USDT.address.toLowerCase()]: 'usdt',
[WBTC.address.toLowerCase()]: 'wbtc',
[MATIC.address.toLowerCase()]: 'polygon',
native: 'eth',
},
[Chain.Arbitrum]: {
[USDC_ARBITRUM.address.toLowerCase()]: 'usdc_arbitrum',
[BRIDGED_USDC_ARBITRUM.address.toLowerCase()]: 'usdc_arbitrum',
native: 'eth_arbitrum',
},
[Chain.Optimism]: {
[USDC_OPTIMISM.address.toLowerCase()]: 'usdc_optimism',
native: 'eth_optimism',
},
[Chain.Polygon]: {
[USDC_POLYGON.address.toLowerCase()]: 'usdc_polygon',
[WETH_POLYGON.address.toLowerCase()]: 'eth_polygon',
native: 'matic_polygon',
},
}
export function getDefaultCurrencyCode(
address: string | undefined,
chainName: string | undefined
): MoonpaySupportedCurrencyCode {
const chain = validateUrlChainParam(chainName)
if (!address || !chain) return 'eth'
if (moonPaySupportedChains.includes(chain)) {
const code = CURRENCY_CODES[chain as MoonpaySupportedChain]?.[address.toLowerCase()]
return code ?? 'eth'
}
return 'eth'
}
/**
* You should use useParams() from react-router-dom instead of this function if possible.
* This function is only used in the case where we need to parse the path outside the scope of the router.
*/
export function parsePathParts(pathname: string): { network?: string; tokenAddress?: string } {
const pathParts = pathname.split('/')
// Matches the /tokens/<network>/<tokenAddress> path.
const network = pathParts.length > 2 ? pathParts[pathParts.length - 2] : undefined
const tokenAddress = pathParts.length > 2 ? pathParts[pathParts.length - 1] : undefined
return { network, tokenAddress }
}

@ -96,6 +96,13 @@ export const DAI_OPTIMISM = new Token(
'DAI',
'Dai stable coin'
)
export const MATIC = new Token(
ChainId.MAINNET,
'0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0',
18,
'MATIC',
'Polygon Matic'
)
export const DAI_POLYGON = new Token(
ChainId.POLYGON,
'0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063',

@ -11,6 +11,10 @@ export function isAddress(value: any): string | false {
}
}
export function isSameAddress(a?: string, b?: string) {
return a === b || a?.toLowerCase() === b?.toLowerCase() // Lazy-lowercases the addresses
}
// Shortens an Ethereum address
export function shortenAddress(address = '', charsStart = 4, charsEnd = 4): string {
const parsed = isAddress(address)