From e7e8d4d26dec900bbb955463f3b7a73926a9270c Mon Sep 17 00:00:00 2001 From: Jean Ribeiro Date: Thu, 11 Jul 2024 19:00:28 -0300 Subject: [PATCH] feat: adds multichain support in Buy/Sell view (#2725) * feat: adds multichain support in BuySell view * refactor: use network id on transak cryptocurrency Co-authored-by: Jean Ribeiro * use id in token popup filter * enable buy sell on all profiles * add network into transak crypto currencies * dynamically pull in transak cryptos based of profile networks * enhancment: sort transak cryptos --------- Co-authored-by: Nicole O'Brien --- .../desktop/components/popup/Popup.svelte | 2 + .../popups/TransakSelectTokenPopup.svelte | 106 ++++++++++++++++++ .../auxiliary/popup/enums/popup-id.enum.ts | 1 + .../lib/electron/managers/transak.manager.ts | 6 +- .../views/dashboard/buy-sell/BuySell.svelte | 3 +- .../buy-sell/components/TokenTile.svelte | 50 --------- .../TransakCryptoCurrencyAvatar.svelte | 41 +++++++ .../TransakCryptoCurrencyTile.svelte | 24 ++++ .../components/TransakExchangePanel.svelte | 30 ++++- .../dashboard/buy-sell/components/index.ts | 2 +- .../components/DashboardSidebar.svelte | 8 +- .../actions/updateTransakCryptoCurrencies.ts | 90 +++++++++++++++ .../lib/auxiliary/transak/apis/transak.api.ts | 28 ++++- ...pi-crypto-currencies-response.interface.ts | 2 +- .../src/lib/auxiliary/transak/stores/index.ts | 1 + ...selected-exchange-crypto-currency.store.ts | 4 + .../stores/transak-crypto-currencies.store.ts | 13 ++- .../transak-window-data.interface.ts | 2 + packages/shared/src/locales/en.json | 2 +- 19 files changed, 341 insertions(+), 74 deletions(-) create mode 100644 packages/desktop/components/popup/popups/TransakSelectTokenPopup.svelte delete mode 100644 packages/desktop/views/dashboard/buy-sell/components/TokenTile.svelte create mode 100644 packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyAvatar.svelte create mode 100644 packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyTile.svelte create mode 100644 packages/shared/src/lib/auxiliary/transak/stores/selected-exchange-crypto-currency.store.ts diff --git a/packages/desktop/components/popup/Popup.svelte b/packages/desktop/components/popup/Popup.svelte index 9d54771aba..9f82ac46fc 100644 --- a/packages/desktop/components/popup/Popup.svelte +++ b/packages/desktop/components/popup/Popup.svelte @@ -64,6 +64,7 @@ import SyncAccountsPopup from './popups/SyncAccountsPopup.svelte' import TestDeepLinkFormPopup from './popups/TestDeepLinkFormPopup.svelte' import TokenInformationPopup from './popups/TokenInformationPopup.svelte' + import TransakSelectTokenPopup from './popups/TransakSelectTokenPopup.svelte' import VoteForProposal from './popups/VoteForProposalPopup.svelte' import VotingPowerToZeroPopup from './popups/VotingPowerToZeroPopup.svelte' import { localize } from '@core/i18n' @@ -146,6 +147,7 @@ [PopupId.SyncAccounts]: SyncAccountsPopup, [PopupId.TestDeepLinkForm]: TestDeepLinkFormPopup, [PopupId.TokenInformation]: TokenInformationPopup, + [PopupId.TransakSelectToken]: TransakSelectTokenPopup, [PopupId.VoteForProposal]: VoteForProposal, [PopupId.VotingPowerToZero]: VotingPowerToZeroPopup, } diff --git a/packages/desktop/components/popup/popups/TransakSelectTokenPopup.svelte b/packages/desktop/components/popup/popups/TransakSelectTokenPopup.svelte new file mode 100644 index 0000000000..c9f575bb54 --- /dev/null +++ b/packages/desktop/components/popup/popups/TransakSelectTokenPopup.svelte @@ -0,0 +1,106 @@ + + + + + diff --git a/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts b/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts index 429921049d..53c9ed9b22 100644 --- a/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts +++ b/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts @@ -48,6 +48,7 @@ export enum PopupId { SyncAccounts = 'syncAccounts', TestDeepLinkForm = 'testDeepLinkForm', TokenInformation = 'tokenInformation', + TransakSelectToken = 'transakSelectToken', VoteForProposal = 'voteForProposal', VotingPowerToZero = 'votingPowerToZero', } diff --git a/packages/desktop/lib/electron/managers/transak.manager.ts b/packages/desktop/lib/electron/managers/transak.manager.ts index fc0373bff5..cfbd42b7a0 100644 --- a/packages/desktop/lib/electron/managers/transak.manager.ts +++ b/packages/desktop/lib/electron/managers/transak.manager.ts @@ -219,7 +219,7 @@ export default class TransakManager implements ITransakManager { } private getUrl(data: ITransakWindowData): string { - const { address, currency, service, amount, paymentMethod } = data + const { address, currency, service, amount, paymentMethod, networkName, cryptoCurrencySymbol } = data const apiKey = process.env.TRANSAK_API_KEY if (typeof apiKey !== 'string') { @@ -248,8 +248,8 @@ export default class TransakManager implements ITransakManager { walletAddress: address, paymentMethod: paymentMethod, productsAvailed: service, - cryptoCurrencyCode: 'IOTA', - network: 'miota', + cryptoCurrencyCode: cryptoCurrencySymbol, + network: networkName, themeColor: '7C41C9', hideMenu: true, disableWalletAddressForm: true, diff --git a/packages/desktop/views/dashboard/buy-sell/BuySell.svelte b/packages/desktop/views/dashboard/buy-sell/BuySell.svelte index ac7e6c7e5c..9569e66a27 100644 --- a/packages/desktop/views/dashboard/buy-sell/BuySell.svelte +++ b/packages/desktop/views/dashboard/buy-sell/BuySell.svelte @@ -4,7 +4,7 @@ import { BuySellMainView } from './views' import { dashboardRoute, DashboardRoute } from '@core/router' import { onMount } from 'svelte' - import { updateTransakFiatCurrencies } from '@auxiliary/transak' + import { updateTransakCryptoCurrencies, updateTransakFiatCurrencies } from '@auxiliary/transak' $: if (features.analytics.dashboardRoute.buySell.enabled && $dashboardRoute === DashboardRoute.BuySell) { Platform.trackEvent('buy-sell-route', { route: $dashboardRoute }) @@ -12,6 +12,7 @@ onMount(() => { void updateTransakFiatCurrencies() + void updateTransakCryptoCurrencies() }) diff --git a/packages/desktop/views/dashboard/buy-sell/components/TokenTile.svelte b/packages/desktop/views/dashboard/buy-sell/components/TokenTile.svelte deleted file mode 100644 index ee3336b5a2..0000000000 --- a/packages/desktop/views/dashboard/buy-sell/components/TokenTile.svelte +++ /dev/null @@ -1,50 +0,0 @@ - - -{#if token && token.metadata} - -
- -
-
- - {token.metadata.name - ? truncateString(token.metadata.name, 13, 0) - : truncateString(token.id, 6, 7)} - - - {token.metadata - ? `≈ ${formatTokenAmount(tokenAmount ?? BigInt(0), token.metadata, { round: false })}` - : '-'} - -
-
- - {marketPrice ? formatCurrency(marketPrice, currency) : '-'} - - - {fiatValue !== undefined ? formatCurrency(fiatValue ?? '', currency) : '-'} - -
-
-
-
-{/if} diff --git a/packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyAvatar.svelte b/packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyAvatar.svelte new file mode 100644 index 0000000000..37cc41dd5b --- /dev/null +++ b/packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyAvatar.svelte @@ -0,0 +1,41 @@ + + +
+ + {#if image && !imageLoadError} + {cryptoCurrency.name} (imageLoadError = true)} + /> + {/if} + + {#if (size === 'base' || size === 'md' || size === 'lg') && !hideNetworkBadge} + + + + {/if} +
diff --git a/packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyTile.svelte b/packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyTile.svelte new file mode 100644 index 0000000000..b223344196 --- /dev/null +++ b/packages/desktop/views/dashboard/buy-sell/components/TransakCryptoCurrencyTile.svelte @@ -0,0 +1,24 @@ + + + +
+ +
+
+ {cryptoCurrency.symbol} + {cryptoCurrency.name} +
+ + {cryptoCurrency.network.name} + +
+
+
diff --git a/packages/desktop/views/dashboard/buy-sell/components/TransakExchangePanel.svelte b/packages/desktop/views/dashboard/buy-sell/components/TransakExchangePanel.svelte index c414ddf300..fd156ca64b 100644 --- a/packages/desktop/views/dashboard/buy-sell/components/TransakExchangePanel.svelte +++ b/packages/desktop/views/dashboard/buy-sell/components/TransakExchangePanel.svelte @@ -11,12 +11,19 @@ import { isDashboardSideBarExpanded } from '@core/ui' import { drawerState } from '@desktop/auxiliary/drawer/stores' import { DrawerState } from '@desktop/auxiliary/drawer/types' - import { IPopupState, IProfileAuthPopupState, popupState, profileAuthPopup } from '@desktop/auxiliary/popup' + import { + IPopupState, + IProfileAuthPopupState, + openPopup, + PopupId, + popupState, + profileAuthPopup, + } from '@desktop/auxiliary/popup' import { isFeatureEnabled } from '@lib/features/utils' import { Pane } from '@ui' import { onDestroy, tick } from 'svelte' - import { TokenTile, TransakAmountInput } from './' - import { transakFiatCurrencies } from '@auxiliary/transak' + import { TransakCryptoCurrencyTile, TransakAmountInput } from './' + import { selectedExchangeCryptoCurrency, transakCryptoCurrencies, transakFiatCurrencies } from '@auxiliary/transak' const CURRENCY_OPTIONS: IOption[] = Object.keys(FiatCurrency).map((currency) => ({ value: currency, @@ -65,7 +72,7 @@ state: IPopupState, profilePopupState: IProfileAuthPopupState, settingsState: ISettingsState, - drawerState: DrawerState + drawerState: DrawerState | undefined ): Promise { if (state.active || profilePopupState.active || settingsState.open || drawerState?.id) { await Platform.hideTransak() @@ -132,6 +139,10 @@ service: selectedTab.key as 'BUY' | 'SELL', amount: Number(fiatValue), paymentMethod: selectedPaymentOption.value ?? '', + networkName: + $selectedExchangeCryptoCurrency?.networkName ?? $transakCryptoCurrencies?.[0]?.networkName ?? 'miota', + cryptoCurrencySymbol: + $selectedExchangeCryptoCurrency?.symbol ?? $transakCryptoCurrencies?.[0]?.symbol ?? 'IOTA', }) isTransakOpen = true await updateTransakBounds() @@ -142,6 +153,10 @@ isTransakOpen = false } + function onTokenTileClick(): void { + openPopup({ id: PopupId.TransakSelectToken }) + } + onDestroy(() => { void Platform.closeTransak() isTransakOpen = false @@ -181,7 +196,12 @@ /> - + {#if $transakCryptoCurrencies && $transakCryptoCurrencies.length > 0} + + {/if}
= { + ...(process.env.STAGE === AppStage.PROD + ? { [SupportedStardustNetworkId.Iota]: 'miota' } + : { [SupportedStardustNetworkId.IotaTestnet]: 'miota' }), +} + +export async function updateTransakCryptoCurrencies(): Promise { + const stardustNetwork = getStardustNetwork() + const transakNetworkNameForStardustNetwork = STARDUST_NETWORK_ID_TO_TRANSAK_NETWORK_NAME_MAP[stardustNetwork.id] + const evmNetworks = getEvmNetworks() + + const allowedNetworkNames = [transakNetworkNameForStardustNetwork] + const allowedNetworkChainIds = evmNetworks.map((network) => network.chainId) + + const api = new TransakApi() + const response = await api.getFilteredCryptoCurrencies(allowedNetworkNames, allowedNetworkChainIds) + + // sort response by stardust network first and then by order of evm networks + response?.sort((a, b) => { + const aNetworkId = getNetworkIdFromTransakNetwork(a.network.name, a.network.chainId) + const bNetworkId = getNetworkIdFromTransakNetwork(b.network.name, b.network.chainId) + if (aNetworkId === stardustNetwork.id) { + return -1 + } else if (bNetworkId === stardustNetwork.id) { + return 1 + } else { + return evmNetworks.findIndex((network) => network.id === aNetworkId) < + evmNetworks.findIndex((network) => network.id === bNetworkId) + ? -1 + : 1 + } + }) + + transakCryptoCurrencies.set( + response?.reduce((acc, token) => { + if (!token.isAllowed) { + return acc + } + + return [...acc, buildTransakCryptoCurrencyFromResponseItem(token)] + }, [] as TransakCryptoCurrency[]) + ) +} + +function buildTransakCryptoCurrencyFromResponseItem( + responseItem: ITransakApiCryptoCurrenciesResponseItem +): TransakCryptoCurrency { + const networkId = getNetworkIdFromTransakNetwork(responseItem.network.name, responseItem.network.chainId) + const network = getNetwork(networkId) + if (!network) { + throw new Error(`Unsupported Transak network: ${responseItem.network.name}`) + } + return { + name: responseItem.name, + symbol: responseItem.symbol, + image: { + thumb: responseItem.image.thumb, + small: responseItem.image.small, + large: responseItem.image.large, + }, + network, + decimals: responseItem.decimals, + } +} + +function getNetworkIdFromTransakNetwork(transakNetworkName: string, chainId: string | null | undefined): NetworkId { + if (transakNetworkName === 'miota') { + return process.env.STAGE === AppStage.PROD + ? SupportedStardustNetworkId.Iota + : SupportedStardustNetworkId.IotaTestnet + } else if (chainId) { + return `${NetworkNamespace.Evm}:${chainId}` + } else { + throw new Error(`Unsupported Transak network: ${transakNetworkName}`) + } +} diff --git a/packages/shared/src/lib/auxiliary/transak/apis/transak.api.ts b/packages/shared/src/lib/auxiliary/transak/apis/transak.api.ts index 99030c5452..2b054b592c 100644 --- a/packages/shared/src/lib/auxiliary/transak/apis/transak.api.ts +++ b/packages/shared/src/lib/auxiliary/transak/apis/transak.api.ts @@ -1,6 +1,10 @@ import { BaseApi } from '@core/utils' import { TRANSAK_API_BASE_URL } from '../constants' -import { ITransakApiCryptoCurrenciesResponse, ITransakApiFiatCurrenciesResponse } from '../interfaces' +import { + ITransakApiCryptoCurrenciesResponse, + ITransakApiCryptoCurrenciesResponseItem, + ITransakApiFiatCurrenciesResponse, +} from '../interfaces' import { TransakApiEndpoint } from '../enums' export class TransakApi extends BaseApi { @@ -13,8 +17,26 @@ export class TransakApi extends BaseApi { return response } - async getCryptoCurrencies(): Promise { + async getCryptoCurrencies(): Promise { const response = await this.get(TransakApiEndpoint.CryptoCurrencies) - return response + return response?.response + } + + async getFilteredCryptoCurrencies( + allowedNetworkNames: string[], + allowedNetworkChainIds: string[] + ): Promise { + const response = await this.getCryptoCurrencies() + const filteredResponse = response?.filter((cryptoCurrency) => { + if (cryptoCurrency.isAllowed && !cryptoCurrency.isSuspended) { + return ( + allowedNetworkNames.some((networkName) => networkName === cryptoCurrency.network.name) || + allowedNetworkChainIds.some((chainId) => chainId === cryptoCurrency.network.chainId) + ) + } else { + return false + } + }) + return filteredResponse } } diff --git a/packages/shared/src/lib/auxiliary/transak/interfaces/transak-api-crypto-currencies-response.interface.ts b/packages/shared/src/lib/auxiliary/transak/interfaces/transak-api-crypto-currencies-response.interface.ts index 744ff7f655..9d7673933c 100644 --- a/packages/shared/src/lib/auxiliary/transak/interfaces/transak-api-crypto-currencies-response.interface.ts +++ b/packages/shared/src/lib/auxiliary/transak/interfaces/transak-api-crypto-currencies-response.interface.ts @@ -19,7 +19,7 @@ export interface ITransakApiCryptoCurrenciesResponseItem { isIgnorePriceVerification: boolean image_bk?: ITransakCryptoCurrencyImage kycCountriesNotSupported: string[] - network: { name: string; fiatCurrenciesNotSupported: string[]; chainId?: string } + network: { name: string; fiatCurrenciesNotSupported: string[]; chainId?: string | null } uniqueId: string tokenType: string isPayInAllowed: boolean diff --git a/packages/shared/src/lib/auxiliary/transak/stores/index.ts b/packages/shared/src/lib/auxiliary/transak/stores/index.ts index 2c21a11252..20bd985aac 100644 --- a/packages/shared/src/lib/auxiliary/transak/stores/index.ts +++ b/packages/shared/src/lib/auxiliary/transak/stores/index.ts @@ -1,2 +1,3 @@ +export * from './selected-exchange-crypto-currency.store' export * from './transak-crypto-currencies.store' export * from './transak-fiat-currencies.store' diff --git a/packages/shared/src/lib/auxiliary/transak/stores/selected-exchange-crypto-currency.store.ts b/packages/shared/src/lib/auxiliary/transak/stores/selected-exchange-crypto-currency.store.ts new file mode 100644 index 0000000000..376f5707cb --- /dev/null +++ b/packages/shared/src/lib/auxiliary/transak/stores/selected-exchange-crypto-currency.store.ts @@ -0,0 +1,4 @@ +import { writable, Writable } from 'svelte/store' +import { TransakCryptoCurrency } from './transak-crypto-currencies.store' + +export const selectedExchangeCryptoCurrency: Writable = writable(undefined) diff --git a/packages/shared/src/lib/auxiliary/transak/stores/transak-crypto-currencies.store.ts b/packages/shared/src/lib/auxiliary/transak/stores/transak-crypto-currencies.store.ts index 19dfaf7b2f..0ca87fba69 100644 --- a/packages/shared/src/lib/auxiliary/transak/stores/transak-crypto-currencies.store.ts +++ b/packages/shared/src/lib/auxiliary/transak/stores/transak-crypto-currencies.store.ts @@ -1,3 +1,12 @@ -import { writable } from 'svelte/store' +import { Network } from '@core/network' +import { Writable, writable } from 'svelte/store' -export const transakCryptoCurrencies = writable({}) +export type TransakCryptoCurrency = { + name: string + symbol: string + image: { thumb: string; small: string; large: string } + network: Network + decimals: number +} + +export const transakCryptoCurrencies: Writable = writable(undefined) diff --git a/packages/shared/src/lib/core/app/interfaces/transak-window-data.interface.ts b/packages/shared/src/lib/core/app/interfaces/transak-window-data.interface.ts index 32793943ae..1d49e11a74 100644 --- a/packages/shared/src/lib/core/app/interfaces/transak-window-data.interface.ts +++ b/packages/shared/src/lib/core/app/interfaces/transak-window-data.interface.ts @@ -6,4 +6,6 @@ export interface ITransakWindowData { service: 'BUY' | 'SELL' amount: number paymentMethod: string + networkName: string + cryptoCurrencySymbol: string } diff --git a/packages/shared/src/locales/en.json b/packages/shared/src/locales/en.json index b3ee089596..3b2ef2faee 100644 --- a/packages/shared/src/locales/en.json +++ b/packages/shared/src/locales/en.json @@ -2299,7 +2299,7 @@ "collectibles": "Collectibles", "governance": "Governance", "campaigns": "Campaigns", - "buySell": "Buy IOTA", + "buySell": "Buy / Sell", "developer": "Developer", "tokens": "Tokens", "settings": "Settings",