@rango-dev/widget-embedded 0.20.1-next.16 → 0.20.1-next.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/BlockchainList/BlockchainList.styles.d.ts +4 -0
- package/dist/components/BlockchainList/BlockchainList.styles.d.ts.map +1 -1
- package/dist/components/BlockchainsSection/BlockchainsSection.styles.d.ts +2 -0
- package/dist/components/BlockchainsSection/BlockchainsSection.styles.d.ts.map +1 -1
- package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts +488 -1102
- package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts.map +1 -1
- package/dist/components/ConfirmWalletsModal/ConfirmWalletsModal.d.ts.map +1 -1
- package/dist/components/ConfirmWalletsModal/WalletList.styles.d.ts +6 -0
- package/dist/components/ConfirmWalletsModal/WalletList.styles.d.ts.map +1 -1
- package/dist/components/CustomCollapsible/CustomCollapsible.d.ts +5 -0
- package/dist/components/CustomCollapsible/CustomCollapsible.d.ts.map +1 -0
- package/dist/components/CustomCollapsible/CustomCollapsible.styles.d.ts +654 -0
- package/dist/components/CustomCollapsible/CustomCollapsible.styles.d.ts.map +1 -0
- package/dist/components/CustomCollapsible/CustomCollapsible.types.d.ts +10 -0
- package/dist/components/CustomCollapsible/CustomCollapsible.types.d.ts.map +1 -0
- package/dist/components/HeaderButtons/HeaderButtons.styles.d.ts +10 -0
- package/dist/components/HeaderButtons/HeaderButtons.styles.d.ts.map +1 -1
- package/dist/components/Layout/Layout.styles.d.ts +6 -0
- package/dist/components/Layout/Layout.styles.d.ts.map +1 -1
- package/dist/components/LoadingLiquiditySourceList/LoadingLiquiditySourceList.styles.d.ts +2 -0
- package/dist/components/LoadingLiquiditySourceList/LoadingLiquiditySourceList.styles.d.ts.map +1 -1
- package/dist/components/LoadingSwapDetails/LoadingSwapDetails.styles.d.ts +28 -0
- package/dist/components/LoadingSwapDetails/LoadingSwapDetails.styles.d.ts.map +1 -1
- package/dist/components/NoResult/NoResult.styles.d.ts +6 -0
- package/dist/components/NoResult/NoResult.styles.d.ts.map +1 -1
- package/dist/components/NotificationContent/NotificationContent.styles.d.ts +8 -0
- package/dist/components/NotificationContent/NotificationContent.styles.d.ts.map +1 -1
- package/dist/components/Quote/Quote.d.ts.map +1 -1
- package/dist/components/Quote/Quote.styles.d.ts +356 -2
- package/dist/components/Quote/Quote.styles.d.ts.map +1 -1
- package/dist/components/Quote/Quote.types.d.ts +19 -0
- package/dist/components/Quote/Quote.types.d.ts.map +1 -1
- package/dist/components/Quote/QuoteCostDetails.d.ts +4 -0
- package/dist/components/Quote/QuoteCostDetails.d.ts.map +1 -0
- package/dist/components/Quote/QuoteCostDetails.styles.d.ts +806 -0
- package/dist/components/Quote/QuoteCostDetails.styles.d.ts.map +1 -0
- package/dist/components/Quote/QuoteSummary.styles.d.ts +4 -0
- package/dist/components/Quote/QuoteSummary.styles.d.ts.map +1 -1
- package/dist/components/Quote/QuoteTrigger .d.ts +4 -0
- package/dist/components/Quote/QuoteTrigger .d.ts.map +1 -0
- package/dist/components/QuoteSkeleton/QuoteSkeleton.styles.d.ts +8 -0
- package/dist/components/QuoteSkeleton/QuoteSkeleton.styles.d.ts.map +1 -1
- package/dist/components/QuoteSkeleton/QuoteSummarySkeleton.styles.d.ts +20 -0
- package/dist/components/QuoteSkeleton/QuoteSummarySkeleton.styles.d.ts.map +1 -1
- package/dist/components/QuoteSkeleton/StepSkeleton.styles.d.ts +10 -0
- package/dist/components/QuoteSkeleton/StepSkeleton.styles.d.ts.map +1 -1
- package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts +8 -0
- package/dist/components/QuoteWarningsAndErrors/QuoteWarningsAndErrors.styles.d.ts.map +1 -1
- package/dist/components/SearchInput/SearchInput.styles.d.ts +2 -0
- package/dist/components/SearchInput/SearchInput.styles.d.ts.map +1 -1
- package/dist/components/SettingsContainer/SettingsContainer.style.d.ts +8 -0
- package/dist/components/SettingsContainer/SettingsContainer.style.d.ts.map +1 -1
- package/dist/components/Slippage/Slippage.styles.d.ts +10 -0
- package/dist/components/Slippage/Slippage.styles.d.ts.map +1 -1
- package/dist/components/SwapDetails/SwapDetails.styles.d.ts +24 -0
- package/dist/components/SwapDetails/SwapDetails.styles.d.ts.map +1 -1
- package/dist/components/SwapDetailsAlerts/SwapDetailsAlerts.styles.d.ts +2 -0
- package/dist/components/SwapDetailsAlerts/SwapDetailsAlerts.styles.d.ts.map +1 -1
- package/dist/components/SwapDetailsModal/SwapDetailsModal.styles.d.ts +2 -0
- package/dist/components/SwapDetailsModal/SwapDetailsModal.styles.d.ts.map +1 -1
- package/dist/components/SwapsGroup/SwapsGroup.styles.d.ts +10 -0
- package/dist/components/SwapsGroup/SwapsGroup.styles.d.ts.map +1 -1
- package/dist/components/TokenList/TokenList.styles.d.ts +34 -0
- package/dist/components/TokenList/TokenList.styles.d.ts.map +1 -1
- package/dist/components/WalletModal/WalletModalContent.styles.d.ts +8 -0
- package/dist/components/WalletModal/WalletModalContent.styles.d.ts.map +1 -1
- package/dist/constants/quote.d.ts +12 -0
- package/dist/constants/quote.d.ts.map +1 -0
- package/dist/containers/App/App.styles.d.ts +2 -0
- package/dist/containers/App/App.styles.d.ts.map +1 -1
- package/dist/containers/QuoteInfo/QuoteInfo.styles.d.ts +10 -0
- package/dist/containers/QuoteInfo/QuoteInfo.styles.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +4 -4
- package/dist/utils/quote.d.ts +5 -1
- package/dist/utils/quote.d.ts.map +1 -1
- package/dist/utils/swap.d.ts +5 -1
- package/dist/utils/swap.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/ConfirmWalletsModal/ConfirmWallets.styles.ts +27 -96
- package/src/components/ConfirmWalletsModal/ConfirmWalletsModal.tsx +24 -39
- package/src/components/CustomCollapsible/CustomCollapsible.styles.ts +75 -0
- package/src/components/CustomCollapsible/CustomCollapsible.tsx +55 -0
- package/src/components/CustomCollapsible/CustomCollapsible.types.ts +10 -0
- package/src/components/Quote/Quote.styles.ts +44 -5
- package/src/components/Quote/Quote.tsx +10 -77
- package/src/components/Quote/Quote.types.ts +22 -0
- package/src/components/Quote/QuoteCostDetails.styles.ts +50 -0
- package/src/components/Quote/QuoteCostDetails.tsx +218 -0
- package/src/components/Quote/QuoteTrigger .tsx +215 -0
- package/src/constants/quote.ts +23 -0
- package/src/utils/quote.ts +18 -1
- package/src/utils/swap.ts +35 -0
|
@@ -5,12 +5,8 @@ import type { SwapResult } from 'rango-sdk';
|
|
|
5
5
|
import { i18n } from '@lingui/core';
|
|
6
6
|
import {
|
|
7
7
|
Alert,
|
|
8
|
-
ChevronDownIcon,
|
|
9
|
-
ChevronRightIcon,
|
|
10
8
|
Divider,
|
|
11
|
-
Image,
|
|
12
9
|
InfoIcon,
|
|
13
|
-
QuoteCost,
|
|
14
10
|
StepDetails,
|
|
15
11
|
TokenAmount,
|
|
16
12
|
Tooltip,
|
|
@@ -20,8 +16,6 @@ import BigNumber from 'bignumber.js';
|
|
|
20
16
|
import React, { useLayoutEffect, useRef, useState } from 'react';
|
|
21
17
|
|
|
22
18
|
import {
|
|
23
|
-
GAS_FEE_MAX_DECIMALS,
|
|
24
|
-
GAS_FEE_MIN_DECIMALS,
|
|
25
19
|
PERCENTAGE_CHANGE_MAX_DECIMALS,
|
|
26
20
|
PERCENTAGE_CHANGE_MIN_DECIMALS,
|
|
27
21
|
TOKEN_AMOUNT_MAX_DECIMALS,
|
|
@@ -40,32 +34,25 @@ import {
|
|
|
40
34
|
getBlockchainShortNameFor,
|
|
41
35
|
getSwapperDisplayName,
|
|
42
36
|
} from '../../utils/meta';
|
|
43
|
-
import {
|
|
44
|
-
formatTooltipNumbers,
|
|
45
|
-
numberToString,
|
|
46
|
-
secondsToString,
|
|
47
|
-
totalArrivalTime,
|
|
48
|
-
} from '../../utils/numbers';
|
|
37
|
+
import { formatTooltipNumbers, numberToString } from '../../utils/numbers';
|
|
49
38
|
import { getPriceImpact, getPriceImpactLevel } from '../../utils/quote';
|
|
50
|
-
import { getTotalFeeInUsd } from '../../utils/swap';
|
|
51
39
|
|
|
52
40
|
import {
|
|
53
41
|
BasicInfoOutput,
|
|
54
42
|
basicInfoStyles,
|
|
55
|
-
ChainImageContainer,
|
|
56
|
-
Chains,
|
|
57
43
|
ContainerInfoOutput,
|
|
58
44
|
Content,
|
|
59
45
|
EXPANDABLE_QUOTE_TRANSITION_DURATION,
|
|
60
46
|
FrameIcon,
|
|
61
47
|
HorizontalSeparator,
|
|
62
|
-
IconContainer,
|
|
63
48
|
QuoteContainer,
|
|
64
49
|
stepsDetailsStyles,
|
|
65
50
|
SummaryContainer,
|
|
66
51
|
summaryStyles,
|
|
67
52
|
} from './Quote.styles';
|
|
53
|
+
import { QuoteCostDetails } from './QuoteCostDetails';
|
|
68
54
|
import { QuoteSummary } from './QuoteSummary';
|
|
55
|
+
import { QuoteTrigger } from './QuoteTrigger ';
|
|
69
56
|
|
|
70
57
|
export function Quote(props: QuoteProps) {
|
|
71
58
|
const {
|
|
@@ -77,19 +64,12 @@ export function Quote(props: QuoteProps) {
|
|
|
77
64
|
type,
|
|
78
65
|
recommended = true,
|
|
79
66
|
} = props;
|
|
80
|
-
const tokens = useAppStore().tokens();
|
|
81
67
|
const blockchains = useAppStore().blockchains();
|
|
82
68
|
const swappers = useAppStore().swappers();
|
|
83
69
|
|
|
84
70
|
const [expanded, setExpanded] = useState(props.expanded);
|
|
85
71
|
const quoteRef = useRef<HTMLButtonElement | null>(null);
|
|
86
72
|
const prevExpanded = useRef(expanded);
|
|
87
|
-
const totalFee = numberToString(
|
|
88
|
-
getTotalFeeInUsd(quote.result?.swaps ?? [], tokens),
|
|
89
|
-
GAS_FEE_MIN_DECIMALS,
|
|
90
|
-
GAS_FEE_MAX_DECIMALS
|
|
91
|
-
);
|
|
92
|
-
const totalTime = secondsToString(totalArrivalTime(quote.result?.swaps));
|
|
93
73
|
const roundedInput = numberToString(
|
|
94
74
|
input.value,
|
|
95
75
|
TOKEN_AMOUNT_MIN_DECIMALS,
|
|
@@ -283,7 +263,6 @@ export function Quote(props: QuoteProps) {
|
|
|
283
263
|
const steps = getQuoteSteps(quote.result?.swaps ?? []);
|
|
284
264
|
const numberOfSteps = steps.length;
|
|
285
265
|
const tooltipContainer = getContainer();
|
|
286
|
-
|
|
287
266
|
useLayoutEffect(() => {
|
|
288
267
|
if (expanded && !prevExpanded.current && quoteRef.current) {
|
|
289
268
|
setTimeout(() => {
|
|
@@ -300,7 +279,7 @@ export function Quote(props: QuoteProps) {
|
|
|
300
279
|
listItem={type === 'list-item'}
|
|
301
280
|
basic={type === 'basic'}>
|
|
302
281
|
<div className={summaryStyles()}>
|
|
303
|
-
<
|
|
282
|
+
<QuoteCostDetails steps={numberOfSteps} quote={quote} />
|
|
304
283
|
{type === 'basic' && (
|
|
305
284
|
<div className={basicInfoStyles()}>
|
|
306
285
|
<FrameIcon>
|
|
@@ -374,59 +353,13 @@ export function Quote(props: QuoteProps) {
|
|
|
374
353
|
recommended={recommended}
|
|
375
354
|
open={expanded}
|
|
376
355
|
onOpenChange={setExpanded}>
|
|
377
|
-
<
|
|
378
|
-
|
|
356
|
+
<QuoteTrigger
|
|
357
|
+
quoteRef={quoteRef}
|
|
379
358
|
recommended={recommended}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const arrow = (
|
|
385
|
-
<IconContainer>
|
|
386
|
-
<ChevronRightIcon
|
|
387
|
-
size={12}
|
|
388
|
-
color="black"
|
|
389
|
-
{...(step.state && {
|
|
390
|
-
color: step.state === 'error' ? 'error' : 'warning',
|
|
391
|
-
})}
|
|
392
|
-
/>
|
|
393
|
-
</IconContainer>
|
|
394
|
-
);
|
|
395
|
-
return (
|
|
396
|
-
<React.Fragment key={key}>
|
|
397
|
-
<Tooltip
|
|
398
|
-
container={tooltipContainer}
|
|
399
|
-
side="bottom"
|
|
400
|
-
sideOffset={4}
|
|
401
|
-
content={step.from.chain.displayName}>
|
|
402
|
-
<ChainImageContainer
|
|
403
|
-
state={step.state || steps[index - 1]?.state}>
|
|
404
|
-
<Image src={step.from.chain.image} size={16} />
|
|
405
|
-
</ChainImageContainer>
|
|
406
|
-
</Tooltip>
|
|
407
|
-
{index === numberOfSteps - 1 && (
|
|
408
|
-
<>
|
|
409
|
-
{arrow}
|
|
410
|
-
<Tooltip
|
|
411
|
-
container={tooltipContainer}
|
|
412
|
-
side="bottom"
|
|
413
|
-
sideOffset={4}
|
|
414
|
-
content={step.to.chain.displayName}>
|
|
415
|
-
<ChainImageContainer state={step.state}>
|
|
416
|
-
<Image src={step.to.chain.image} size={16} />
|
|
417
|
-
</ChainImageContainer>
|
|
418
|
-
</Tooltip>
|
|
419
|
-
</>
|
|
420
|
-
)}
|
|
421
|
-
{index !== numberOfSteps - 1 && <>{arrow}</>}
|
|
422
|
-
</React.Fragment>
|
|
423
|
-
);
|
|
424
|
-
})}
|
|
425
|
-
</div>
|
|
426
|
-
<IconContainer orientation={expanded ? 'up' : 'down'}>
|
|
427
|
-
<ChevronDownIcon size={12} color="black" />
|
|
428
|
-
</IconContainer>
|
|
429
|
-
</Chains>
|
|
359
|
+
setExpanded={setExpanded}
|
|
360
|
+
expanded={expanded}
|
|
361
|
+
steps={steps}
|
|
362
|
+
/>
|
|
430
363
|
<Content open={expanded}>
|
|
431
364
|
<HorizontalSeparator />
|
|
432
365
|
<div className={stepsDetailsStyles()}>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { QuoteError, QuoteWarning } from '../../types';
|
|
2
|
+
import type { Step } from '@rango-dev/ui';
|
|
2
3
|
import type { BestRouteResponse } from 'rango-sdk';
|
|
3
4
|
import type { ReactNode } from 'react';
|
|
4
5
|
|
|
@@ -13,3 +14,24 @@ export type QuoteProps = {
|
|
|
13
14
|
output: { value: string; usdValue?: string };
|
|
14
15
|
expanded?: boolean;
|
|
15
16
|
};
|
|
17
|
+
|
|
18
|
+
export type QuoteTriggerProps = {
|
|
19
|
+
quoteRef: React.MutableRefObject<HTMLButtonElement | null>;
|
|
20
|
+
recommended: boolean;
|
|
21
|
+
setExpanded: React.Dispatch<React.SetStateAction<boolean | undefined>>;
|
|
22
|
+
steps: Step[];
|
|
23
|
+
expanded?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type QuoteTriggerImagesProps = {
|
|
27
|
+
content: ReactNode;
|
|
28
|
+
state?: 'error' | 'warning' | undefined;
|
|
29
|
+
src: string;
|
|
30
|
+
open?: boolean;
|
|
31
|
+
className?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type QuoteCostDetailsProps = {
|
|
35
|
+
quote: BestRouteResponse;
|
|
36
|
+
steps: number;
|
|
37
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { css, styled } from '@rango-dev/ui';
|
|
2
|
+
|
|
3
|
+
export const FeeSection = styled('div', {
|
|
4
|
+
display: 'flex',
|
|
5
|
+
justifyContent: 'space-between',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
padding: '$5 0',
|
|
8
|
+
'&.total_payable_fee': {
|
|
9
|
+
padding: '$12 0',
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const Line = styled('div', {
|
|
14
|
+
width: '100%',
|
|
15
|
+
borderTop: '1px solid $neutral300',
|
|
16
|
+
margin: '$10 0',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const ModalContainer = styled('div', {
|
|
20
|
+
padding: '$20 0',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
flexDirection: 'column',
|
|
23
|
+
'& .collapsible_trigger': {
|
|
24
|
+
display: 'flex',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const ModalHeader = styled('div', {
|
|
30
|
+
padding: '$20 $20 $10',
|
|
31
|
+
textAlign: 'center',
|
|
32
|
+
position: 'relative',
|
|
33
|
+
'._icon-button': {
|
|
34
|
+
position: 'absolute',
|
|
35
|
+
zIndex: 10,
|
|
36
|
+
top: '$16',
|
|
37
|
+
right: '$16',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
export const trigger = css({
|
|
41
|
+
display: 'flex',
|
|
42
|
+
flexDirection: 'row',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
padding: '$5',
|
|
45
|
+
'&:hover': {
|
|
46
|
+
'& ._typography, & svg': {
|
|
47
|
+
color: '$secondary',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { QuoteCostDetailsProps } from './Quote.types';
|
|
2
|
+
import type { NameOfFees } from '../../constants/quote';
|
|
3
|
+
|
|
4
|
+
import { i18n } from '@lingui/core';
|
|
5
|
+
import {
|
|
6
|
+
ChevronDownIcon,
|
|
7
|
+
CloseIcon,
|
|
8
|
+
Divider,
|
|
9
|
+
IconButton,
|
|
10
|
+
Modal,
|
|
11
|
+
QuoteCost,
|
|
12
|
+
Tooltip,
|
|
13
|
+
Typography,
|
|
14
|
+
} from '@rango-dev/ui';
|
|
15
|
+
import BigNumber from 'bignumber.js';
|
|
16
|
+
import React, { useState } from 'react';
|
|
17
|
+
|
|
18
|
+
import { NAME_OF_FEES } from '../../constants/quote';
|
|
19
|
+
import {
|
|
20
|
+
GAS_FEE_MAX_DECIMALS,
|
|
21
|
+
GAS_FEE_MIN_DECIMALS,
|
|
22
|
+
USD_VALUE_MAX_DECIMALS,
|
|
23
|
+
USD_VALUE_MIN_DECIMALS,
|
|
24
|
+
} from '../../constants/routing';
|
|
25
|
+
import { useAppStore } from '../../store/AppStore';
|
|
26
|
+
import { getContainer } from '../../utils/common';
|
|
27
|
+
import {
|
|
28
|
+
formatTooltipNumbers,
|
|
29
|
+
numberToString,
|
|
30
|
+
secondsToString,
|
|
31
|
+
totalArrivalTime,
|
|
32
|
+
} from '../../utils/numbers';
|
|
33
|
+
import {
|
|
34
|
+
getFeesGroup,
|
|
35
|
+
getTotalFeeInUsd,
|
|
36
|
+
getTotalFeesInUsd,
|
|
37
|
+
getUsdFee,
|
|
38
|
+
} from '../../utils/swap';
|
|
39
|
+
import { CustomCollapsible } from '../CustomCollapsible/CustomCollapsible';
|
|
40
|
+
import { ExpandedIcon } from '../CustomCollapsible/CustomCollapsible.styles';
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
FeeSection,
|
|
44
|
+
Line,
|
|
45
|
+
ModalContainer,
|
|
46
|
+
ModalHeader,
|
|
47
|
+
trigger,
|
|
48
|
+
} from './QuoteCostDetails.styles';
|
|
49
|
+
|
|
50
|
+
const GAS_FEE_MAX = 30;
|
|
51
|
+
|
|
52
|
+
const NonPayableFee = (props: { fee: BigNumber; label: string }) => {
|
|
53
|
+
return !props.fee.isZero() ? (
|
|
54
|
+
<FeeSection>
|
|
55
|
+
<Typography variant="label" size="medium" color="neutral600">
|
|
56
|
+
{props.label}
|
|
57
|
+
</Typography>
|
|
58
|
+
<Typography variant="label" size="medium">
|
|
59
|
+
$
|
|
60
|
+
{numberToString(
|
|
61
|
+
props.fee,
|
|
62
|
+
USD_VALUE_MIN_DECIMALS,
|
|
63
|
+
USD_VALUE_MAX_DECIMALS
|
|
64
|
+
)}
|
|
65
|
+
</Typography>
|
|
66
|
+
</FeeSection>
|
|
67
|
+
) : null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function QuoteCostDetails(props: QuoteCostDetailsProps) {
|
|
71
|
+
const [open, setOpen] = useState<boolean>(false);
|
|
72
|
+
const [openCollapse, setOpenCollapse] = useState<boolean>(false);
|
|
73
|
+
const { steps, quote } = props;
|
|
74
|
+
const tokens = useAppStore().tokens();
|
|
75
|
+
const totalTime = secondsToString(totalArrivalTime(quote.result?.swaps));
|
|
76
|
+
const swaps = quote.result?.swaps ?? [];
|
|
77
|
+
const container = getContainer();
|
|
78
|
+
|
|
79
|
+
const totalFee = getTotalFeeInUsd(swaps, tokens);
|
|
80
|
+
|
|
81
|
+
const feesGroup = getFeesGroup(swaps);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<>
|
|
85
|
+
<QuoteCost
|
|
86
|
+
onClickFee={() => setOpen(!open)}
|
|
87
|
+
fee={numberToString(
|
|
88
|
+
totalFee,
|
|
89
|
+
GAS_FEE_MIN_DECIMALS,
|
|
90
|
+
GAS_FEE_MAX_DECIMALS
|
|
91
|
+
)}
|
|
92
|
+
feeWarning={totalFee.gte(new BigNumber(GAS_FEE_MAX))}
|
|
93
|
+
time={totalTime}
|
|
94
|
+
steps={steps}
|
|
95
|
+
tooltipGas={i18n.t('View more info')}
|
|
96
|
+
tooltipContainer={container}
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
<Modal
|
|
100
|
+
container={container}
|
|
101
|
+
open={open}
|
|
102
|
+
header={
|
|
103
|
+
<ModalHeader>
|
|
104
|
+
<Typography variant="title" size="medium">
|
|
105
|
+
{i18n.t('Gas & Fee Explanation')}
|
|
106
|
+
</Typography>
|
|
107
|
+
|
|
108
|
+
<IconButton onClick={() => setOpen(false)} variant="ghost">
|
|
109
|
+
<CloseIcon color="gray" size={14} />
|
|
110
|
+
</IconButton>
|
|
111
|
+
</ModalHeader>
|
|
112
|
+
}
|
|
113
|
+
onClose={() => setOpen(false)}>
|
|
114
|
+
<ModalContainer>
|
|
115
|
+
<Typography variant="title" size="small">
|
|
116
|
+
{i18n.t('Details')}
|
|
117
|
+
</Typography>
|
|
118
|
+
<Divider size={10} />
|
|
119
|
+
{Object.entries(feesGroup.payable).flatMap(
|
|
120
|
+
([payableFeesKey, feeArray]) =>
|
|
121
|
+
feeArray.map((fee, index) => {
|
|
122
|
+
const key = `payable-fee-${index}`;
|
|
123
|
+
const usdValue = getUsdFee(fee);
|
|
124
|
+
return (
|
|
125
|
+
<FeeSection key={key}>
|
|
126
|
+
<Typography
|
|
127
|
+
variant="label"
|
|
128
|
+
size="medium"
|
|
129
|
+
color="neutral600">
|
|
130
|
+
{NAME_OF_FEES[payableFeesKey as NameOfFees]}
|
|
131
|
+
</Typography>
|
|
132
|
+
<Tooltip
|
|
133
|
+
content={formatTooltipNumbers(fee.amount)}
|
|
134
|
+
container={container}>
|
|
135
|
+
<Typography variant="label" size="medium">
|
|
136
|
+
{numberToString(
|
|
137
|
+
fee.amount,
|
|
138
|
+
GAS_FEE_MIN_DECIMALS,
|
|
139
|
+
GAS_FEE_MAX_DECIMALS
|
|
140
|
+
)}{' '}
|
|
141
|
+
{fee.asset.symbol} ($
|
|
142
|
+
{numberToString(
|
|
143
|
+
usdValue,
|
|
144
|
+
USD_VALUE_MIN_DECIMALS,
|
|
145
|
+
USD_VALUE_MAX_DECIMALS
|
|
146
|
+
)}
|
|
147
|
+
)
|
|
148
|
+
</Typography>
|
|
149
|
+
</Tooltip>
|
|
150
|
+
</FeeSection>
|
|
151
|
+
);
|
|
152
|
+
})
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
<FeeSection className="total_payable_fee">
|
|
156
|
+
<Typography variant="label" size="medium">
|
|
157
|
+
{i18n.t('Total Payable Fee')}
|
|
158
|
+
</Typography>
|
|
159
|
+
<Typography variant="label" size="medium">
|
|
160
|
+
$
|
|
161
|
+
{numberToString(
|
|
162
|
+
totalFee,
|
|
163
|
+
USD_VALUE_MIN_DECIMALS,
|
|
164
|
+
USD_VALUE_MAX_DECIMALS
|
|
165
|
+
)}
|
|
166
|
+
</Typography>
|
|
167
|
+
</FeeSection>
|
|
168
|
+
<Line />
|
|
169
|
+
{Object.keys(feesGroup.nonePayable).length && (
|
|
170
|
+
<CustomCollapsible
|
|
171
|
+
triggerAnchor="bottom"
|
|
172
|
+
onClickTrigger={() => setOpenCollapse((prev) => !prev)}
|
|
173
|
+
trigger={
|
|
174
|
+
<div className={trigger()}>
|
|
175
|
+
<Typography size="small" variant="body" color="neutral700">
|
|
176
|
+
{openCollapse
|
|
177
|
+
? i18n.t('Hide non-payable fees')
|
|
178
|
+
: i18n.t('Show non-payable fees')}
|
|
179
|
+
</Typography>
|
|
180
|
+
<Divider size={4} direction="horizontal" />
|
|
181
|
+
<ExpandedIcon orientation={openCollapse ? 'up' : 'down'}>
|
|
182
|
+
<ChevronDownIcon size={12} color="gray" />
|
|
183
|
+
</ExpandedIcon>
|
|
184
|
+
</div>
|
|
185
|
+
}
|
|
186
|
+
open={openCollapse}>
|
|
187
|
+
<Typography size="small" variant="title">
|
|
188
|
+
{i18n.t('Description')}
|
|
189
|
+
</Typography>
|
|
190
|
+
<Divider size={4} />
|
|
191
|
+
<Typography size="small" variant="body" color="neutral700">
|
|
192
|
+
{i18n.t(`The following fees are considered in the transaction output and
|
|
193
|
+
you won’t need to pay extra gas for them.`)}
|
|
194
|
+
</Typography>
|
|
195
|
+
<Divider size={10} />
|
|
196
|
+
{Object.entries(feesGroup.nonePayable).map(
|
|
197
|
+
([nonPayableFeesKey, fees], index) => {
|
|
198
|
+
const totalFeeInUsd = getTotalFeesInUsd(fees);
|
|
199
|
+
const label = NAME_OF_FEES[nonPayableFeesKey as NameOfFees];
|
|
200
|
+
const key = `non-payable-fee-${index}`;
|
|
201
|
+
return (
|
|
202
|
+
<NonPayableFee
|
|
203
|
+
key={key}
|
|
204
|
+
fee={totalFeeInUsd}
|
|
205
|
+
label={label}
|
|
206
|
+
/>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
<Line />
|
|
212
|
+
</CustomCollapsible>
|
|
213
|
+
)}
|
|
214
|
+
</ModalContainer>
|
|
215
|
+
</Modal>
|
|
216
|
+
</>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type { QuoteTriggerImagesProps, QuoteTriggerProps } from './Quote.types';
|
|
2
|
+
|
|
3
|
+
import { i18n } from '@lingui/core';
|
|
4
|
+
import {
|
|
5
|
+
ChevronDownIcon,
|
|
6
|
+
ChevronRightIcon,
|
|
7
|
+
Divider,
|
|
8
|
+
Image,
|
|
9
|
+
Tooltip,
|
|
10
|
+
Typography,
|
|
11
|
+
} from '@rango-dev/ui';
|
|
12
|
+
import React, { useEffect, useState } from 'react';
|
|
13
|
+
|
|
14
|
+
import { getContainer } from '../../utils/common';
|
|
15
|
+
import { getUniqueBlockchains } from '../../utils/quote';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
IconContainer,
|
|
19
|
+
ImageContainer,
|
|
20
|
+
MoreStep,
|
|
21
|
+
rowStyles,
|
|
22
|
+
Trigger,
|
|
23
|
+
} from './Quote.styles';
|
|
24
|
+
|
|
25
|
+
const MAX_STEPS = 4;
|
|
26
|
+
const MAX_BLOCKCHAINS = 6;
|
|
27
|
+
const MIN_WIDTH_WINDOW = 375;
|
|
28
|
+
|
|
29
|
+
const ImageComponent = (props: QuoteTriggerImagesProps) => {
|
|
30
|
+
const tooltipContainer = getContainer();
|
|
31
|
+
const { content, src, className, open, state } = props;
|
|
32
|
+
return (
|
|
33
|
+
<Tooltip
|
|
34
|
+
container={tooltipContainer}
|
|
35
|
+
side="bottom"
|
|
36
|
+
sideOffset={4}
|
|
37
|
+
open={open}
|
|
38
|
+
content={content}>
|
|
39
|
+
<ImageContainer className={className} state={state}>
|
|
40
|
+
<Image src={src} size={16} />
|
|
41
|
+
</ImageContainer>
|
|
42
|
+
</Tooltip>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function QuoteTrigger(props: QuoteTriggerProps) {
|
|
47
|
+
const { quoteRef, recommended, setExpanded, steps, expanded } = props;
|
|
48
|
+
const tooltipContainer = getContainer();
|
|
49
|
+
const numberOfSteps = steps.length;
|
|
50
|
+
const blockchains = getUniqueBlockchains(steps);
|
|
51
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
52
|
+
|
|
53
|
+
//choose the screen size
|
|
54
|
+
const handleResize = () => {
|
|
55
|
+
if (window.innerWidth < MIN_WIDTH_WINDOW) {
|
|
56
|
+
setIsMobile(true);
|
|
57
|
+
} else {
|
|
58
|
+
setIsMobile(false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
window.addEventListener('resize', handleResize);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Trigger
|
|
68
|
+
ref={(ref) => (quoteRef.current = ref)}
|
|
69
|
+
recommended={recommended}
|
|
70
|
+
onClick={() => setExpanded((prevState) => !prevState)}>
|
|
71
|
+
<div className={rowStyles()}>
|
|
72
|
+
<Typography variant="body" size="xsmall">
|
|
73
|
+
{i18n.t('Via:')}
|
|
74
|
+
</Typography>
|
|
75
|
+
<Divider direction="horizontal" size={4} />
|
|
76
|
+
{steps.map((step, index) => {
|
|
77
|
+
const key = `item-${index}`;
|
|
78
|
+
const arrow = (
|
|
79
|
+
<IconContainer>
|
|
80
|
+
<ChevronRightIcon size={12} color="black" />
|
|
81
|
+
</IconContainer>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return isMobile ? (
|
|
85
|
+
<>
|
|
86
|
+
<ImageComponent
|
|
87
|
+
content={step.swapper.displayName}
|
|
88
|
+
src={step.swapper.image}
|
|
89
|
+
state={step.state}
|
|
90
|
+
/>
|
|
91
|
+
{index !== numberOfSteps - 1 && <>{arrow}</>}
|
|
92
|
+
</>
|
|
93
|
+
) : (
|
|
94
|
+
<React.Fragment key={key}>
|
|
95
|
+
{numberOfSteps <= MAX_STEPS ||
|
|
96
|
+
(numberOfSteps > MAX_STEPS && index < MAX_STEPS - 1) ? (
|
|
97
|
+
<>
|
|
98
|
+
<ImageComponent
|
|
99
|
+
content={step.swapper.displayName}
|
|
100
|
+
src={step.swapper.image}
|
|
101
|
+
state={step.state}
|
|
102
|
+
/>
|
|
103
|
+
{index !== numberOfSteps - 1 && <>{arrow}</>}
|
|
104
|
+
</>
|
|
105
|
+
) : (
|
|
106
|
+
index === MAX_STEPS - 1 && (
|
|
107
|
+
<Tooltip
|
|
108
|
+
container={tooltipContainer}
|
|
109
|
+
side="bottom"
|
|
110
|
+
align="right"
|
|
111
|
+
sideOffset={4}
|
|
112
|
+
content={
|
|
113
|
+
<div className={rowStyles()}>
|
|
114
|
+
{arrow}
|
|
115
|
+
{steps.map((step, i) => {
|
|
116
|
+
const key = `image-${i}`;
|
|
117
|
+
return (
|
|
118
|
+
i >= index && (
|
|
119
|
+
<React.Fragment key={key}>
|
|
120
|
+
<ImageComponent
|
|
121
|
+
content={step.swapper.displayName}
|
|
122
|
+
src={step.swapper.image}
|
|
123
|
+
state={step.state}
|
|
124
|
+
open={false}
|
|
125
|
+
/>
|
|
126
|
+
{i !== numberOfSteps - 1 && <>{arrow}</>}
|
|
127
|
+
</React.Fragment>
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
})}
|
|
131
|
+
</div>
|
|
132
|
+
}>
|
|
133
|
+
<MoreStep
|
|
134
|
+
state={
|
|
135
|
+
steps.find(
|
|
136
|
+
(step, i) =>
|
|
137
|
+
i >= index &&
|
|
138
|
+
(step.state === 'error' || step.state === 'warning')
|
|
139
|
+
)?.state
|
|
140
|
+
}>
|
|
141
|
+
<Typography size="xsmall" variant="body">
|
|
142
|
+
+{numberOfSteps - index}
|
|
143
|
+
</Typography>
|
|
144
|
+
</MoreStep>
|
|
145
|
+
</Tooltip>
|
|
146
|
+
)
|
|
147
|
+
)}
|
|
148
|
+
</React.Fragment>
|
|
149
|
+
);
|
|
150
|
+
})}
|
|
151
|
+
</div>
|
|
152
|
+
<div className={rowStyles()}>
|
|
153
|
+
<div className="blockchains_section">
|
|
154
|
+
<div className={rowStyles()}>
|
|
155
|
+
<Typography variant="body" size="xsmall">
|
|
156
|
+
{i18n.t('Chains:')}
|
|
157
|
+
</Typography>
|
|
158
|
+
<Divider direction="horizontal" size={4} />
|
|
159
|
+
{blockchains.map((blockchain, index) => (
|
|
160
|
+
<>
|
|
161
|
+
{blockchains.length <= MAX_BLOCKCHAINS ||
|
|
162
|
+
(blockchains.length > MAX_BLOCKCHAINS &&
|
|
163
|
+
index < MAX_BLOCKCHAINS - 1) ? (
|
|
164
|
+
<ImageComponent
|
|
165
|
+
key={blockchain.displayName}
|
|
166
|
+
content={''}
|
|
167
|
+
src={blockchain.image}
|
|
168
|
+
open={false}
|
|
169
|
+
className={index !== 0 ? 'blockchainImage' : ''}
|
|
170
|
+
/>
|
|
171
|
+
) : (
|
|
172
|
+
index === MAX_BLOCKCHAINS - 1 && (
|
|
173
|
+
<Tooltip
|
|
174
|
+
container={tooltipContainer}
|
|
175
|
+
side="bottom"
|
|
176
|
+
align="right"
|
|
177
|
+
sideOffset={4}
|
|
178
|
+
content={
|
|
179
|
+
<div className={rowStyles()}>
|
|
180
|
+
{blockchains.map(
|
|
181
|
+
(chain, i) =>
|
|
182
|
+
i >= index && (
|
|
183
|
+
<ImageComponent
|
|
184
|
+
key={chain.displayName}
|
|
185
|
+
content={''}
|
|
186
|
+
src={chain.image}
|
|
187
|
+
open={false}
|
|
188
|
+
className={i > index ? 'blockchainImage' : ''}
|
|
189
|
+
/>
|
|
190
|
+
)
|
|
191
|
+
)}
|
|
192
|
+
</div>
|
|
193
|
+
}>
|
|
194
|
+
<MoreStep className={'blockchainImage'}>
|
|
195
|
+
<Typography size="xsmall" variant="body">
|
|
196
|
+
+{blockchains.length - index}
|
|
197
|
+
</Typography>
|
|
198
|
+
</MoreStep>
|
|
199
|
+
</Tooltip>
|
|
200
|
+
)
|
|
201
|
+
)}
|
|
202
|
+
</>
|
|
203
|
+
))}
|
|
204
|
+
|
|
205
|
+
<Divider direction="horizontal" size={32} />
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<IconContainer orientation={expanded ? 'up' : 'down'}>
|
|
210
|
+
<ChevronDownIcon size={12} color="black" />
|
|
211
|
+
</IconContainer>
|
|
212
|
+
</div>
|
|
213
|
+
</Trigger>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SwapFee } from 'rango-sdk';
|
|
2
|
+
|
|
3
|
+
import { i18n } from '@lingui/core';
|
|
4
|
+
|
|
5
|
+
export type NameOfFees =
|
|
6
|
+
| 'Swapper Fee'
|
|
7
|
+
| 'Affiliate Fee'
|
|
8
|
+
| 'Outbound network fee'
|
|
9
|
+
| 'Rango Fee'
|
|
10
|
+
| 'Network Fee';
|
|
11
|
+
|
|
12
|
+
export const NAME_OF_FEES: Record<NameOfFees, string> = {
|
|
13
|
+
'Network Fee': i18n.t('Network Fee'),
|
|
14
|
+
'Swapper Fee': i18n.t('Protocol Fee'),
|
|
15
|
+
'Affiliate Fee': i18n.t('Affiliate Fee'),
|
|
16
|
+
'Outbound network fee': i18n.t('Outbound Fee'),
|
|
17
|
+
'Rango Fee': i18n.t('Rango Fee'),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type FeesGroup = {
|
|
21
|
+
payable: { [key in NameOfFees]?: SwapFee[] };
|
|
22
|
+
nonePayable: { [key in NameOfFees]?: SwapFee[] };
|
|
23
|
+
};
|