Skip to content

Commit 08be812

Browse files
author
VeTrade
committed
In-App-Swap with different aggregators
1 parent 6166473 commit 08be812

33 files changed

Lines changed: 2282 additions & 37 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
3+
export const BetterSwapLogo = ({ boxSize = '24px', ...props }: { boxSize?: string; [key: string]: any }) => {
4+
return (
5+
<svg
6+
width={boxSize}
7+
height={boxSize}
8+
viewBox="0 0 32 32"
9+
fill="none"
10+
xmlns="http://www.w3.org/2000/svg"
11+
style={{ borderRadius: '4px' }}
12+
{...props}
13+
>
14+
<rect width="32" height="32" rx="4" fill="#000000" />
15+
<path
16+
d="M27.1113 18.2502C26.8649 17.9295 26.5926 17.6427 26.2949 17.3892L21.5105 21.0938C21.4697 22.4315 20.631 23.1005 18.9942 23.1005H18.9189L11.9738 28.4782H20.3213C22.8626 28.4782 24.8367 27.8996 26.2434 26.7424C27.6501 25.5852 28.3538 23.9857 28.3538 21.9433C28.3538 20.5592 27.9394 19.3282 27.1113 18.2502Z"
17+
fill="#FFFFFF"
18+
/>
19+
<path
20+
d="M27.6991 10.836C27.6991 8.86222 27.0011 6.27989 25.6059 5.19083C24.2103 4.10176 22.2874 3.55681 19.8368 3.55682H8.33262V17.6668L11.3195 15.3505L15.0037 12.4748V8.86674H18.3731C18.8086 8.86674 19.7341 8.83209 19.9589 9.05695C20.7337 9.83166 20.4264 11.0472 19.9023 11.5303C19.8049 11.6201 19.6982 11.7195 19.5814 11.8295C19.4991 11.9069 19.4125 11.9798 19.3222 12.0477L15.0037 15.2932L10.2059 18.965L7.15909 21.4083L6.23537 19.8432L1.40462 28.4729H11.3287L10.4157 26.926L26.0913 14.7484C26.5695 14.3769 26.9562 13.8981 27.2046 13.3458C27.5342 12.6133 27.6991 11.7766 27.6991 10.836Z"
21+
fill="#FFFFFF"
22+
/>
23+
</svg>
24+
);
25+
};
26+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { BetterSwapLogo } from './BetterSwapLogo';
2+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React, { useId } from 'react';
2+
3+
export const VeTradeLogo = ({ boxSize = '24px', ...props }: { boxSize?: string; [key: string]: any }) => {
4+
const id = useId();
5+
const gradient1Id = `vetrade-gradient-1-${id}`;
6+
const gradient2Id = `vetrade-gradient-2-${id}`;
7+
8+
return (
9+
<svg
10+
width={boxSize}
11+
height={boxSize}
12+
viewBox="0 0 512 512"
13+
fill="none"
14+
xmlns="http://www.w3.org/2000/svg"
15+
{...props}
16+
>
17+
<defs>
18+
<linearGradient id={gradient1Id} x1="0%" y1="0%" x2="100%" y2="0%">
19+
<stop offset="0%" stopColor="#ec4899" />
20+
<stop offset="100%" stopColor="#9333ea" />
21+
</linearGradient>
22+
<linearGradient id={gradient2Id} x1="0%" y1="0%" x2="100%" y2="0%">
23+
<stop offset="0%" stopColor="#9333ea" />
24+
<stop offset="100%" stopColor="#ec4899" />
25+
</linearGradient>
26+
</defs>
27+
<g transform="rotate(-30 256 256)">
28+
<path
29+
d="M440.712 282.966c-32.398-54.779-92.034-91.509-160.268-91.509v32.705c0 11.693-13.789 17.925-22.566 10.197L156.16 144.796c-6.145-5.411-6.144-14.988.003-20.397l101.718-89.512c8.777-7.724 22.563-1.492 22.563 10.2v32.716c110.627 0 193.535 94.769 185.668 199.701C465.114 290.808 447.503 294.449 440.712 282.966z"
30+
fill={`url(#${gradient1Id})`}
31+
/>
32+
<path
33+
d="M71.285 229.034c32.398 54.779 92.034 91.509 160.268 91.509v-32.705c0-11.693 13.789-17.925 22.566-10.197l101.718 89.563c6.145 5.411 6.144 14.988-.003 20.397l-101.718 89.512c-8.777 7.724-22.563 1.492-22.563-10.2l0-32.716c-110.627 0-193.535-94.768-185.668-199.701C46.882 221.192 64.493 217.551 71.285 229.034z"
34+
fill={`url(#${gradient2Id})`}
35+
/>
36+
</g>
37+
<rect width="512" height="512" rx="8" fill="none" />
38+
</svg>
39+
);
40+
};
41+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { VeTradeLogo } from './VeTradeLogo';
2+

