Skip to content

Commit 922ae03

Browse files
committed
feat(suite-native): wire approval and revoke screen to trade data
1 parent ecbbbba commit 922ae03

File tree

8 files changed

+267
-72
lines changed

8 files changed

+267
-72
lines changed

suite-native/intl/src/messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,7 @@ export const messages = {
23002300
providerNamePlaceholder: 'Provider',
23012301
providerReceiveAddressLabel: "{providerName}'s receive address",
23022302
confirmationAlertTitle: 'Failed to confirm offer.',
2303+
approvalSuccessAlert: 'Spending approval confirmed.',
23032304
},
23042305
tradingExchangeApprovalScreen: {
23052306
title: 'Set {symbol} spending',

suite-native/module-trading/src/components/exchange/ExchangeApprovalLimitSheet/ExchangeApprovalLimitSheet.tsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,42 @@
11
import { memo, useEffect } from 'react';
22
import { useSelector } from 'react-redux';
33

4+
import { DexApprovalType, ExchangeTrade } from 'invity-api';
5+
46
import {
57
TradingRootState,
68
cryptoIdToNetworkSymbolAndContractAddress,
79
selectTradingCoinSymbolByCryptoId,
8-
selectTradingExchangeSelectedQuote,
910
selectTradingProviderByNameAndTradeType,
1011
} from '@suite-common/trading';
1112
import { BottomSheetModal, VStack, useBottomSheetModal } from '@suite-native/atoms';
1213
import { Translation } from '@suite-native/intl';
1314

1415
import { ExchangeApprovalLimitCard } from './ExchangeApprovalLimitCard';
1516

16-
type ExchangeApprovalLimitSheetProps = {
17+
export type ExchangeApprovalLimitSheetProps = {
1718
isVisible: boolean;
1819
onDismiss: () => void;
20+
onApprovalTypeSelect: (type: DexApprovalType) => void;
21+
selectedApprovalType: DexApprovalType;
22+
quote: ExchangeTrade;
1923
};
2024

2125
export const ExchangeApprovalLimitSheet = memo(
22-
({ isVisible, onDismiss }: ExchangeApprovalLimitSheetProps) => {
23-
const { bottomSheetRef, openModal } = useBottomSheetModal();
26+
({
27+
isVisible,
28+
onDismiss,
29+
onApprovalTypeSelect,
30+
selectedApprovalType,
31+
quote,
32+
}: ExchangeApprovalLimitSheetProps) => {
33+
const { bottomSheetRef, openModal, closeModal } = useBottomSheetModal();
2434

25-
useEffect(() => {
26-
if (isVisible) {
27-
openModal();
28-
}
29-
}, [isVisible, openModal]);
35+
useEffect(
36+
() => (isVisible ? openModal() : closeModal()),
37+
[isVisible, openModal, closeModal],
38+
);
3039

31-
const quote = useSelector(selectTradingExchangeSelectedQuote);
3240
const providerInfo = useSelector((state: TradingRootState) =>
3341
selectTradingProviderByNameAndTradeType(state, quote?.exchange, 'exchange'),
3442
);
@@ -37,10 +45,6 @@ export const ExchangeApprovalLimitSheet = memo(
3745
selectTradingCoinSymbolByCryptoId(state, quote?.send),
3846
);
3947

40-
if (!quote) {
41-
return null;
42-
}
43-
4448
const { symbol, contractAddress } = quote.send
4549
? cryptoIdToNetworkSymbolAndContractAddress(quote.send)
4650
: {};
@@ -49,7 +53,7 @@ export const ExchangeApprovalLimitSheet = memo(
4953
return null;
5054
}
5155

52-
const limitAmount = `200.32 ${coinSymbol}`; //TODO
56+
const limitAmount = `${quote.sendStringAmount} ${coinSymbol}`;
5357

5458
return (
5559
<BottomSheetModal
@@ -74,9 +78,9 @@ export const ExchangeApprovalLimitSheet = memo(
7478
}
7579
symbol={symbol}
7680
contractAddress={contractAddress}
77-
isChecked={true}
81+
isChecked={selectedApprovalType === 'INFINITE'}
7882
onChange={() => {
79-
// TODO
83+
onApprovalTypeSelect('INFINITE');
8084
}}
8185
/>
8286

@@ -90,9 +94,9 @@ export const ExchangeApprovalLimitSheet = memo(
9094
}
9195
symbol={symbol}
9296
contractAddress={contractAddress}
93-
isChecked={false}
97+
isChecked={selectedApprovalType === 'MINIMAL'}
9498
onChange={() => {
95-
// TODO
99+
onApprovalTypeSelect('MINIMAL');
96100
}}
97101
/>
98102
</VStack>

suite-native/module-trading/src/hooks/exchange/useExchangeSelectQuote.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useNavigation } from '@react-navigation/native';
55

66
import {
77
exchangeThunks,
8+
parseCryptoId,
89
selectTradingExchangeIsLoading,
910
selectTradingMaxSlippagePercentage,
1011
} from '@suite-common/trading';
@@ -22,6 +23,10 @@ import {
2223
selectExchangeSelectedSendAccount,
2324
} from '../../selectors/exchangeSelectors';
2425
import { ExchangeFormType } from '../../types/exchange';
26+
import {
27+
getApprovalStatus,
28+
tokenSupportsIncreasingAllowance,
29+
} from '../../utils/general/approvalStatusUtils';
2530
import { isFullySelectedReceiveAccount } from '../../utils/general/receiveAccountUtils';
2631
import { getSymbolFromTradeableAsset } from '../../utils/general/tradeableAssetUtils';
2732
import { useConsent } from '../general/useConsent';
@@ -87,7 +92,36 @@ export const useExchangeSelectQuote = (form: ExchangeFormType) => {
8792
userConsent: waitForConsent,
8893
nextStep: () => {
8994
clearExchangeFormQuoteData(form);
90-
navigation.navigate(TradingStackRoutes.TradingExchangePreview);
95+
96+
const approvalStatus = getApprovalStatus(candidateQuote);
97+
if (approvalStatus === 'approved' || approvalStatus === 'not_needed') {
98+
return navigation.navigate(TradingStackRoutes.TradingExchangePreview, {});
99+
}
100+
101+
const { contractAddress } = candidateQuote.send
102+
? parseCryptoId(candidateQuote.send)
103+
: {};
104+
105+
const isIncreasingAllowanceSupported =
106+
tokenSupportsIncreasingAllowance(contractAddress);
107+
108+
if (approvalStatus === 'needs_increase' && isIncreasingAllowanceSupported) {
109+
return navigation.navigate(TradingStackRoutes.TradingExchangeApproval, {
110+
quote: candidateQuote,
111+
shouldIncreaseLimit: true,
112+
});
113+
}
114+
115+
if (approvalStatus === 'needs_increase') {
116+
return navigation.navigate(TradingStackRoutes.TradingExchangeRevoke, {
117+
quote: candidateQuote,
118+
shouldIncreaseLimit: true,
119+
});
120+
}
121+
122+
return navigation.navigate(TradingStackRoutes.TradingExchangeApproval, {
123+
quote: candidateQuote,
124+
});
91125
},
92126
onCancel: () => {},
93127
}),

suite-native/module-trading/src/screens/TradingExchangeApprovalScreen.tsx

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,58 @@
1-
import { Pressable } from 'react-native';
1+
import { useState } from 'react';
2+
import { Pressable } from 'react-native-gesture-handler';
3+
import Animated from 'react-native-reanimated';
24
import { useSelector } from 'react-redux';
35

4-
import { invariant } from '@suite-common/suite-utils';
6+
import { useNavigation } from '@react-navigation/native';
7+
import { DexApprovalType } from 'invity-api';
8+
59
import {
610
TradingRootState,
711
cryptoIdToNetworkAndContractAddress,
812
selectTradingCoinSymbolByCryptoId,
9-
selectTradingExchangeSelectedQuote,
1013
selectTradingProviderByNameAndTradeType,
1114
} from '@suite-common/trading';
1215
import { asBaseCurrencyAmount } from '@suite-common/wallet-utils';
1316
import { Box, Button, Card, HStack, InlineAlertBox, Text, VStack } from '@suite-native/atoms';
1417
import { BaseCurrencyAmountFormatter } from '@suite-native/formatters';
1518
import { CryptoIcon, Icon, NetworkIcon } from '@suite-native/icons';
1619
import { Translation } from '@suite-native/intl';
17-
import { DynamicScreenHeader, Screen } from '@suite-native/navigation';
20+
import {
21+
DynamicScreenHeader,
22+
RootStackParamList,
23+
Screen,
24+
StackNavigationProps,
25+
StackToStackCompositeScreenProps,
26+
TradingStackParamList,
27+
TradingStackRoutes,
28+
} from '@suite-native/navigation';
1829
import { BigNumber } from '@trezor/utils';
1930

2031
import { TradeInfoHeader } from '../components/TradeInfo/TradeInfoHeader';
2132
import { TradeInfoRow } from '../components/TradeInfo/TradeInfoRow';
2233
import { ExchangeApprovalLimitSheet } from '../components/exchange/ExchangeApprovalLimitSheet/ExchangeApprovalLimitSheet';
2334
import { ProviderLogo } from '../components/general/ProviderLogo';
35+
import { useExchangeFlow } from '../hooks/exchange/useExchangeFlow';
2436
import { useBottomSheetControls } from '../hooks/general/useBottomSheetControls';
2537
import { selectExchangeSelectedSendAccount } from '../selectors/exchangeSelectors';
2638

27-
export const TradingExchangeApprovalScreen = () => {
28-
const quote = useSelector(selectTradingExchangeSelectedQuote);
39+
type TradingExchangeApprovalScreenProps = StackToStackCompositeScreenProps<
40+
TradingStackParamList,
41+
TradingStackRoutes.TradingExchangeApproval,
42+
RootStackParamList
43+
>;
2944

30-
invariant(quote, 'quote must be defined');
45+
export const TradingExchangeApprovalScreen = ({
46+
route: { params },
47+
}: TradingExchangeApprovalScreenProps) => {
48+
const { quote, shouldIncreaseLimit, isRevoked } = params;
49+
const navigation =
50+
useNavigation<
51+
StackNavigationProps<TradingStackParamList, TradingStackRoutes.TradingExchangeApproval>
52+
>();
3153

3254
const account = useSelector(selectExchangeSelectedSendAccount);
55+
const [selectedApprovalType, setSelectedApprovalType] = useState<DexApprovalType>('INFINITE');
3356

3457
const { network, contractAddress } = quote.send
3558
? cryptoIdToNetworkAndContractAddress(quote.send)
@@ -45,8 +68,36 @@ export const TradingExchangeApprovalScreen = () => {
4568
selectTradingCoinSymbolByCryptoId(state, quote?.send),
4669
);
4770

71+
const { confirmTrade, fetchFeesAndCompose } = useExchangeFlow();
72+
4873
const fee = '4.76'; // TODO
4974

75+
const handleContinue = async () => {
76+
const updatedQuote = {
77+
...quote,
78+
approvalType: selectedApprovalType,
79+
};
80+
81+
const success = await confirmTrade({
82+
receiveAddress: quote.receiveAddress ?? '',
83+
trade: updatedQuote,
84+
approvalFlow: true,
85+
nextStep: () => {},
86+
});
87+
88+
if (success) {
89+
// TODO
90+
await fetchFeesAndCompose();
91+
92+
navigation.navigate(TradingStackRoutes.TradingExchangePreview, { isApproved: true });
93+
}
94+
};
95+
96+
const handleApprovalTypeChange = (newType: DexApprovalType) => {
97+
setSelectedApprovalType(newType);
98+
hideSheet();
99+
};
100+
50101
return (
51102
<Screen
52103
header={
@@ -63,23 +114,32 @@ export const TradingExchangeApprovalScreen = () => {
63114
values={{ symbol: coinSymbol }}
64115
/>
65116
}
66-
closeActionType="close"
117+
closeActionType="back"
67118
/>
68119
}
69120
>
70121
<VStack spacing="sp16">
71-
<InlineAlertBox
72-
title={
73-
<Translation id="moduleTrading.tradingExchangeApprovalScreen.revokeSuccessAlert" />
74-
}
75-
variant="success"
76-
/>
77-
<InlineAlertBox
78-
title={
79-
<Translation id="moduleTrading.tradingExchangeApprovalScreen.lowLimitInfoAlert" />
80-
}
81-
variant="info"
82-
/>
122+
{!!shouldIncreaseLimit && (
123+
<Animated.View>
124+
<InlineAlertBox
125+
title={
126+
<Translation id="moduleTrading.tradingExchangeApprovalScreen.lowLimitInfoAlert" />
127+
}
128+
variant="warning"
129+
/>
130+
</Animated.View>
131+
)}
132+
133+
{!!isRevoked && (
134+
<Animated.View>
135+
<InlineAlertBox
136+
variant="success"
137+
title={
138+
<Translation id="moduleTrading.tradingExchangeApprovalScreen.revokeSuccessAlert" />
139+
}
140+
/>
141+
</Animated.View>
142+
)}
83143

84144
<Card noPadding>
85145
<TradeInfoHeader
@@ -140,7 +200,11 @@ export const TradingExchangeApprovalScreen = () => {
140200
/>
141201
)}
142202
<Text variant="hint" color="textSubdued">
143-
<Translation id="moduleTrading.tradingExchangeApprovalScreen.unlimitedLabel" />
203+
{selectedApprovalType === 'INFINITE' ? (
204+
<Translation id="moduleTrading.tradingExchangeApprovalScreen.unlimitedLabel" />
205+
) : (
206+
`${quote.sendStringAmount} ${coinSymbol}`
207+
)}
144208
</Text>
145209
<Icon name="caretDown" size="medium" />
146210
</HStack>
@@ -177,15 +241,17 @@ export const TradingExchangeApprovalScreen = () => {
177241
</VStack>
178242

179243
<Box paddingTop="sp20">
180-
<Button
181-
onPress={() => {
182-
// TODO
183-
}}
184-
>
244+
<Button onPress={handleContinue}>
185245
<Translation id="generic.buttons.continue" />
186246
</Button>
187247
</Box>
188-
<ExchangeApprovalLimitSheet isVisible={isSheetVisible} onDismiss={hideSheet} />
248+
<ExchangeApprovalLimitSheet
249+
isVisible={isSheetVisible}
250+
onDismiss={hideSheet}
251+
onApprovalTypeSelect={handleApprovalTypeChange}
252+
selectedApprovalType={selectedApprovalType}
253+
quote={quote}
254+
/>
189255
</Screen>
190256
);
191257
};

0 commit comments

Comments
 (0)