@aurora-is-near/intents-swap-widget 3.17.1 → 3.17.3
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/{config-CQ4V08as.js → config-VpsP2eLV.js} +959 -880
- package/dist/config-VpsP2eLV.js.map +1 -0
- package/dist/config.js +2 -2
- package/dist/errors.js +1 -1
- package/dist/ext/alchemy/index.js +1 -1
- package/dist/ext/index.js +1 -1
- package/dist/features/BalanceRpcLoader/TokenBalanceLoader.js +1 -1
- package/dist/features/BalanceRpcLoader/index.js +1 -1
- package/dist/features/BalanceRpcLoader/useTokenBalanceRpc.js +1 -1
- package/dist/features/ChainsDropdown/index.js +1 -1
- package/dist/features/DepositMethodSwitcher.js +1 -1
- package/dist/features/ErrorBoundary.js +1 -1
- package/dist/features/ExternalDeposit.js +2 -2
- package/dist/features/SendAddress/index.js +1 -1
- package/dist/features/SendAddress/useNotification.js +1 -1
- package/dist/features/SubmitButton/index.js +1 -1
- package/dist/features/SuccessScreen/index.js +1 -1
- package/dist/features/SwapDirectionSwitcher.js +1 -1
- package/dist/features/SwapQuote/SwapQuote.js +1 -1
- package/dist/features/SwapQuote/index.js +1 -1
- package/dist/features/TokenInput/TokenInput.js +1 -1
- package/dist/features/TokenInput/TokenInputEmpty.js +1 -1
- package/dist/features/TokenInput/TokenInputSource.js +1 -1
- package/dist/features/TokenInput/TokenInputTarget.js +1 -1
- package/dist/features/TokenInput/WalletBalance.js +1 -1
- package/dist/features/TokenInput/hooks/index.js +1 -1
- package/dist/features/TokenInput/hooks/useTokenInputBalance.js +1 -1
- package/dist/features/TokenInput/index.js +1 -1
- package/dist/features/TokensList/TokenItem.d.ts +2 -2
- package/dist/features/TokensList/TokenItem.js +1 -2
- package/dist/features/TokensList/TokensList.js +10 -2
- package/dist/features/TokensList/TokensList.js.map +1 -1
- package/dist/features/TokensList/constants.d.ts +4 -0
- package/dist/features/TokensList/constants.js +8 -0
- package/dist/features/TokensList/constants.js.map +1 -0
- package/dist/features/TokensList/hooks/index.d.ts +1 -0
- package/dist/features/TokensList/hooks/index.js +5 -0
- package/dist/features/TokensList/hooks/index.js.map +1 -0
- package/dist/features/TokensList/hooks/useFocusOnList.d.ts +9 -0
- package/dist/features/TokensList/hooks/useFocusOnList.js +33 -0
- package/dist/features/TokensList/hooks/useFocusOnList.js.map +1 -0
- package/dist/features/TokensList/index.d.ts +2 -1
- package/dist/features/TokensList/index.js +1 -1
- package/dist/features/TokensList/types.d.ts +14 -0
- package/dist/features/TokensList/types.js +2 -0
- package/dist/features/TokensList/types.js.map +1 -0
- package/dist/features/TokensList/utils/getFirstGroupItemTotalIndex.d.ts +9 -0
- package/dist/features/TokensList/utils/getFirstGroupItemTotalIndex.js +8 -0
- package/dist/features/TokensList/utils/getFirstGroupItemTotalIndex.js.map +1 -0
- package/dist/features/TokensList/utils/getGroupHeadersTotalIndexes.d.ts +8 -0
- package/dist/features/TokensList/utils/getGroupHeadersTotalIndexes.js +5 -0
- package/dist/features/TokensList/utils/getGroupHeadersTotalIndexes.js.map +1 -0
- package/dist/features/TokensList/utils/getListItemsTotalCount.d.ts +8 -0
- package/dist/features/TokensList/utils/getListItemsTotalCount.js +8 -0
- package/dist/features/TokensList/utils/getListItemsTotalCount.js.map +1 -0
- package/dist/features/TokensList/utils/getListState.d.ts +9 -0
- package/dist/features/TokensList/utils/getListState.js +5 -0
- package/dist/features/TokensList/utils/getListState.js.map +1 -0
- package/dist/features/TokensList/utils/getListTotalHeight.d.ts +8 -0
- package/dist/features/TokensList/utils/getListTotalHeight.js +12 -0
- package/dist/features/TokensList/utils/getListTotalHeight.js.map +1 -0
- package/dist/features/TokensList/utils/getTokenByTotalIndex.d.ts +9 -0
- package/dist/features/TokensList/utils/getTokenByTotalIndex.js +7 -0
- package/dist/features/TokensList/utils/getTokenByTotalIndex.js.map +1 -0
- package/dist/features/TokensList/utils/index.d.ts +6 -0
- package/dist/features/TokensList/utils/index.js +15 -0
- package/dist/features/TokensList/utils/index.js.map +1 -0
- package/dist/features/TokensModal.js +2 -2
- package/dist/features/WalletCompatibilityCheck/WalletCompatibilityModal.js +2 -2
- package/dist/features/WalletCompatibilityCheck/index.js +1 -1
- package/dist/features/index.js +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useAllTokens.js +1 -1
- package/dist/hooks/useChains.js +1 -1
- package/dist/hooks/useCompatibilityCheck.js +1 -1
- package/dist/hooks/useDefaultToken.js +1 -1
- package/dist/hooks/useExternalDepositStatus/index.js +1 -1
- package/dist/hooks/useExternalDepositStatus/usePoaExternalDepositStatus.js +1 -1
- package/dist/hooks/useIntentsBalance.js +1 -1
- package/dist/hooks/useIsCompatibilityCheckRequired.js +1 -1
- package/dist/hooks/useMakeDepositAddress.js +1 -1
- package/dist/hooks/useMakeIntentsTransfer.js +4 -3
- package/dist/hooks/useMakeIntentsTransfer.js.map +1 -1
- package/dist/hooks/useMakeNEARFtTransferCall.js +4 -2
- package/dist/hooks/useMakeNEARFtTransferCall.js.map +1 -1
- package/dist/hooks/useMakeQuote.js +1 -1
- package/dist/hooks/useMakeQuoteTransfer.js +3 -2
- package/dist/hooks/useMakeQuoteTransfer.js.map +1 -1
- package/dist/hooks/useMakeSolanaTransfer.js +1 -1
- package/dist/hooks/useMakeTransfer.js +1 -1
- package/dist/hooks/useMergedBalance.js +1 -1
- package/dist/hooks/useSwitchChain.js +1 -1
- package/dist/hooks/useTheme.js +1 -1
- package/dist/hooks/useTokenInputPair.js +1 -1
- package/dist/hooks/useTokens.js +1 -1
- package/dist/hooks/useTokensFiltered.js +1 -1
- package/dist/hooks/useTokensIntentsUnique.js +1 -1
- package/dist/{index-Ce9bzNHX.js → index-JGjxyZXI.js} +70 -70
- package/dist/index-JGjxyZXI.js.map +1 -0
- package/dist/index.browser.esm-COWShN0a.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/machine/effects/index.js +1 -1
- package/dist/machine/effects/useAlchemyBalanceEffect.js +1 -1
- package/dist/machine/effects/useBalancesUpdateEffect.js +1 -1
- package/dist/machine/effects/useMakeQuoteEffect.js +1 -1
- package/dist/machine/effects/useSelectedTokensEffect.js +1 -1
- package/dist/machine/effects/useSetTokenBalanceEffect.js +1 -1
- package/dist/machine/effects/useSetTokenIntentsTargetEffect.js +1 -1
- package/dist/machine/effects/useWalletConnEffect.js +1 -1
- package/dist/machine/events/index.js +1 -1
- package/dist/machine/events/tokenSelect.js +1 -1
- package/dist/machine/events/validateInputAndMoveTo.js +1 -1
- package/dist/machine/events/validateInputs.js +1 -1
- package/dist/machine/index.js +1 -1
- package/dist/machine/snap.js +1 -1
- package/dist/machine/subscriptions/checkers/isSendAddressAsConnected.js +1 -1
- package/dist/machine/subscriptions/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme/ThemeProvider.js +1 -1
- package/dist/utils/checkers/isUserDeniedSigning.d.ts +2 -1
- package/dist/utils/checkers/isUserDeniedSigning.js +6 -4
- package/dist/utils/checkers/isUserDeniedSigning.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +26 -24
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/intents/signers/near.d.ts +1 -1
- package/dist/utils/intents/signers/near.js +2 -2
- package/dist/utils/intents/signers/privy.d.ts +1 -1
- package/dist/utils/intents/signers/privy.js +1 -1
- package/dist/utils/intents/signers/solana.d.ts +1 -1
- package/dist/utils/intents/signers/solana.js.map +1 -1
- package/dist/utils/isErrorLikeObject.d.ts +4 -0
- package/dist/utils/isErrorLikeObject.js +5 -0
- package/dist/utils/isErrorLikeObject.js.map +1 -0
- package/dist/utils/near/getNearNep141StorageBalance.js +1 -1
- package/dist/widgets/WidgetDeposit/WidgetDepositContent.js +2 -2
- package/dist/widgets/WidgetDeposit/WidgetDepositSkeleton.js +1 -1
- package/dist/widgets/WidgetSwap/WidgetSwapContent.js +2 -2
- package/dist/widgets/WidgetSwap/WidgetSwapSkeleton.js +1 -1
- package/dist/widgets/WidgetWithdraw/WidgetWithdrawContent.js +2 -2
- package/dist/widgets/WidgetWithdraw/WidgetWithdrawSkeleton.js +1 -1
- package/package.json +2 -2
- package/src/features/TokensList/TokenItem.tsx +7 -3
- package/src/features/TokensList/TokensList.tsx +151 -57
- package/src/features/TokensList/constants.ts +4 -0
- package/src/features/TokensList/hooks/index.ts +1 -0
- package/src/features/TokensList/hooks/useFocusOnList.ts +56 -0
- package/src/features/TokensList/types.ts +28 -0
- package/src/features/TokensList/utils/getFirstGroupItemTotalIndex.ts +28 -0
- package/src/features/TokensList/utils/getGroupHeadersTotalIndexes.ts +21 -0
- package/src/features/TokensList/utils/getListItemsTotalCount.ts +14 -0
- package/src/features/TokensList/utils/getListState.ts +16 -0
- package/src/features/TokensList/utils/getListTotalHeight.ts +25 -0
- package/src/features/TokensList/utils/getTokenByTotalIndex.ts +19 -0
- package/src/features/TokensList/utils/index.ts +6 -0
- package/src/hooks/useMakeIntentsTransfer.ts +40 -29
- package/src/hooks/useMakeNEARFtTransferCall.ts +10 -0
- package/src/hooks/useMakeQuoteTransfer.ts +2 -1
- package/src/utils/checkers/isUserDeniedSigning.ts +11 -3
- package/src/utils/index.ts +1 -0
- package/src/utils/intents/signers/near.ts +1 -1
- package/src/utils/intents/signers/privy.ts +1 -1
- package/src/utils/intents/signers/solana.ts +1 -1
- package/src/utils/isErrorLikeObject.ts +11 -0
- package/dist/config-CQ4V08as.js.map +0 -1
- package/dist/index-Ce9bzNHX.js.map +0 -1
|
@@ -6,8 +6,6 @@ import { TinyNumber } from '@/components/TinyNumber';
|
|
|
6
6
|
import { getUsdDisplayBalance } from '@/utils/formatters/getUsdDisplayBalance';
|
|
7
7
|
import type { Token, TokenBalance } from '@/types/token';
|
|
8
8
|
|
|
9
|
-
export const TOKEN_ITEM_HEIGHT = 58;
|
|
10
|
-
|
|
11
9
|
type Msg = { type: 'on_select_token'; token: Token };
|
|
12
10
|
|
|
13
11
|
type Props = {
|
|
@@ -15,6 +13,7 @@ type Props = {
|
|
|
15
13
|
balance: TokenBalance;
|
|
16
14
|
showBalance?: boolean;
|
|
17
15
|
isNotSelectable?: boolean;
|
|
16
|
+
isFocused?: boolean;
|
|
18
17
|
className?: string;
|
|
19
18
|
onMsg: (msg: Msg) => void;
|
|
20
19
|
};
|
|
@@ -24,6 +23,7 @@ export const TokenItem = ({
|
|
|
24
23
|
balance,
|
|
25
24
|
showBalance = true,
|
|
26
25
|
isNotSelectable,
|
|
26
|
+
isFocused,
|
|
27
27
|
className,
|
|
28
28
|
onMsg,
|
|
29
29
|
}: Props) => {
|
|
@@ -43,6 +43,7 @@ export const TokenItem = ({
|
|
|
43
43
|
{
|
|
44
44
|
'cursor-not-allowed': isNotSelectable,
|
|
45
45
|
'cursor-pointer hover:bg-sw-gray-800': !isNotSelectable,
|
|
46
|
+
'bg-sw-gray-800': isFocused && !isNotSelectable,
|
|
46
47
|
},
|
|
47
48
|
className,
|
|
48
49
|
)}
|
|
@@ -52,7 +53,10 @@ export const TokenItem = ({
|
|
|
52
53
|
<TokenIcon
|
|
53
54
|
token={token}
|
|
54
55
|
chainShowIcon={!token.isIntent}
|
|
55
|
-
className=
|
|
56
|
+
className={cn('group-hover:border-sw-gray-800 transition-colors', {
|
|
57
|
+
'border-sw-gray-800': isFocused,
|
|
58
|
+
'border-sw-gray-900': !isFocused,
|
|
59
|
+
})}
|
|
56
60
|
/>
|
|
57
61
|
|
|
58
62
|
<div className="gap-sw-xs mr-auto flex flex-col">
|
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
import { VList } from 'virtua';
|
|
2
|
-
import {
|
|
1
|
+
import { VList, VListHandle } from 'virtua';
|
|
2
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { TokenItem } from './TokenItem';
|
|
5
|
+
import { useFocusOnList } from './hooks';
|
|
5
6
|
import { TokensListPlaceholder } from './TokensListPlaceholder';
|
|
7
|
+
import {
|
|
8
|
+
LIST_CONTAINER_ID,
|
|
9
|
+
MAX_LIST_VIEW_AREA_HEIGHT,
|
|
10
|
+
TOKEN_ITEM_HEIGHT,
|
|
11
|
+
} from './constants';
|
|
12
|
+
import {
|
|
13
|
+
getFirstGroupItemTotalIndex,
|
|
14
|
+
getGroupHeadersTotalIndexes,
|
|
15
|
+
getListItemsTotalCount,
|
|
16
|
+
getListState,
|
|
17
|
+
getListTotalHeight,
|
|
18
|
+
getTokenByTotalIndex,
|
|
19
|
+
} from './utils';
|
|
20
|
+
import type { ListGroup } from './types';
|
|
21
|
+
|
|
6
22
|
import { cn } from '@/utils/cn';
|
|
7
23
|
import { Hr } from '@/components/Hr';
|
|
8
24
|
import { useUnsafeSnapshot } from '@/machine/snap';
|
|
@@ -31,18 +47,6 @@ type Props = {
|
|
|
31
47
|
onMsg: (msg: Msg) => void;
|
|
32
48
|
};
|
|
33
49
|
|
|
34
|
-
const useListState = (tokens: ReadonlyArray<Token>, search: string) => {
|
|
35
|
-
if (tokens.length === 0 && search) {
|
|
36
|
-
return 'EMPTY_SEARCH';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (tokens.length === 0 && !search) {
|
|
40
|
-
return 'NO_TOKENS';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return 'HAS_TOKENS';
|
|
44
|
-
};
|
|
45
|
-
|
|
46
50
|
export const TokensList = ({
|
|
47
51
|
variant,
|
|
48
52
|
search,
|
|
@@ -68,37 +72,52 @@ export const TokensList = ({
|
|
|
68
72
|
});
|
|
69
73
|
|
|
70
74
|
const areTokensGrouped = ctx.walletAddress ? groupTokens : false;
|
|
71
|
-
const tokensListState =
|
|
75
|
+
const tokensListState = getListState(filteredTokens.all, search);
|
|
72
76
|
|
|
73
|
-
const
|
|
74
|
-
|
|
77
|
+
const ref = useRef<VListHandle>(null);
|
|
78
|
+
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
79
|
+
|
|
80
|
+
const tokensUngrouped = useMemo<ListGroup<1>>(
|
|
81
|
+
() => [{ tokens: filteredTokens.all }],
|
|
75
82
|
[filteredTokens.all],
|
|
76
83
|
);
|
|
77
84
|
|
|
78
|
-
const tokensBySection = useMemo(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
const tokensBySection = useMemo(() => {
|
|
86
|
+
return [
|
|
87
|
+
...(filteredTokens.intents.length > 0
|
|
88
|
+
? [
|
|
89
|
+
{ label: appName, count: filteredTokens.intents.length },
|
|
90
|
+
{ tokens: filteredTokens.intents },
|
|
91
|
+
]
|
|
92
|
+
: []),
|
|
93
|
+
...(filteredTokens.wallet.length > 0
|
|
94
|
+
? [
|
|
95
|
+
{
|
|
96
|
+
label: chainIsNotSupported ? null : 'Connected wallet',
|
|
97
|
+
count: filteredTokens.wallet.length,
|
|
98
|
+
},
|
|
99
|
+
{ tokens: filteredTokens.wallet },
|
|
100
|
+
]
|
|
101
|
+
: []),
|
|
102
|
+
].filter(Boolean) as ListGroup<0 | 2 | 4>;
|
|
103
|
+
}, [filteredTokens.wallet, filteredTokens.intents, chainIsNotSupported]);
|
|
88
104
|
|
|
89
|
-
const
|
|
90
|
-
return (areTokensGrouped ? tokensBySection : tokensUngrouped).reduce(
|
|
91
|
-
(acc, group) => acc + group.tokens.length,
|
|
92
|
-
0,
|
|
93
|
-
);
|
|
94
|
-
}, [tokensBySection, tokensUngrouped, areTokensGrouped]);
|
|
105
|
+
const tokensList = areTokensGrouped ? tokensBySection : tokensUngrouped;
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
const listHeight = getListTotalHeight(tokensList);
|
|
108
|
+
const totalItems = getListItemsTotalCount(tokensList);
|
|
109
|
+
const headerIndexes = getGroupHeadersTotalIndexes(tokensList);
|
|
110
|
+
|
|
111
|
+
const handleBlur = useCallback(() => {
|
|
112
|
+
setFocusedIndex(-1);
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
useFocusOnList({
|
|
116
|
+
listRef: ref.current,
|
|
117
|
+
initialFocusedIndex: areTokensGrouped ? 1 : 0,
|
|
118
|
+
onFocus: (index) => setFocusedIndex(index),
|
|
119
|
+
onBlur: handleBlur,
|
|
120
|
+
});
|
|
102
121
|
|
|
103
122
|
switch (tokensListState) {
|
|
104
123
|
case 'EMPTY_SEARCH':
|
|
@@ -121,26 +140,98 @@ export const TokensList = ({
|
|
|
121
140
|
return (
|
|
122
141
|
<div className={cn('gap-sw-lg flex flex-col', className)}>
|
|
123
142
|
<VList
|
|
143
|
+
ref={ref}
|
|
144
|
+
tabIndex={0}
|
|
145
|
+
id={LIST_CONTAINER_ID}
|
|
146
|
+
itemSize={TOKEN_ITEM_HEIGHT}
|
|
124
147
|
className="hide-scrollbar"
|
|
125
148
|
style={{
|
|
126
|
-
maxHeight,
|
|
127
149
|
minHeight: 200,
|
|
128
|
-
height:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
150
|
+
height: listHeight,
|
|
151
|
+
maxHeight: MAX_LIST_VIEW_AREA_HEIGHT,
|
|
152
|
+
overflowAnchor: 'none',
|
|
153
|
+
outline: 'none',
|
|
154
|
+
}}
|
|
155
|
+
onKeyDown={(e) => {
|
|
156
|
+
if (!ref.current) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
switch (e.code) {
|
|
161
|
+
case 'ArrowUp': {
|
|
162
|
+
e.preventDefault();
|
|
163
|
+
let prevIndex = Math.max(focusedIndex - 1, 0);
|
|
164
|
+
|
|
165
|
+
if (areTokensGrouped && headerIndexes.includes(prevIndex)) {
|
|
166
|
+
prevIndex = prevIndex === 0 ? 1 : prevIndex - 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setFocusedIndex(prevIndex);
|
|
170
|
+
ref.current?.scrollToIndex(prevIndex, {
|
|
171
|
+
align: 'center',
|
|
172
|
+
smooth: true,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'ArrowDown': {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
let nextIndex = Math.min(focusedIndex + 1, totalItems - 1);
|
|
181
|
+
|
|
182
|
+
if (areTokensGrouped && headerIndexes.includes(nextIndex)) {
|
|
183
|
+
nextIndex = nextIndex === 0 ? 1 : nextIndex + 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
setFocusedIndex(nextIndex);
|
|
187
|
+
ref.current?.scrollToIndex(nextIndex, {
|
|
188
|
+
align: 'center',
|
|
189
|
+
smooth: true,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
case 'Enter': {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
const token = getTokenByTotalIndex(tokensList, focusedIndex);
|
|
198
|
+
|
|
199
|
+
if (token) {
|
|
200
|
+
onMsg({ type: 'on_select_token', token });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
default:
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
132
209
|
}}>
|
|
133
|
-
{
|
|
134
|
-
({ label, tokens: tokensToDisplay }) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
210
|
+
{tokensList.map(
|
|
211
|
+
({ label, count, tokens: tokensToDisplay }, groupIndex) => {
|
|
212
|
+
const firstItemInGroupIndex = getFirstGroupItemTotalIndex(
|
|
213
|
+
tokensList,
|
|
214
|
+
groupIndex,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (label !== undefined) {
|
|
218
|
+
if (!label) {
|
|
219
|
+
// must be rendered for calculations even if label is null
|
|
220
|
+
return <header key={groupIndex} />;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<header
|
|
225
|
+
key={label}
|
|
226
|
+
className="pb-sw-lg pt-sw-sm flex flex-col">
|
|
138
227
|
<Hr />
|
|
139
|
-
<span className="text-sw-label-sm pt-sw-xl text-sw-gray-100">{`${label} — ${
|
|
228
|
+
<span className="text-sw-label-sm pt-sw-xl text-sw-gray-100">{`${label} — ${count}`}</span>
|
|
140
229
|
</header>
|
|
141
|
-
)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
142
232
|
|
|
143
|
-
|
|
233
|
+
if (tokensToDisplay) {
|
|
234
|
+
return tokensToDisplay.map((token, tokenIndex) => {
|
|
144
235
|
const tokenBalanceKey = getTokenBalanceKey(token);
|
|
145
236
|
|
|
146
237
|
return (
|
|
@@ -149,17 +240,20 @@ export const TokensList = ({
|
|
|
149
240
|
key={tokenBalanceKey}
|
|
150
241
|
showBalance={showBalances}
|
|
151
242
|
balance={mergedBalance[tokenBalanceKey]}
|
|
243
|
+
isFocused={
|
|
244
|
+
focusedIndex === firstItemInGroupIndex + tokenIndex
|
|
245
|
+
}
|
|
152
246
|
isNotSelectable={
|
|
153
247
|
chainIsNotSupported && !!ctx.walletAddress
|
|
154
248
|
}
|
|
155
249
|
onMsg={onMsg}
|
|
156
250
|
/>
|
|
157
251
|
);
|
|
158
|
-
})
|
|
252
|
+
});
|
|
253
|
+
}
|
|
159
254
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
),
|
|
255
|
+
return null;
|
|
256
|
+
},
|
|
163
257
|
)}
|
|
164
258
|
</VList>
|
|
165
259
|
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useFocusOnList } from './useFocusOnList';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useCallback, useEffect } from 'react';
|
|
2
|
+
import type { VListHandle } from 'virtua';
|
|
3
|
+
|
|
4
|
+
import { LIST_CONTAINER_ID } from '../constants';
|
|
5
|
+
|
|
6
|
+
import { useHandleKeyDown } from '@/hooks/useHandleKeyDown';
|
|
7
|
+
|
|
8
|
+
type Args = {
|
|
9
|
+
initialFocusedIndex: number;
|
|
10
|
+
listRef: VListHandle | null;
|
|
11
|
+
onFocus: (index: number) => void;
|
|
12
|
+
onBlur: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const useFocusOnList = ({
|
|
16
|
+
initialFocusedIndex,
|
|
17
|
+
listRef,
|
|
18
|
+
onFocus,
|
|
19
|
+
onBlur,
|
|
20
|
+
}: Args) => {
|
|
21
|
+
useHandleKeyDown('ArrowDown', () => {
|
|
22
|
+
const virtualListDiv = document.getElementById(LIST_CONTAINER_ID);
|
|
23
|
+
|
|
24
|
+
if (virtualListDiv && document.activeElement !== virtualListDiv) {
|
|
25
|
+
onFocus(initialFocusedIndex);
|
|
26
|
+
listRef?.scrollToIndex(initialFocusedIndex, { align: 'nearest' });
|
|
27
|
+
|
|
28
|
+
// required to prevent initial scroll on focus
|
|
29
|
+
// which causes list's jump
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
virtualListDiv.focus({ preventScroll: true });
|
|
32
|
+
}, 0);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const handleBlur = useCallback(
|
|
37
|
+
(event: FocusEvent) => {
|
|
38
|
+
const virtualListDiv = document.getElementById(LIST_CONTAINER_ID);
|
|
39
|
+
|
|
40
|
+
if (event.target === virtualListDiv) {
|
|
41
|
+
onBlur();
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
[onBlur],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const virtualListDiv = document.getElementById(LIST_CONTAINER_ID);
|
|
49
|
+
|
|
50
|
+
virtualListDiv?.addEventListener('blur', handleBlur);
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
virtualListDiv?.removeEventListener('blur', handleBlur);
|
|
54
|
+
};
|
|
55
|
+
}, [handleBlur]);
|
|
56
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Token } from '@/types/token';
|
|
2
|
+
|
|
3
|
+
type OddGroup = {
|
|
4
|
+
label: string | null;
|
|
5
|
+
count: number;
|
|
6
|
+
tokens?: never;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type EvenGroup = {
|
|
10
|
+
label?: never;
|
|
11
|
+
count?: never;
|
|
12
|
+
tokens: Token[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// covers 3 sections max (extend if needed)
|
|
16
|
+
export type ListGroup<N extends number = 6> = N extends 0
|
|
17
|
+
? []
|
|
18
|
+
: N extends 1
|
|
19
|
+
? [EvenGroup]
|
|
20
|
+
: N extends 2
|
|
21
|
+
? [OddGroup, EvenGroup]
|
|
22
|
+
: N extends 4
|
|
23
|
+
? [OddGroup, EvenGroup, OddGroup, EvenGroup]
|
|
24
|
+
: N extends 6
|
|
25
|
+
? [OddGroup, EvenGroup, OddGroup, EvenGroup, OddGroup, EvenGroup]
|
|
26
|
+
: never;
|
|
27
|
+
|
|
28
|
+
export type AnyListGroup = ListGroup<0 | 1 | 2 | 4 | 6>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AnyListGroup } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get index across all groups of the first item in a given group
|
|
5
|
+
*
|
|
6
|
+
* @param tokensList - The list of tokens (grouped or ungrouped)
|
|
7
|
+
* @param groupIndex - The index of the group
|
|
8
|
+
* @returns The total index of the first item in the group
|
|
9
|
+
*/
|
|
10
|
+
export const getFirstGroupItemTotalIndex = (
|
|
11
|
+
tokensList: AnyListGroup,
|
|
12
|
+
groupIndex: number,
|
|
13
|
+
) => {
|
|
14
|
+
const tokensGroupIndex = groupIndex % 2 !== 0 ? groupIndex : 0;
|
|
15
|
+
|
|
16
|
+
if (tokensGroupIndex <= 1) {
|
|
17
|
+
return tokensGroupIndex;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
tokensList.reduce((acc, group, index) => {
|
|
22
|
+
return (
|
|
23
|
+
acc +
|
|
24
|
+
(group.tokens && index < tokensGroupIndex ? group.tokens.length + 1 : 0)
|
|
25
|
+
);
|
|
26
|
+
}, 0) + 1
|
|
27
|
+
); // +1 for current group's header
|
|
28
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AnyListGroup } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get positions of all group headers in the combined list
|
|
5
|
+
*
|
|
6
|
+
* @param tokensList - The list of tokens (grouped or ungrouped)
|
|
7
|
+
* @returns List of positions of all group headers in the combined list
|
|
8
|
+
*/
|
|
9
|
+
export const getGroupHeadersTotalIndexes = (tokensList: AnyListGroup) => {
|
|
10
|
+
return tokensList.reduce<number[]>((acc, group, index) => {
|
|
11
|
+
if (index === 0 && group.label) {
|
|
12
|
+
return [0];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (group.tokens && index < tokensList.length - 1) {
|
|
16
|
+
return [...acc, (acc[acc.length - 1] ?? 0) + group.tokens.length + 1];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return acc;
|
|
20
|
+
}, []);
|
|
21
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AnyListGroup } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the total number of RENDERED items in the list (including group headers)
|
|
5
|
+
*
|
|
6
|
+
* @param tokensList - The list of tokens (grouped or ungrouped)
|
|
7
|
+
* @returns The total number of items in the list
|
|
8
|
+
*/
|
|
9
|
+
export const getListItemsTotalCount = (tokensList: AnyListGroup) => {
|
|
10
|
+
return tokensList.reduce(
|
|
11
|
+
(acc, group) => acc + (group.tokens?.length ?? 0) + (group.label ? 1 : 0),
|
|
12
|
+
0,
|
|
13
|
+
);
|
|
14
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Token } from '@/types/token';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the state of the list
|
|
5
|
+
*
|
|
6
|
+
* @param tokens - The list of tokens (grouped or ungrouped)
|
|
7
|
+
* @param search - The search string
|
|
8
|
+
* @returns The state of the list: 'EMPTY_SEARCH', 'NO_TOKENS', 'HAS_TOKENS'
|
|
9
|
+
*/
|
|
10
|
+
export const getListState = (tokens: ReadonlyArray<Token>, search: string) => {
|
|
11
|
+
if (tokens.length) {
|
|
12
|
+
return 'HAS_TOKENS';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return !search ? 'NO_TOKENS' : 'EMPTY_SEARCH';
|
|
16
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { LIST_SECTION_HEADER_HEIGHT, TOKEN_ITEM_HEIGHT } from '../constants';
|
|
2
|
+
import type { AnyListGroup } from '../types';
|
|
3
|
+
|
|
4
|
+
const getTokensTotalCount = (tokensList: AnyListGroup) => {
|
|
5
|
+
return tokensList.reduce(
|
|
6
|
+
(acc, group) => acc + (group.tokens?.length ?? 0),
|
|
7
|
+
0,
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the total height of the list
|
|
13
|
+
*
|
|
14
|
+
* @param tokensList - The list of tokens (grouped or ungrouped)
|
|
15
|
+
* @returns The total height of the list
|
|
16
|
+
*/
|
|
17
|
+
export const getListTotalHeight = (tokensList: AnyListGroup) => {
|
|
18
|
+
const tokensCount = getTokensTotalCount(tokensList);
|
|
19
|
+
|
|
20
|
+
return tokensCount
|
|
21
|
+
? tokensCount * TOKEN_ITEM_HEIGHT +
|
|
22
|
+
tokensList.filter((group) => !!group.label).length *
|
|
23
|
+
LIST_SECTION_HEADER_HEIGHT
|
|
24
|
+
: TOKEN_ITEM_HEIGHT * 2;
|
|
25
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AnyListGroup } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the token by total index across all groups and headers
|
|
5
|
+
*
|
|
6
|
+
* @param tokensList - The list of tokens (grouped or ungrouped)
|
|
7
|
+
* @param totalIndex - The total index of the token
|
|
8
|
+
* @returns The token
|
|
9
|
+
*/
|
|
10
|
+
export const getTokenByTotalIndex = (
|
|
11
|
+
tokensList: AnyListGroup,
|
|
12
|
+
totalIndex: number,
|
|
13
|
+
) => {
|
|
14
|
+
const flatList = tokensList.flatMap((group) =>
|
|
15
|
+
'label' in group ? [null] : group.tokens,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return flatList[totalIndex];
|
|
19
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { getGroupHeadersTotalIndexes } from './getGroupHeadersTotalIndexes';
|
|
2
|
+
export { getFirstGroupItemTotalIndex } from './getFirstGroupItemTotalIndex';
|
|
3
|
+
export { getListItemsTotalCount } from './getListItemsTotalCount';
|
|
4
|
+
export { getTokenByTotalIndex } from './getTokenByTotalIndex';
|
|
5
|
+
export { getListTotalHeight } from './getListTotalHeight';
|
|
6
|
+
export { getListState } from './getListState';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BridgeSDK,
|
|
3
2
|
createIntentSignerNEP413,
|
|
4
3
|
createInternalTransferRoute,
|
|
5
4
|
createNearWithdrawalRoute,
|
|
6
5
|
FeeExceedsAmountError,
|
|
6
|
+
IntentsSDK,
|
|
7
7
|
MinWithdrawalAmountError,
|
|
8
8
|
type RouteConfig,
|
|
9
|
-
} from '@defuse-protocol/
|
|
9
|
+
} from '@defuse-protocol/intents-sdk';
|
|
10
10
|
import type { NearWalletBase as NearWallet } from '@hot-labs/near-connect/build/types/wallet';
|
|
11
11
|
import { snakeCase } from 'change-case';
|
|
12
12
|
import { generateRandomBytes } from '../utils/near/getRandomBytes';
|
|
@@ -18,6 +18,7 @@ import { TransferError } from '@/errors';
|
|
|
18
18
|
import { INTENTS_CONTRACT } from '@/constants';
|
|
19
19
|
import { CHAIN_IDS_MAP } from '@/constants/chains';
|
|
20
20
|
import { notReachable } from '@/utils/notReachable';
|
|
21
|
+
import { isErrorLikeObject } from '@/utils/isErrorLikeObject';
|
|
21
22
|
import { localStorageTyped } from '@/utils/localstorage';
|
|
22
23
|
import { queryContract } from '@/utils/near/queryContract';
|
|
23
24
|
import { IntentSignerPrivy } from '@/utils/intents/signers/privy';
|
|
@@ -245,7 +246,7 @@ export const useMakeIntentsTransfer = ({ providers }: IntentsTransferArgs) => {
|
|
|
245
246
|
notReachable(intentsAccountType);
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
const sdk = new
|
|
249
|
+
const sdk = new IntentsSDK({ referral: snakeCase(appName) });
|
|
249
250
|
|
|
250
251
|
sdk.setIntentSigner(signer);
|
|
251
252
|
|
|
@@ -261,35 +262,48 @@ export const useMakeIntentsTransfer = ({ providers }: IntentsTransferArgs) => {
|
|
|
261
262
|
routeConfig = createInternalTransferRoute();
|
|
262
263
|
}
|
|
263
264
|
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
265
|
+
const withdrawalParams = {
|
|
266
|
+
assetId: ctx.sourceToken.assetId,
|
|
267
|
+
amount: BigInt(ctx.sourceTokenAmount),
|
|
268
|
+
destinationAddress: getDestinationAddress(
|
|
269
|
+
ctx,
|
|
270
|
+
isDirectNearTokenWithdrawal || isDirectNonNearWithdrawal,
|
|
271
|
+
),
|
|
272
|
+
destinationMemo: undefined,
|
|
273
|
+
feeInclusive: false,
|
|
274
|
+
routeConfig,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
onPending('WAITING_CONFIRMATION');
|
|
277
278
|
|
|
278
279
|
try {
|
|
279
|
-
await
|
|
280
|
+
const feeEstimation = await sdk.estimateWithdrawalFee({
|
|
281
|
+
withdrawalParams,
|
|
282
|
+
});
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
const { intentHash } = await sdk.signAndSendWithdrawalIntent({
|
|
285
|
+
withdrawalParams,
|
|
286
|
+
feeEstimation,
|
|
287
|
+
});
|
|
283
288
|
|
|
284
289
|
onPending('PROCESSING');
|
|
285
|
-
const
|
|
290
|
+
const intentTx = await sdk.waitForIntentSettlement({ intentHash });
|
|
286
291
|
|
|
287
|
-
await
|
|
292
|
+
const completionResult = await sdk.waitForWithdrawalCompletion({
|
|
293
|
+
withdrawalParams,
|
|
294
|
+
intentTx,
|
|
295
|
+
});
|
|
288
296
|
|
|
289
297
|
return {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
298
|
+
intent: intentTx.hash,
|
|
299
|
+
// no hash means completion not trackable for this bridge
|
|
300
|
+
hash: completionResult.hash ?? '',
|
|
301
|
+
transactionLink: completionResult.hash
|
|
302
|
+
? getTransactionLink(
|
|
303
|
+
CHAIN_IDS_MAP[ctx.targetToken.blockchain],
|
|
304
|
+
completionResult.hash,
|
|
305
|
+
)
|
|
306
|
+
: '',
|
|
293
307
|
};
|
|
294
308
|
} catch (e: unknown) {
|
|
295
309
|
logger.error('[TRANSFER ERROR]', e);
|
|
@@ -313,7 +327,7 @@ export const useMakeIntentsTransfer = ({ providers }: IntentsTransferArgs) => {
|
|
|
313
327
|
});
|
|
314
328
|
}
|
|
315
329
|
|
|
316
|
-
if (e
|
|
330
|
+
if (isErrorLikeObject(e)) {
|
|
317
331
|
if (e.message.includes('Fee is not estimated')) {
|
|
318
332
|
throw new TransferError({
|
|
319
333
|
code: 'FEES_NOT_ESTIMATED',
|
|
@@ -321,10 +335,7 @@ export const useMakeIntentsTransfer = ({ providers }: IntentsTransferArgs) => {
|
|
|
321
335
|
}
|
|
322
336
|
|
|
323
337
|
// User rejected
|
|
324
|
-
if (
|
|
325
|
-
isUserDeniedSigning(e.message) ||
|
|
326
|
-
isUserDeniedSigning(`${e.cause}`)
|
|
327
|
-
) {
|
|
338
|
+
if (isUserDeniedSigning(e)) {
|
|
328
339
|
return undefined;
|
|
329
340
|
}
|
|
330
341
|
}
|