diff --git a/mobile-app/app/components/TokenDropdownButton.tsx b/mobile-app/app/components/TokenDropdownButton.tsx index 9ffab19ee1..e6b0210c4e 100644 --- a/mobile-app/app/components/TokenDropdownButton.tsx +++ b/mobile-app/app/components/TokenDropdownButton.tsx @@ -16,6 +16,7 @@ export enum TokenDropdownButtonStatus { export function TokenDropdownButton(props: { symbol?: string; + displayedTextSymbol?: string; testID: string; onPress?: () => void; status: TokenDropdownButtonStatus; @@ -61,7 +62,7 @@ export function TokenDropdownButton(props: { })} testID={`token_select_button_${props.testID}_display_symbol`} > - {props.symbol} + {props.displayedTextSymbol ?? props.symbol} )} diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioNavigator.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioNavigator.tsx index a75a5e893b..702f982fd3 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioNavigator.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioNavigator.tsx @@ -146,7 +146,7 @@ export interface PortfolioParamList { }; listType: TokenListType; list: any; - onTokenPress: (item: SelectionToken) => {}; + onTokenPress: (item: SelectionToken) => void; isFutureSwap?: boolean; isSearchDTokensOnly?: boolean; }; diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/ActionButtons.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/ActionButtons.tsx index 339025f866..a8524971c5 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/ActionButtons.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/ActionButtons.tsx @@ -24,6 +24,8 @@ import BigNumber from "bignumber.js"; import { ConvertIcon } from "@components/icons/assets/ConvertIcon"; import { ConversionMode } from "@screens/enum"; import { PortfolioParamList } from "../PortfolioNavigator"; +import { TokenListType } from "../../Dex/CompositeSwap/SwapTokenSelectionScreen"; +import { useConvertibleTokens } from "../hooks/ConvertibleTokens"; export interface ActionButtonsProps { name: string; @@ -40,6 +42,8 @@ export function ActionButtons(): JSX.Element { const { isFeatureAvailable } = useFeatureFlagContext(); const { domain } = useDomainContext(); const isEvmDomain = domain === DomainType.EVM; + + const { fromTokens } = useConvertibleTokens(); const navigation = useNavigation>(); const futureSwaps = useSelector((state: RootState) => futureSwapSelector(state) @@ -55,6 +59,31 @@ export function ActionButtons(): JSX.Element { hasFetchedToken && new BigNumber(DFIUtxo.amount ?? 0).plus(DFIToken.amount ?? 0).gt(0); + const navigateToTokenSelectionScreen = (listType: TokenListType): void => { + navigation.navigate("SwapTokenSelectionScreen", { + fromToken: { + symbol: undefined, + displaySymbol: undefined, + }, + listType: listType, + list: fromTokens, + onTokenPress: (item) => { + navigation.navigate({ + name: "ConvertScreen", + params: { + mode: + item.tokenId === "0" + ? ConversionMode.accountToUtxos + : ConversionMode.utxosToAccount, + }, + merge: true, + }); + }, + isFutureSwap: false, + isSearchDTokensOnly: false, + }); + }; + return ( { - navigation.navigate({ - name: "ConvertScreen", - params: { - mode: isEvmDomain - ? ConversionMode.accountToEvm - : ConversionMode.utxosToAccount, - }, - merge: true, - }); + if (isEvmDomain) { + navigation.navigate({ + name: "ConvertScreen", + params: { + mode: ConversionMode.evmToAccount, + }, + merge: true, + }); + } else { + navigateToTokenSelectionScreen(TokenListType.From); + } }} isEvmDomain /> diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/hooks/ConvertibleTokens.ts b/mobile-app/app/screens/AppNavigator/screens/Portfolio/hooks/ConvertibleTokens.ts new file mode 100644 index 0000000000..e31894e0bd --- /dev/null +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/hooks/ConvertibleTokens.ts @@ -0,0 +1,58 @@ +import { useSelector } from "react-redux"; +import BigNumber from "bignumber.js"; +import { RootState } from "@store"; +import { + DFITokenSelector, + DFIUtxoSelector, +} from "@waveshq/walletkit-ui/dist/store"; + +interface ConvertibleToken { + tokenId: string; + available: BigNumber; + token: { + name: string; + displaySymbol: string; + symbol: string; + isLPS?: boolean; + }; + factor?: string; + reserve?: string; +} + +export function useConvertibleTokens(): { + fromTokens: ConvertibleToken[]; +} { + const DFIUtxo = useSelector((state: RootState) => + DFIUtxoSelector(state.wallet) + ); + const DFIToken = useSelector((state: RootState) => + DFITokenSelector(state.wallet) + ); + + const fromTokens: ConvertibleToken[] = [ + { + tokenId: DFIToken.id, + available: new BigNumber(DFIToken.amount), + token: { + name: DFIToken.name, + displaySymbol: DFIToken.displaySymbol, + symbol: DFIToken.symbol, + isLPS: false, + }, + }, + { + tokenId: DFIUtxo.id, + available: new BigNumber(DFIUtxo.amount), + token: { + name: DFIUtxo.name, + displaySymbol: DFIUtxo.displaySymbol, + symbol: DFIUtxo.symbol, + isLPS: false, + }, + }, + ]; + + return { + fromTokens, + }; +} diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertConfirmationScreen.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertConfirmationScreen.tsx index b93e1e85c4..653d3ea0dc 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertConfirmationScreen.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertConfirmationScreen.tsx @@ -26,12 +26,9 @@ import { View } from "react-native"; import { useWalletContext } from "@shared-contexts/WalletContext"; import { useAddressLabel } from "@hooks/useAddressLabel"; import { NumberRowV2 } from "@components/NumberRowV2"; -import { - ConvertTokenUnit, - getDisplayUnit, -} from "@screens/AppNavigator/screens/Portfolio/screens/ConvertScreen"; -import { ScreenName } from "@screens/enum"; -import { ConversionMode } from "./ConvertScreen"; +import { ConvertTokenUnit } from "@screens/AppNavigator/screens/Portfolio/screens/ConvertScreen"; +import { ConversionMode, ScreenName } from "@screens/enum"; +import { DomainType, useDomainContext } from "@contexts/DomainContext"; import { PortfolioParamList } from "../PortfolioNavigator"; type Props = StackScreenProps; @@ -48,6 +45,7 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { originScreen, } = route.params; const { address } = useWalletContext(); + const { domain } = useDomainContext(); const addressLabel = useAddressLabel(address); const hasPendingJob = useSelector((state: RootState) => hasTxQueued(state.transactionQueue) @@ -124,10 +122,7 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { "screens/ConvertConfirmScreen", "You are converting to {{unit}}", { - unit: translate( - "screens/ConvertScreen", - getDisplayUnit(targetUnit) - ), + unit: translate("screens/ConvertScreen", targetUnit), } )} amount={amount} @@ -182,7 +177,7 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { }} rhs={{ value: getResultingValue( - ConvertTokenUnit.Token, + ConvertTokenUnit.DFI, fee, sourceBalance, sourceUnit, @@ -197,7 +192,7 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { }, subValue: { value: getResultingPercentage( - ConvertTokenUnit.Token, + ConvertTokenUnit.DFI, sourceBalance, sourceUnit, targetBalance @@ -218,7 +213,12 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { dark: tailwind("bg-transparent border-mono-dark-v2-300"), }} lhs={{ - value: translate("screens/ConvertConfirmScreen", "Resulting UTXO"), + value: translate( + "screens/ConvertConfirmScreen", + domain === DomainType.DFI + ? "Resulting UTXO" + : "Resulting Tokens (EVM)" + ), testID: "resulting_utxo_label", themedProps: { light: tailwind("text-mono-light-v2-500"), @@ -227,14 +227,16 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { }} rhs={{ value: getResultingValue( - ConvertTokenUnit.UTXO, + domain === DomainType.DFI + ? ConvertTokenUnit.UTXO + : ConvertTokenUnit.EVMDFI, fee, sourceBalance, sourceUnit, targetBalance, targetUnit ), - suffix: " DFI", + suffix: domain === DomainType.DFI ? " DFI" : "", testID: "resulting_utxo_value", themedProps: { light: tailwind("text-mono-light-v2-900 font-semibold-v2"), @@ -242,7 +244,9 @@ export function ConvertConfirmationScreen({ route }: Props): JSX.Element { }, subValue: { value: getResultingPercentage( - ConvertTokenUnit.UTXO, + domain === DomainType.DFI + ? ConvertTokenUnit.UTXO + : ConvertTokenUnit.EVMDFI, sourceBalance, sourceUnit, targetBalance diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertScreen.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertScreen.tsx index cfd6689daf..4fac4da047 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertScreen.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/ConvertScreen.tsx @@ -34,9 +34,15 @@ import { useToast } from "react-native-toast-notifications"; import { NumericFormat as NumberFormat } from "react-number-format"; import { getNumberFormatValue } from "@api/number-format-value"; import { ConversionMode } from "@screens/enum"; +import { + TokenDropdownButton, + TokenDropdownButtonStatus, +} from "@components/TokenDropdownButton"; +import { DomainType, useDomainContext } from "@contexts/DomainContext"; import { PortfolioParamList } from "../PortfolioNavigator"; import { TokenListType } from "../../Dex/CompositeSwap/SwapTokenSelectionScreen"; import { useTokenPrice } from "../hooks/TokenPrice"; +import { useConvertibleTokens } from "../hooks/ConvertibleTokens"; type Props = StackScreenProps; @@ -53,12 +59,13 @@ enum InlineTextStatus { export enum ConvertTokenUnit { UTXO = "UTXO", DFI = "DFI", - EVM = "EVM", + EVMDFI = "DFI (EVM)", } export function ConvertScreen(props: Props): JSX.Element { const { getTokenPrice } = useTokenPrice(); const { isLight } = useThemeContext(); + const { domain } = useDomainContext(); const client = useWhaleApiClient(); const logger = useLogger(); const tokens = useSelector((state: RootState) => @@ -85,6 +92,8 @@ export function ConvertScreen(props: Props): JSX.Element { InlineTextStatus.Default ); + const { fromTokens } = useConvertibleTokens(); + useEffect(() => { client.fee .estimate() @@ -162,10 +171,7 @@ export function ConvertScreen(props: Props): JSX.Element { ? "Max available {{unit}} entered" : "{{percent}} of available {{unit}} entered"; const toastOption = { - unit: translate( - "screens/ConvertScreen", - getDisplayUnit(sourceToken.unit) - ), + unit: translate("screens/ConvertScreen", sourceToken.unit), percent: type, }; toast.show(translate("screens/ConvertScreen", toastMessage, toastOption), { @@ -177,6 +183,7 @@ export function ConvertScreen(props: Props): JSX.Element { function onTogglePress(): void { let toggledMode: ConversionMode = mode; + if (mode === ConversionMode.accountToEvm) { toggledMode = ConversionMode.evmToAccount; } else if (mode === ConversionMode.evmToAccount) { @@ -191,6 +198,23 @@ export function ConvertScreen(props: Props): JSX.Element { setAmount(""); } + const navigateToTokenSelectionScreen = (listType: TokenListType): void => { + navigation.navigate("SwapTokenSelectionScreen", { + fromToken: { + symbol: sourceToken.symbol, + displaySymbol: sourceToken.displaySymbol, + }, + listType: listType, + list: fromTokens, + onTokenPress: () => { + // TODO(Pierre): add token press + // onTokenSelect(item, listType); + }, + isFutureSwap: false, + isSearchDTokensOnly: false, + }); + }; + return ( - + + {domain === DomainType.DFI && ( + { + navigateToTokenSelectionScreen(TokenListType.From); + }} + status={TokenDropdownButtonStatus.Enabled} + /> + )} + {domain === DomainType.EVM && ( + + )} @@ -300,7 +344,7 @@ export function ConvertScreen(props: Props): JSX.Element { : "", { amount: new BigNumber(sourceToken.amount).toFixed(8), - unit: getDisplayUnit(sourceToken.unit), + unit: sourceToken.unit, } )} @@ -372,16 +416,35 @@ export function ConvertScreen(props: Props): JSX.Element { )} /> - + {domain === DomainType.DFI && ( + { + navigateToTokenSelectionScreen(TokenListType.To); + }} + status={TokenDropdownButtonStatus.Enabled} + /> + )} + {domain === DomainType.EVM && ( + + )} tk.id === "0") as AddressToken), - amount: "696969", // TODO: GET DFI EVM balance here + displaySymbol: "EvmDFI", + amount: "69", // TODO(Pierre): GET DFI EVM balance here }; default: return tokens.find((tk) => tk.id === "0") as AddressToken; @@ -451,15 +515,16 @@ function getDestinationAddressToken( tokens: AddressToken[] ): AddressToken { switch (mode) { - case ConversionMode.utxosToAccount: - return tokens.find((tk) => tk.id === "0") as AddressToken; - case ConversionMode.evmToAccount: + case ConversionMode.accountToEvm: return { ...(tokens.find((tk) => tk.id === "0") as AddressToken), - amount: "696969", // TODO: GET DFI EVM balance here + displaySymbol: "EvmDFI", + amount: "69", // TODO(Pierre): GET DFI EVM balance here }; - default: + case ConversionMode.accountToUtxos: return tokens.find((tk) => tk.id === "0_utxo") as AddressToken; + default: + return tokens.find((tk) => tk.id === "0") as AddressToken; } } @@ -468,7 +533,7 @@ function getSourceTokenUnit(mode: ConversionMode): ConvertTokenUnit { case ConversionMode.utxosToAccount: return ConvertTokenUnit.UTXO; case ConversionMode.evmToAccount: - return ConvertTokenUnit.EVM; + return ConvertTokenUnit.EVMDFI; default: return ConvertTokenUnit.DFI; } @@ -477,7 +542,7 @@ function getSourceTokenUnit(mode: ConversionMode): ConvertTokenUnit { function getTargetTokenUnit(mode: ConversionMode): ConvertTokenUnit { switch (mode) { case ConversionMode.accountToEvm: - return ConvertTokenUnit.EVM; + return ConvertTokenUnit.EVMDFI; case ConversionMode.accountToUtxos: return ConvertTokenUnit.UTXO; default: @@ -490,7 +555,6 @@ function getDFIBalances( tokens: AddressToken[] ): [source: ConversionIO, target: ConversionIO] { const source: AddressToken = getSourceAddressToken(mode, tokens); - const sourceUnit = getSourceTokenUnit(mode); const target: AddressToken = getDestinationAddressToken(mode, tokens); @@ -620,7 +684,13 @@ function getConvertibleUtxoAmount( mode: ConversionMode, source: AddressToken ): string { - if (mode === ConversionMode.accountToUtxos) { + if ( + [ + ConversionMode.accountToUtxos, + ConversionMode.accountToEvm, + ConversionMode.evmToAccount, + ].includes(mode) + ) { return source.amount; } @@ -635,14 +705,6 @@ function isUtxoToAccount(mode: ConversionMode): boolean { return mode === ConversionMode.utxosToAccount; } -export function getDisplayUnit(unit: ConvertTokenUnit): "UTXO" | "DFI" { - if (unit === ConvertTokenUnit.EVM) { - return "DFI"; - } - - return unit; -} - function FixedTokenButton(props: { symbol: string; testID: string; diff --git a/mobile-app/app/screens/enum.ts b/mobile-app/app/screens/enum.ts index 47039ac461..6aacc4deb3 100644 --- a/mobile-app/app/screens/enum.ts +++ b/mobile-app/app/screens/enum.ts @@ -6,8 +6,8 @@ export enum ScreenName { } export enum ConversionMode { - utxosToAccount, - accountToUtxos, - evmToAccount, - accountToEvm, + utxosToAccount = "utxosToAccount", + accountToUtxos = "accountToUtxos", + evmToAccount = "evmToAccount", + accountToEvm = "accountToEvm", }