diff --git a/CHANGELOG.md b/CHANGELOG.md index 5aaedf42c8c..b6ea1ec8935 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,13 @@ - changed: Some unecessary `showError` dropdowns demoted to hidden `showDevError` - changed: Restrict Bity buy/sell to no-KYC asset +- changed: Determine Moonpay asset support using chainCode/contractAddress +- changed: Allow reverse quotes for Kado +- changed: Credit card allowed countries to add Botswana, Cambodia, Panama, and Sri Lanka - fixed: Default swap pair logic from the trade modal was not prioritizing mainnet assets -- removed: 'fasterpayments' payment type - fixed: "FIO Address does not exist" error after transferring a FIO name +- removed: 'fasterpayments' payment type +- removed: Turking bank transfer support ## 4.12.0 diff --git a/src/constants/plugins/buyPluginList.json b/src/constants/plugins/buyPluginList.json index 52c935edf04..6672cb18bc2 100644 --- a/src/constants/plugins/buyPluginList.json +++ b/src/constants/plugins/buyPluginList.json @@ -36,6 +36,7 @@ "BA", "BV", "BR", + "BW", "IO", "BN", "BG", @@ -96,6 +97,7 @@ "GN", "GW", "GY", + "KH", "HT", "HM", "VA", @@ -126,6 +128,7 @@ "LR", "LY", "LI", + "LK", "LT", "LU", "MO", @@ -165,6 +168,7 @@ "MP", "NO", "OM", + "PA", "PW", "PS", "PG", @@ -395,7 +399,7 @@ "paymentTypes": ["turkishbank"], "title": "Turkish Bank Transfer", "description": "Fee: 8-9%\nSettlement: Instant - 1 hour", - "forCountries": ["TR"], + "forCountries": [], "cryptoCodes": [], "paymentTypeLogoKey": "bank" }, diff --git a/src/plugins/gui/fiatProviderTypes.ts b/src/plugins/gui/fiatProviderTypes.ts index 8cf865c6a7a..25eae6c1745 100644 --- a/src/plugins/gui/fiatProviderTypes.ts +++ b/src/plugins/gui/fiatProviderTypes.ts @@ -131,11 +131,13 @@ export interface FiatProvider { } export type FiatProviderGetTokenId = (pluginId: string, currencyCode: string) => EdgeTokenId | undefined +export type FiatProviderGetTokenIdFromContract = (params: { pluginId: string; contractAddress: string }) => EdgeTokenId | undefined export type FiatProviderMakeUuid = () => Promise export interface FiatProviderFactoryParams { deviceId: string io: { store: FiatProviderStore; makeUuid: FiatProviderMakeUuid } getTokenId: FiatProviderGetTokenId + getTokenIdFromContract: FiatProviderGetTokenIdFromContract apiKeys?: unknown // Data specific to the requirements of each provider, // which lets the provider know that these orders were made from within Edge. // Typically an API key, but can be some other information like a client ID. diff --git a/src/plugins/gui/providers/kadoProvider.ts b/src/plugins/gui/providers/kadoProvider.ts index 0a99bb7d4d7..5b8a3552e1d 100644 --- a/src/plugins/gui/providers/kadoProvider.ts +++ b/src/plugins/gui/providers/kadoProvider.ts @@ -85,8 +85,8 @@ const allowedPaymentTypes: AllowedPaymentTypes = { } } -const allowedBuyCurrencyCodes: FiatProviderAssetMap = { providerId, requiredAmountType: 'fiat', crypto: {}, fiat: {} } -const allowedSellCurrencyCodes: FiatProviderAssetMap = { providerId, requiredAmountType: 'crypto', crypto: {}, fiat: {} } +const allowedBuyCurrencyCodes: FiatProviderAssetMap = { providerId, crypto: {}, fiat: {} } +const allowedSellCurrencyCodes: FiatProviderAssetMap = { providerId, crypto: {}, fiat: {} } const allowedCountryCodes: { [code: string]: boolean } = { US: true } /** @@ -190,10 +190,10 @@ const asBlockchains = asObject({ */ // Define cleaners for nested objects and properties -// const asAmountCurrency = asObject({ -// amount: asNumber, -// currency: asString -// }) +const asAmountCurrency = asObject({ + amount: asNumber, + currency: asString +}) // const asRequest = asObject({ // transactionType: asValue('buy', 'sell'), @@ -219,14 +219,14 @@ const asMinMaxValue = asObject({ const asQuote = asObject({ // asset: asString, - // baseAmount: asAmountCurrency, + baseAmount: asAmountCurrency, // pricePerUnit: asNumber, // price: asPrice, // processingFee: asAmountCurrency, // bridgeFee: asAmountCurrency, // networkFee: asAmountCurrency, // smartContractFee: asAmountCurrency, - // totalFee: asAmountCurrency, + totalFee: asAmountCurrency, // receiveAmountAfterFees: asAmountCurrency, // receiveUnitCountAfterFees: asAmountCurrency, receive: asObject({ @@ -397,6 +397,7 @@ interface GetQuoteParams { amount: number blockchain: string currency: string + reverse: boolean asset: string } @@ -505,12 +506,6 @@ export const kadoProvider: FiatProviderFactory = { const allowedCurrencyCodes = direction === 'buy' ? allowedBuyCurrencyCodes : allowedSellCurrencyCodes if (!allowedCountryCodes[regionCode.countryCode]) throw new FiatProviderError({ providerId, errorType: 'regionRestricted', displayCurrencyCode }) - if (direction === 'buy' && amountType !== 'fiat') { - throw new FiatProviderError({ providerId, errorType: 'assetUnsupported' }) - } - if (direction === 'sell' && amountType !== 'crypto') { - throw new FiatProviderError({ providerId, errorType: 'assetUnsupported' }) - } if (!paymentTypes.some(paymentType => allowedPaymentTypes[direction][paymentType] === true)) throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) @@ -533,12 +528,15 @@ export const kadoProvider: FiatProviderFactory = { throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) } + const reverse = (direction === 'sell' && amountType === 'fiat') || (direction === 'buy' && amountType === 'crypto') + const queryParams: GetQuoteParams = { transactionType: direction, fiatMethod, amount: Number(exchangeAmount), blockchain, currency: 'USD', + reverse, asset } @@ -561,17 +559,31 @@ export const kadoProvider: FiatProviderFactory = { const { amount: minAmount, unit: minUnit } = quote.data.quote.minValue const { amount: maxAmount, unit: maxUnit } = quote.data.quote.maxValue + const { baseAmount } = quote.data.quote let fiatAmount: string let cryptoAmount: string + const { totalFee } = quote.data.quote + const { amount, unitCount } = quote.data.quote.receive if (direction === 'buy') { - const { unitCount } = quote.data.quote.receive - cryptoAmount = unitCount.toString() - fiatAmount = exchangeAmount + if (amountType === 'fiat') { + cryptoAmount = unitCount.toString() + fiatAmount = exchangeAmount + } else { + // For some reason, the totalFee is not included in the fiatAmount on buy reverse quotes + fiatAmount = (amount + totalFee.amount).toString() + cryptoAmount = exchangeAmount + } } else { - const { amount } = quote.data.quote.receive - cryptoAmount = exchangeAmount - fiatAmount = amount.toString() + if (amountType === 'crypto') { + cryptoAmount = exchangeAmount + fiatAmount = amount.toString() + } else { + fiatAmount = exchangeAmount + // The unitCount is 0 for sell reverse quotes but seems to be in + // the baseAmount.amount so use that + cryptoAmount = baseAmount.amount.toString() + } } if (lt(fiatAmount, minAmount.toString())) throw new FiatProviderError({ providerId, errorType: 'underLimit', errorAmount: minAmount, displayCurrencyCode: minUnit }) diff --git a/src/plugins/gui/providers/moonpayProvider.ts b/src/plugins/gui/providers/moonpayProvider.ts index 89840854fca..0fcdf65ea8c 100644 --- a/src/plugins/gui/providers/moonpayProvider.ts +++ b/src/plugins/gui/providers/moonpayProvider.ts @@ -1,10 +1,11 @@ // import { div, gt, lt, mul, toFixed } from 'biggystring' import { asArray, asBoolean, asEither, asNull, asNumber, asObject, asOptional, asString, asValue } from 'cleaners' +import { EdgeTokenId } from 'edge-core-js' import URL from 'url-parse' import { StringMap } from '../../../types/types' import { removeIsoPrefix } from '../../../util/utils' -import { asFiatPaymentType, FiatDirection, FiatPaymentType } from '../fiatPluginTypes' +import { FiatDirection, FiatPaymentType } from '../fiatPluginTypes' import { FiatProvider, FiatProviderApproveQuoteParams, @@ -14,7 +15,6 @@ import { FiatProviderFactory, FiatProviderFactoryParams, FiatProviderGetQuoteParams, - FiatProviderGetTokenId, FiatProviderQuote } from '../fiatProviderTypes' import { addTokenToArray } from '../util/providerUtils' @@ -24,9 +24,17 @@ const storeId = 'com.moonpay' const partnerIcon = 'moonpay_symbol_prp.png' const pluginDisplayName = 'Moonpay' -const allowedCurrencyCodes: FiatProviderAssetMap = { providerId, crypto: {}, fiat: {} } +const allowedCurrencyCodes: Record = { + buy: { credit: { providerId, fiat: {}, crypto: {} } }, + sell: { credit: { providerId, fiat: {}, crypto: {}, requiredAmountType: 'crypto' } } +} const allowedCountryCodes: Record = { buy: {}, sell: {} } -const allowedPaymentTypes: { [Payment in FiatPaymentType]?: boolean } = { applepay: true, credit: true, googlepay: true, iach: true } + +const asMetadata = asObject({ + contractAddress: asEither(asString, asNull), // "0x0d8775f648430679a709e98d2b0cb6250d2887ef", + // chainId: asString, // "1" + networkCode: asString // "ethereum" +}) const asMoonpayCurrency = asObject({ type: asValue('crypto', 'fiat'), @@ -36,6 +44,10 @@ const asMoonpayCurrency = asObject({ minAmount: asEither(asNumber, asNull), maxBuyAmount: asEither(asNumber, asNull), minBuyAmount: asEither(asNumber, asNull), + maxSellAmount: asOptional(asNumber), + minSellAmount: asOptional(asNumber), + metadata: asOptional(asMetadata), + isSellSupported: asOptional(asBoolean), isSuspended: asOptional(asBoolean), isSupportedInUS: asOptional(asBoolean) }) @@ -43,19 +55,27 @@ export type MoonpayCurrency = ReturnType const asMoonpayCurrencies = asArray(asMoonpayCurrency) -const asMoonpayQuote = asObject({ +const asMoonpaySellQuote = asObject({ + baseCurrencyCode: asString, + baseCurrencyAmount: asNumber, + quoteCurrencyAmount: asNumber +}) + +const asMoonpayBuyQuote = asObject({ baseCurrencyCode: asString, baseCurrencyAmount: asNumber, - quoteCurrencyCode: asString, quoteCurrencyAmount: asNumber, - quoteCurrencyPrice: asNumber, - feeAmount: asNumber, - extraFeeAmount: asNumber, - extraFeePercentage: asNumber, - networkFeeAmount: asNumber, + quoteCurrencyCode: asString, + // quoteCurrencyPrice: asNumber, + // feeAmount: asNumber, + // extraFeeAmount: asNumber, + // extraFeePercentage: asNumber, + // networkFeeAmount: asNumber, totalAmount: asNumber }) +const asMoonpayQuote = asEither(asMoonpayBuyQuote, asMoonpaySellQuote) + const asState = asObject({ code: asString, // "name": "Alabama", @@ -79,6 +99,7 @@ const asApiKeys = asString const asMoonpayCountries = asArray(asMoonpayCountry) +type MoonpayPaymentMethod = 'ach_bank_transfer' | 'credit_debit_card' interface MoonpayWidgetQueryParams { apiKey: string currencyCode: string @@ -87,52 +108,52 @@ interface MoonpayWidgetQueryParams { walletAddress: string showAllCurrencies: boolean enableRecurringBuys: boolean - paymentMethod: 'ach_bank_transfer' | 'credit_debit_card' + paymentMethod: MoonpayPaymentMethod quoteCurrencyAmount?: number baseCurrencyAmount?: number } -const CURRENCY_CODE_TRANSLATE: StringMap = { - matic_polygon: 'matic' +const MOONPAY_PAYMENT_TYPE_MAP: Partial> = { + applepay: 'credit_debit_card', + credit: 'credit_debit_card', + googlepay: 'credit_debit_card', + iach: 'ach_bank_transfer' } -const CURRENCY_PLUGINID_MAP: StringMap = { - bch: 'bitcoincash', - bnb: 'binancechain', - btc: 'bitcoin', - celo: 'celo', - dash: 'dash', - dgb: 'digibyte', - doge: 'dogecoin', - dot: 'polkadot', - eos: 'eos', - etc: 'ethereumclassic', - eth: 'ethereum', - hbar: 'hedera', - ltc: 'litecoin', - matic: 'polygon', - qtum: 'qtum', - rvn: 'ravencoin', - sol: 'solana', - xlm: 'stellar', - xrp: 'ripple', - xtz: 'tezos' +const NETWORK_CODE_PLUGINID_MAP: StringMap = { + algorand: 'algorand', + arbitrum: 'arbitrum', + avalanche_c_chain: 'avalanche', + base: 'base', + bitcoin: 'bitcoin', + bitcoin_cash: 'bitcoincash', + cardano: 'cardano', + cosmos: 'cosmoshub', + dogecoin: 'dogecoin', + ethereum: 'ethereum', + litecoin: 'litecoin', + optimism: 'optimism', + polygon: 'polygon', + ripple: 'ripple', + solana: 'solana', + stellar: 'stellar', + tron: 'tron', + zksync: 'zksync' } -const TOKEN_MAP: StringMap = { - bat: 'ethereum', - comp: 'ethereum', - dai: 'ethereum', - tusd: 'ethereum', - zrx: 'ethereum' +const PAYMENT_TYPE_MAP: Partial> = { + applepay: 'credit', + credit: 'credit', + googlepay: 'credit' } + let lastChecked = 0 export const moonpayProvider: FiatProviderFactory = { providerId, storeId, makeProvider: async (params: FiatProviderFactoryParams): Promise => { - const { apiKeys, getTokenId } = params + const { apiKeys, getTokenIdFromContract } = params const apiKey = asApiKeys(apiKeys) if (apiKey == null) throw new Error('Moonpay missing apiKey') const out: FiatProvider = { @@ -140,16 +161,19 @@ export const moonpayProvider: FiatProviderFactory = { partnerIcon, pluginDisplayName, getSupportedAssets: async ({ direction, paymentTypes, regionCode }): Promise => { + const paymentType = PAYMENT_TYPE_MAP[paymentTypes[0]] ?? paymentTypes[0] + if (direction !== 'buy') { throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) } + // Return nothing if paymentTypes are not supported by this provider - if (!paymentTypes.some(paymentType => allowedPaymentTypes[paymentType] === true)) - throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) + const assetMap = allowedCurrencyCodes[direction][paymentType] + if (assetMap == null) throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) if (isDailyCheckDue(lastChecked)) { const response = await fetch(`https://api.moonpay.com/v3/currencies?apiKey=${apiKey}`).catch(e => undefined) - if (response == null || !response.ok) return allowedCurrencyCodes + if (response == null || !response.ok) return assetMap const result = await response.json() let moonpayCurrencies: MoonpayCurrency[] = [] @@ -158,30 +182,36 @@ export const moonpayProvider: FiatProviderFactory = { } catch (error: any) { console.log(error.message) console.log(JSON.stringify(error, null, 2)) - return allowedCurrencyCodes + return assetMap } for (const currency of moonpayCurrencies) { + // if (direction === 'sell' && currency.isSellSupported !== true) { + // continue + // } + if (currency.type === 'crypto') { if (regionCode.countryCode === 'US' && currency.isSupportedInUS !== true) { continue } - if (currency.name.includes('(ERC-20)')) { - addToAllowedCurrencies(getTokenId, 'ethereum', currency, currency.code) + if (currency.isSuspended) continue + const { metadata } = currency + if (metadata == null) continue + const { contractAddress, networkCode } = metadata + const pluginId = NETWORK_CODE_PLUGINID_MAP[networkCode] + if (pluginId == null) continue + + let tokenId: EdgeTokenId + if (contractAddress != null) { + const tId = getTokenIdFromContract({ pluginId, contractAddress }) + if (tId === undefined) continue + tokenId = tId } else { - if (currency.isSuspended) continue - if (CURRENCY_CODE_TRANSLATE[currency.code] != null) { - const currencyCode = CURRENCY_CODE_TRANSLATE[currency.code] - addToAllowedCurrencies(getTokenId, CURRENCY_PLUGINID_MAP[currencyCode], currency, currencyCode) - currency.code = CURRENCY_CODE_TRANSLATE[currency.code] - } else if (TOKEN_MAP[currency.code] != null) { - addToAllowedCurrencies(getTokenId, TOKEN_MAP[currency.code], currency, currency.code) - } - if (CURRENCY_PLUGINID_MAP[currency.code] != null) { - addToAllowedCurrencies(getTokenId, CURRENCY_PLUGINID_MAP[currency.code], currency, currency.code) - } + tokenId = null } + if (assetMap.crypto[pluginId] == null) assetMap.crypto[pluginId] = [] + addTokenToArray({ tokenId, otherInfo: currency }, assetMap.crypto[pluginId]) } else { - allowedCurrencyCodes.fiat['iso:' + currency.code.toUpperCase()] = currency + assetMap.fiat['iso:' + currency.code.toUpperCase()] = currency } } @@ -219,41 +249,47 @@ export const moonpayProvider: FiatProviderFactory = { lastChecked = Date.now() } validateExactRegion(providerId, regionCode, allowedCountryCodes[direction]) - return allowedCurrencyCodes + return assetMap }, getQuote: async (params: FiatProviderGetQuoteParams): Promise => { const { direction, regionCode, paymentTypes, displayCurrencyCode } = params validateExactRegion(providerId, regionCode, allowedCountryCodes[direction]) + if (direction !== 'buy') { throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) } - if (!paymentTypes.some(paymentType => allowedPaymentTypes[paymentType] === true)) - throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) - let foundPaymentType = false - let useIAch = false - for (const type of paymentTypes) { - const t = asFiatPaymentType(type) - if (allowedPaymentTypes[t]) { - foundPaymentType = true - } - if (type === 'iach') { - useIAch = true - } - } - if (!foundPaymentType) throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) + const paymentType = PAYMENT_TYPE_MAP[paymentTypes[0]] ?? paymentTypes[0] + const assetMap = allowedCurrencyCodes[direction][paymentType] + if (assetMap == null) throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) + + const paymentMethod = MOONPAY_PAYMENT_TYPE_MAP[paymentType] + if (paymentMethod == null) throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) let amountParam = '' - const tokens = allowedCurrencyCodes.crypto[params.pluginId] + const tokens = assetMap.crypto[params.pluginId] const moonpayCurrency = tokens.find(token => token.tokenId === params.tokenId) const cryptoCurrencyObj = asMoonpayCurrency(moonpayCurrency?.otherInfo) - const fiatCurrencyObj = asMoonpayCurrency(allowedCurrencyCodes.fiat[params.fiatCurrencyCode]) + const fiatCurrencyObj = asMoonpayCurrency(assetMap.fiat[params.fiatCurrencyCode]) if (cryptoCurrencyObj == null || fiatCurrencyObj == null) throw new Error('Moonpay could not query supported currencies') - const maxFiat = Math.max(fiatCurrencyObj.maxAmount ?? 0, fiatCurrencyObj.maxBuyAmount ?? 0) - const minFiat = Math.min(fiatCurrencyObj.minAmount ?? Infinity, fiatCurrencyObj.minBuyAmount ?? Infinity) - const maxCrypto = Math.max(cryptoCurrencyObj.maxAmount ?? 0, cryptoCurrencyObj.maxBuyAmount ?? 0) - const minCrypto = Math.min(cryptoCurrencyObj.minAmount ?? Infinity, cryptoCurrencyObj.minBuyAmount ?? Infinity) + let maxFiat: number + let minFiat: number + let maxCrypto: number + let minCrypto: number + + if (direction === 'buy') { + maxFiat = fiatCurrencyObj.maxBuyAmount ?? fiatCurrencyObj.maxAmount ?? 0 + minFiat = fiatCurrencyObj.minBuyAmount ?? fiatCurrencyObj.minAmount ?? Infinity + maxCrypto = cryptoCurrencyObj.maxBuyAmount ?? cryptoCurrencyObj.maxAmount ?? 0 + minCrypto = cryptoCurrencyObj.minBuyAmount ?? cryptoCurrencyObj.minAmount ?? Infinity + } else { + maxFiat = fiatCurrencyObj.maxSellAmount ?? fiatCurrencyObj.maxAmount ?? 0 + minFiat = fiatCurrencyObj.minSellAmount ?? fiatCurrencyObj.minAmount ?? Infinity + maxCrypto = cryptoCurrencyObj.maxSellAmount ?? cryptoCurrencyObj.maxAmount ?? 0 + minCrypto = cryptoCurrencyObj.minSellAmount ?? cryptoCurrencyObj.minAmount ?? Infinity + } + const exchangeAmount = parseFloat(params.exchangeAmount) const displayFiatCurrencyCode = removeIsoPrefix(params.fiatCurrencyCode) if (params.amountType === 'fiat') { @@ -262,16 +298,30 @@ export const moonpayProvider: FiatProviderFactory = { if (exchangeAmount < minFiat) throw new FiatProviderError({ providerId, errorType: 'underLimit', errorAmount: minFiat, displayCurrencyCode: displayFiatCurrencyCode }) // User typed a fiat amount. Need a crypto value - amountParam = `baseCurrencyAmount=${params.exchangeAmount}` + if (direction === 'buy') { + amountParam = `baseCurrencyAmount=${params.exchangeAmount}` + } else { + // Moonpay API doesn't let us specify a fiat amount for sell + throw new FiatProviderError({ providerId, errorType: 'paymentUnsupported' }) + } } else { if (exchangeAmount > maxCrypto) throw new FiatProviderError({ providerId, errorType: 'overLimit', errorAmount: maxCrypto, displayCurrencyCode }) if (exchangeAmount < minCrypto) throw new FiatProviderError({ providerId, errorType: 'underLimit', errorAmount: minCrypto, displayCurrencyCode }) - amountParam = `quoteCurrencyAmount=${params.exchangeAmount}` + if (direction === 'buy') { + amountParam = `quoteCurrencyAmount=${params.exchangeAmount}` + } else { + amountParam = `baseCurrencyAmount=${params.exchangeAmount}` + } } const fiatCode = removeIsoPrefix(params.fiatCurrencyCode).toLowerCase() - const paymentMethod = useIAch ? 'ach_bank_transfer' : 'credit_debit_card' - const url = `https://api.moonpay.com/v3/currencies/${cryptoCurrencyObj.code}/buy_quote/?apiKey=${apiKey}"eCurrencyCode=${cryptoCurrencyObj.code}&baseCurrencyCode=${fiatCode}&paymentMethod=${paymentMethod}&areFeesIncluded=true&${amountParam}` + let url + if (direction === 'buy') { + url = `https://api.moonpay.com/v3/currencies/${cryptoCurrencyObj.code}/buy_quote/?apiKey=${apiKey}"eCurrencyCode=${cryptoCurrencyObj.code}&baseCurrencyCode=${fiatCode}&paymentMethod=${paymentMethod}&areFeesIncluded=true&${amountParam}` + } else { + url = `https://api.moonpay.com/v3/currencies/${cryptoCurrencyObj.code}/sell_quote/?apiKey=${apiKey}"eCurrencyCode=${fiatCode}&payoutMethod=${paymentMethod}&areFeesIncluded=true&${amountParam}` + } + const response = await fetch(url).catch(e => { console.log(e) return undefined @@ -287,6 +337,9 @@ export const moonpayProvider: FiatProviderFactory = { console.log('Got Moonpay quote') console.log(JSON.stringify(moonpayQuote, null, 2)) + const fiatAmount = 'totalAmount' in moonpayQuote ? moonpayQuote.totalAmount.toString() : moonpayQuote.quoteCurrencyAmount.toString() + const cryptoAmount = direction === 'buy' ? moonpayQuote.quoteCurrencyAmount.toString() : moonpayQuote.baseCurrencyAmount.toString() + const paymentQuote: FiatProviderQuote = { providerId, partnerIcon, @@ -296,8 +349,8 @@ export const moonpayProvider: FiatProviderFactory = { displayCurrencyCode: params.displayCurrencyCode, isEstimate: false, fiatCurrencyCode: params.fiatCurrencyCode, - fiatAmount: moonpayQuote.totalAmount.toString(), - cryptoAmount: moonpayQuote.quoteCurrencyAmount.toString(), + fiatAmount, + cryptoAmount, direction: params.direction, expirationDate: new Date(Date.now() + 8000), approveQuote: async (approveParams: FiatProviderApproveQuoteParams): Promise => { @@ -317,7 +370,7 @@ export const moonpayProvider: FiatProviderFactory = { if (params.amountType === 'crypto') { queryObj.quoteCurrencyAmount = moonpayQuote.quoteCurrencyAmount } else { - queryObj.baseCurrencyAmount = moonpayQuote.totalAmount + queryObj.baseCurrencyAmount = 'totalAmount' in moonpayQuote ? moonpayQuote.totalAmount : undefined } url.set('query', queryObj) @@ -334,10 +387,3 @@ export const moonpayProvider: FiatProviderFactory = { return out } } - -const addToAllowedCurrencies = (getTokenId: FiatProviderGetTokenId, pluginId: string, currency: MoonpayCurrency, currencyCode: string) => { - if (allowedCurrencyCodes.crypto[pluginId] == null) allowedCurrencyCodes.crypto[pluginId] = [] - const tokenId = getTokenId(pluginId, currencyCode.toUpperCase()) - if (tokenId === undefined) return - addTokenToArray({ tokenId, otherInfo: currency }, allowedCurrencyCodes.crypto[pluginId]) -} diff --git a/src/plugins/gui/util/initializeProviders.ts b/src/plugins/gui/util/initializeProviders.ts index 5d196249138..88df64cfa93 100644 --- a/src/plugins/gui/util/initializeProviders.ts +++ b/src/plugins/gui/util/initializeProviders.ts @@ -1,5 +1,5 @@ import { ENV } from '../../../env' -import { getTokenId } from '../../../util/CurrencyInfoHelpers' +import { findTokenIdByNetworkLocation, getTokenId } from '../../../util/CurrencyInfoHelpers' import { makeUuid } from '../../../util/rnUtils' import { FiatPluginFactoryArgs } from '../fiatPluginTypes' import { FiatProvider, FiatProviderFactory } from '../fiatProviderTypes' @@ -14,6 +14,10 @@ export async function initializeProviders(providerFactories: Array>> = [] const getTokenIdProvider = (pluginId: string, currencyCode: string) => getTokenId(account.currencyConfig[pluginId], currencyCode) + const getTokenIdFromContract = (params: { pluginId: string; contractAddress: string }) => { + const { pluginId, contractAddress } = params + return findTokenIdByNetworkLocation({ account, pluginId, networkLocation: { contractAddress } }) + } for (const providerFactory of providerFactories) { if (disablePlugins[providerFactory.providerId]) continue @@ -22,7 +26,7 @@ export async function initializeProviders(providerFactories: Array { + const { networkLocation } = params + let allTokens: EdgeTokenMap + if ('allTokens' in params) { + allTokens = params.allTokens + } else { + allTokens = params.account.currencyConfig[params.pluginId]?.allTokens + if (allTokens == null) return + } + + for (const tokenId in allTokens) { + const edgeToken = allTokens[tokenId] + if (edgeToken == null) return + let found = true + for (const key in networkLocation) { + const left = edgeToken?.networkLocation?.[key] + const right = networkLocation[key] + if (left === undefined) { + // If the key is not found then assume this key doesn't exist in any token + // and we can early return undefined + console.warn(`findTokenIdByNetworkLocation: key '${key}' not found`) + return + } + if (left === right) continue + + // In the special case of EVM contract addresses which are valid in both lower and upper case, + // we need to compare them case-insensitively. We know the stored contract address is lower case + // so only lower case the parameter if it's a string. + if (typeof right === 'string') { + if (left === right.toLowerCase()) { + continue + } + } + found = false + break + } + if (found) { + return tokenId + } + } + // If we get here, return undefined as we found no match +} + export const getTokenId = (currencyConfig: EdgeCurrencyConfig, currencyCode: string): EdgeTokenId | undefined => { if (currencyConfig == null) return if (currencyConfig.currencyInfo.currencyCode === currencyCode) return null