packages/vechain-kit/src/assets/icons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ export * from './TwitterLogo';
44
export * from './VechainLogo';
55
export * from './PrivyLogo';
66
export * from './VechainEnergy';
7+
export * from './BetterSwapLogo';
8+
export * from './VeTradeLogo';

packages/vechain-kit/src/components/AccountModal/AccountModal.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { AppOverviewContent } from './Contents/Ecosystem/AppOverviewContent';
3434
import { DisconnectConfirmContent } from './Contents/Account/DisconnectConfirmContent';
3535
import { CustomizationContent, CustomizationSummaryContent } from './Contents';
3636
import { SuccessfulOperationContent } from './Contents/SuccessfulOperation/SuccessfulOperationContent';
37+
import { FailedOperationContent } from './Contents/FailedOperation/FailedOperationContent';
3738
import { ManageCustomTokenContent } from './Contents/Assets/ManageCustomTokenContent';
3839
import { UpgradeSmartAccountContent } from './Contents/UpgradeSmartAccount';
3940
import { useModal } from '@/providers/ModalProvider';
@@ -108,6 +109,10 @@ export const AccountModal = ({
108109
return (
109110
<SuccessfulOperationContent {...currentContent.props} />
110111
);
112+
case 'failed-operation':
113+
return (
114+
<FailedOperationContent {...currentContent.props} />
115+
);
111116
case 'upgrade-smart-account':
112117
return (
113118
<UpgradeSmartAccountContent {...currentContent.props} />
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import {
2+
ModalBody,
3+
ModalCloseButton,
4+
ModalHeader,
5+
VStack,
6+
Text,
7+
Button,
8+
Box,
9+
ModalFooter,
10+
Icon,
11+
Link,
12+
HStack,
13+
} from '@chakra-ui/react';
14+
import { StickyHeaderContainer } from '@/components/common';
15+
import { AccountModalContentTypes } from '../../Types';
16+
import { useTranslation } from 'react-i18next';
17+
import { useVeChainKitConfig } from '@/providers';
18+
import { getConfig } from '@/config';
19+
import { GoLinkExternal } from 'react-icons/go';
20+
import { MdOutlineErrorOutline } from 'react-icons/md';
21+
22+
export type FailedOperationContentProps = {
23+
setCurrentContent: React.Dispatch<
24+
React.SetStateAction<AccountModalContentTypes>
25+
>;
26+
txId?: string;
27+
title: string;
28+
description?: string;
29+
onDone: () => void;
30+
};
31+
32+
export const FailedOperationContent = ({
33+
txId,
34+
title,
35+
description,
36+
onDone,
37+
}: FailedOperationContentProps) => {
38+
const { t } = useTranslation();
39+
const { network, darkMode } = useVeChainKitConfig();
40+
const explorerUrl = getConfig(network.type).explorerUrl;
41+
42+
return (
43+
<Box>
44+
<StickyHeaderContainer>
45+
<ModalHeader>{title}</ModalHeader>
46+
<ModalCloseButton />
47+
</StickyHeaderContainer>
48+
49+
<ModalBody>
50+
<VStack align={'center'} p={6} spacing={3}>
51+
<Icon
52+
as={MdOutlineErrorOutline}
53+
fontSize={'100px'}
54+
color={darkMode ? 'red.400' : 'red.500'}
55+
data-testid="error-icon"
56+
/>
57+
58+
{description && (
59+
<Text fontSize="sm" textAlign="center">
60+
{description}
61+
</Text>
62+
)}
63+
</VStack>
64+
</ModalBody>
65+
66+
<ModalFooter justifyContent={'center'}>
67+
<VStack width="full" spacing={4}>
68+
<Button
69+
onClick={onDone}
70+
variant="vechainKitSecondary"
71+
width="full"
72+
>
73+
{t('Done')}
74+
</Button>
75+
76+
{txId && (
77+
<Link
78+
href={`${explorerUrl}/${txId}`}
79+
isExternal
80+
opacity={0.5}
81+
fontSize={'14px'}
82+
textDecoration={'underline'}
83+
>
84+
<HStack
85+
spacing={1}
86+
alignItems={'center'}
87+
w={'full'}
88+
justifyContent={'center'}
89+
>
90+
<Text>
91+
{t('View transaction on the explorer')}
92+
</Text>
93+
<Icon size={16} as={GoLinkExternal} />
94+
</HStack>
95+
</Link>
96+
)}
97+
</VStack>
98+
</ModalFooter>
99+
</Box>
100+
);
101+
};
102+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './FailedOperationContent';
2+

packages/vechain-kit/src/components/AccountModal/Contents/Receive/ReceiveTokenContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const ReceiveTokenContent = ({ setCurrentContent }: Props) => {
3737

3838
<Container maxW={'container.lg'}>
3939
<ModalBody>
40-
<VStack spacing={6} align="center" w="full">
40+
<VStack spacing={0} align="center" w="full">
4141
<QRCode
4242
value={account?.address ?? ''}
4343
size={200}

packages/vechain-kit/src/components/AccountModal/Contents/SendToken/SelectTokenContent.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { FiSlash } from 'react-icons/fi';
1616
import { ModalBackButton, StickyHeaderContainer } from '@/components/common';
1717
import { AccountModalContentTypes, AssetButton } from '@/components';
1818
import { useWallet, useTokensWithValues, TokenWithValue } from '@/hooks';
19-
import { useState, useEffect } from 'react';
19+
import { useState, useEffect, useMemo } from 'react';
2020
import { useTranslation } from 'react-i18next';
2121
import { useVeChainKitConfig } from '@/providers';
2222
import { Analytics } from '@/utils/mixpanelClientInstance';
@@ -29,20 +29,42 @@ type Props = {
2929
>;
3030
onSelectToken: (token: TokenWithValue) => void;
3131
onBack: () => void;
32+
/**
33+
* If true, shows all tokens (not just tokens with balance) and sorts owned tokens first
34+
*/
35+
showAllTokens?: boolean;
3236
};
3337

34-
export const SelectTokenContent = ({ onSelectToken, onBack }: Props) => {
38+
export const SelectTokenContent = ({ onSelectToken, onBack, showAllTokens = false }: Props) => {
3539
const { t } = useTranslation();
3640
const { darkMode: isDark } = useVeChainKitConfig();
3741
const { currentCurrency } = useCurrency();
3842
const { account } = useWallet();
39-
const { tokensWithBalance } = useTokensWithValues({
43+
const { tokensWithBalance, sortedTokens } = useTokensWithValues({
4044
address: account?.address ?? '',
4145
});
4246
const [searchQuery, setSearchQuery] = useState('');
4347

48+
// Get the appropriate token list based on showAllTokens prop
49+
const availableTokens = useMemo(() => {
50+
if (showAllTokens) {
51+
// Show all tokens, sorted with owned tokens first (by value), then unowned
52+
const ownedTokens = sortedTokens.filter((token) => Number(token.balance) > 0);
53+
const unownedTokens = sortedTokens.filter((token) => Number(token.balance) === 0);
54+
55+
// Owned tokens are already sorted by value (highest first)
56+
// Unowned tokens are sorted alphabetically
57+
const sortedUnowned = [...unownedTokens].sort((a, b) =>
58+
a.symbol.localeCompare(b.symbol)
59+
);
60+
61+
return [...ownedTokens, ...sortedUnowned];
62+
}
63+
return tokensWithBalance;
64+
}, [showAllTokens, sortedTokens, tokensWithBalance]);
65+
4466
// Filter tokens
45-
const filteredTokens = tokensWithBalance.filter(({ symbol }) =>
67+
const filteredTokens = availableTokens.filter(({ symbol }) =>
4668
symbol.toLowerCase().includes(searchQuery.toLowerCase()),
4769
);
4870

@@ -89,7 +111,7 @@ export const SelectTokenContent = ({ onSelectToken, onBack }: Props) => {
89111
color={isDark ? 'whiteAlpha.800' : 'gray.700'}
90112
mt={4}
91113
>
92-
{t('Your tokens')}
114+
{showAllTokens ? t('All tokens') : t('Your tokens')}
93115
</Text>
94116

95117
{filteredTokens.length === 0 ? (

0 commit comments

Comments
 (0)