@ensofinance/checkout-widget 0.1.6 → 0.1.8
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/checkout-widget.es.js +25523 -24215
- package/dist/checkout-widget.es.js.map +1 -1
- package/dist/checkout-widget.umd.js +64 -59
- package/dist/checkout-widget.umd.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/package.json +1 -1
- package/src/assets/providers/alchemypay.svg +21 -0
- package/src/assets/providers/banxa.svg +21 -0
- package/src/assets/providers/binanceconnect.svg +14 -0
- package/src/assets/providers/kryptonim.svg +6 -0
- package/src/assets/providers/mercuryo.svg +21 -0
- package/src/assets/providers/moonpay.svg +14 -0
- package/src/assets/providers/stripe.svg +16 -0
- package/src/assets/providers/swapped.svg +1 -0
- package/src/assets/providers/topper.svg +14 -0
- package/src/assets/providers/transak.svg +21 -0
- package/src/assets/providers/unlimit.svg +21 -0
- package/src/components/AmountInput.tsx +41 -25
- package/src/components/ChakraProvider.tsx +36 -13
- package/src/components/Checkout.tsx +7 -1
- package/src/components/CurrencySwapDisplay.tsx +59 -22
- package/src/components/DepositProcessing.tsx +1 -1
- package/src/components/ExchangeConfirmSecurity.tsx +1 -1
- package/src/components/QuoteParameters.tsx +1 -1
- package/src/components/TransactionDetailRow.tsx +2 -2
- package/src/components/cards/ExchangeCard.tsx +1 -1
- package/src/components/cards/OptionCard.tsx +2 -1
- package/src/components/cards/WalletCard.tsx +1 -1
- package/src/components/modal.tsx +3 -3
- package/src/components/steps/CardBuyFlow/CardBuyFlow.tsx +412 -0
- package/src/components/steps/CardBuyFlow/ChooseAmountStep.tsx +352 -0
- package/src/components/steps/CardBuyFlow/OpenWidgetStep.tsx +193 -0
- package/src/components/steps/ExchangeFlow.tsx +254 -1416
- package/src/components/steps/FlowSelector.tsx +117 -60
- package/src/components/steps/SmartAccountFlow.tsx +372 -0
- package/src/components/steps/WalletFlow/WalletAmountStep.tsx +2 -2
- package/src/components/steps/WalletFlow/WalletConfirmStep.tsx +92 -51
- package/src/components/steps/WalletFlow/WalletFlow.tsx +17 -16
- package/src/components/steps/WalletFlow/WalletQuoteStep.tsx +2 -2
- package/src/components/steps/WalletFlow/WalletTokenStep.tsx +6 -4
- package/src/components/steps/shared/ChooseAmountStep.tsx +325 -0
- package/src/components/steps/shared/SignUserOpStep.tsx +117 -0
- package/src/components/steps/shared/TrackUserOpStep.tsx +625 -0
- package/src/components/steps/shared/exchangeIntegration.ts +19 -0
- package/src/components/steps/shared/types.ts +22 -0
- package/src/components/ui/index.tsx +23 -6
- package/src/components/ui/toaster.tsx +2 -1
- package/src/components/ui/transitions.tsx +16 -0
- package/src/types/index.ts +99 -0
- package/src/util/constants.tsx +27 -0
- package/src/util/enso-hooks.tsx +75 -61
- package/src/util/meld-hooks.tsx +533 -0
- package/src/assets/usdc.webp +0 -0
- package/src/assets/usdt.webp +0 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Box } from "@chakra-ui/react";
|
|
3
|
+
|
|
4
|
+
import { BodyWrapper, HeaderTitle, HeaderDescription } from "../../ui/styled";
|
|
5
|
+
import { Button } from "../../ui";
|
|
6
|
+
import { AmountInput, AmountInputValue } from "../../AmountInput";
|
|
7
|
+
import { useAppStore } from "@/store";
|
|
8
|
+
import {
|
|
9
|
+
denormalizeValue,
|
|
10
|
+
compareCaseInsensitive,
|
|
11
|
+
formatNumber,
|
|
12
|
+
formatUSD,
|
|
13
|
+
normalizeValue,
|
|
14
|
+
} from "@/util";
|
|
15
|
+
import {
|
|
16
|
+
precisionizeNumber,
|
|
17
|
+
getPositiveDecimalValue,
|
|
18
|
+
} from "@/util/common";
|
|
19
|
+
import {
|
|
20
|
+
EXCHANGE_MAX_LIMIT_GAP_USD,
|
|
21
|
+
EXCHANGE_MIN_LIMIT,
|
|
22
|
+
} from "@/util/constants";
|
|
23
|
+
import { useAppDetails } from "@/util/enso-hooks";
|
|
24
|
+
import type { MatchedToken } from "./types";
|
|
25
|
+
|
|
26
|
+
const ChooseAmountStep = ({
|
|
27
|
+
setStep,
|
|
28
|
+
nextStep,
|
|
29
|
+
selectedToken,
|
|
30
|
+
prefillAmountToken,
|
|
31
|
+
mode,
|
|
32
|
+
}: {
|
|
33
|
+
setStep: (step: number) => void;
|
|
34
|
+
nextStep: number;
|
|
35
|
+
selectedToken: MatchedToken | null;
|
|
36
|
+
prefillAmountToken?: string | null;
|
|
37
|
+
mode: "cex" | "smart-account";
|
|
38
|
+
}) => {
|
|
39
|
+
const [amountInput, setAmountInput] = useState<AmountInputValue>({
|
|
40
|
+
tokenAmount: "",
|
|
41
|
+
usdAmount: "",
|
|
42
|
+
mode: "usd",
|
|
43
|
+
});
|
|
44
|
+
const initializedTokenKeyRef = useRef<string | null>(null);
|
|
45
|
+
const setAmountIn = useAppStore((s) => s.setAmountIn);
|
|
46
|
+
const amountInStore = useAppStore((s) => s.amountIn);
|
|
47
|
+
const chainIdInStore = useAppStore((s) => s.chainIdIn);
|
|
48
|
+
const tokenInStore = useAppStore((s) => s.tokenIn);
|
|
49
|
+
const { tokenInData } = useAppDetails();
|
|
50
|
+
const isStable = selectedToken?.symbol.toLowerCase().includes("USD");
|
|
51
|
+
const roundingPrecision = isStable ? 2 : 6;
|
|
52
|
+
const amount = amountInput.tokenAmount;
|
|
53
|
+
const usdValue = amountInput.usdAmount;
|
|
54
|
+
|
|
55
|
+
const isCex = mode === "cex";
|
|
56
|
+
|
|
57
|
+
const maxUsdAmount = selectedToken
|
|
58
|
+
? isCex
|
|
59
|
+
? (selectedToken.marketValue - EXCHANGE_MAX_LIMIT_GAP_USD).toFixed(2)
|
|
60
|
+
: selectedToken.marketValue.toFixed(2)
|
|
61
|
+
: 0;
|
|
62
|
+
|
|
63
|
+
const tokenPriceUsd = useMemo(() => {
|
|
64
|
+
if (!selectedToken || !selectedToken.balance) return undefined;
|
|
65
|
+
return selectedToken.marketValue / selectedToken.balance;
|
|
66
|
+
}, [selectedToken]);
|
|
67
|
+
|
|
68
|
+
const getPercentAmounts = useCallback(
|
|
69
|
+
(percent: number) => {
|
|
70
|
+
if (!selectedToken) return;
|
|
71
|
+
|
|
72
|
+
const targetUsdAmount = (selectedToken.marketValue * percent) / 100;
|
|
73
|
+
|
|
74
|
+
let finalUsdAmount: number;
|
|
75
|
+
let finalTokenAmount: number;
|
|
76
|
+
|
|
77
|
+
if (isCex) {
|
|
78
|
+
const minValueForToken =
|
|
79
|
+
EXCHANGE_MIN_LIMIT[
|
|
80
|
+
selectedToken.symbol as keyof typeof EXCHANGE_MIN_LIMIT
|
|
81
|
+
] || 0;
|
|
82
|
+
|
|
83
|
+
finalUsdAmount = Math.max(
|
|
84
|
+
minValueForToken,
|
|
85
|
+
Math.min(targetUsdAmount, +maxUsdAmount),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const tokenPrice =
|
|
89
|
+
selectedToken.marketValue / selectedToken.balance;
|
|
90
|
+
finalTokenAmount = Math.min(
|
|
91
|
+
finalUsdAmount / tokenPrice,
|
|
92
|
+
selectedToken.balance,
|
|
93
|
+
);
|
|
94
|
+
} else {
|
|
95
|
+
finalUsdAmount = targetUsdAmount;
|
|
96
|
+
finalTokenAmount = (selectedToken.balance * percent) / 100;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
tokenAmount: precisionizeNumber(
|
|
101
|
+
finalTokenAmount,
|
|
102
|
+
roundingPrecision,
|
|
103
|
+
),
|
|
104
|
+
usdAmount: finalUsdAmount.toFixed(2),
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
[selectedToken, isCex, maxUsdAmount, roundingPrecision],
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Set initial value on token change.
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (!selectedToken) return;
|
|
113
|
+
|
|
114
|
+
const tokenKey = [
|
|
115
|
+
selectedToken.chainId,
|
|
116
|
+
selectedToken.symbol,
|
|
117
|
+
selectedToken.tokenAddress ?? "",
|
|
118
|
+
].join(":");
|
|
119
|
+
if (initializedTokenKeyRef.current === tokenKey) return;
|
|
120
|
+
|
|
121
|
+
const storeMatchesSelectedToken =
|
|
122
|
+
!!selectedToken.tokenAddress &&
|
|
123
|
+
!!chainIdInStore &&
|
|
124
|
+
chainIdInStore === selectedToken.chainId &&
|
|
125
|
+
compareCaseInsensitive(tokenInStore, selectedToken.tokenAddress);
|
|
126
|
+
|
|
127
|
+
let tokenAmountToUse: string | null = null;
|
|
128
|
+
|
|
129
|
+
if (
|
|
130
|
+
storeMatchesSelectedToken &&
|
|
131
|
+
tokenInData?.decimals &&
|
|
132
|
+
amountInStore &&
|
|
133
|
+
amountInStore !== "0"
|
|
134
|
+
) {
|
|
135
|
+
const normalizedStoreAmount = normalizeValue(
|
|
136
|
+
amountInStore,
|
|
137
|
+
tokenInData.decimals,
|
|
138
|
+
);
|
|
139
|
+
const normalizedNumber = Number(normalizedStoreAmount);
|
|
140
|
+
if (Number.isFinite(normalizedNumber) && normalizedNumber > 0) {
|
|
141
|
+
tokenAmountToUse = precisionizeNumber(
|
|
142
|
+
normalizedNumber,
|
|
143
|
+
roundingPrecision,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!tokenAmountToUse && prefillAmountToken) {
|
|
149
|
+
const prefillNumber = Number(prefillAmountToken);
|
|
150
|
+
if (Number.isFinite(prefillNumber) && prefillNumber > 0) {
|
|
151
|
+
tokenAmountToUse = precisionizeNumber(
|
|
152
|
+
prefillNumber,
|
|
153
|
+
roundingPrecision,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (tokenAmountToUse) {
|
|
159
|
+
const usdAmountToUse =
|
|
160
|
+
tokenPriceUsd && tokenPriceUsd > 0
|
|
161
|
+
? (Number(tokenAmountToUse) * tokenPriceUsd).toFixed(2)
|
|
162
|
+
: "";
|
|
163
|
+
|
|
164
|
+
setAmountInput((prev) => ({
|
|
165
|
+
...prev,
|
|
166
|
+
tokenAmount: tokenAmountToUse as string,
|
|
167
|
+
usdAmount: usdAmountToUse,
|
|
168
|
+
}));
|
|
169
|
+
initializedTokenKeyRef.current = tokenKey;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const percentAmounts = getPercentAmounts(100);
|
|
174
|
+
if (!percentAmounts) return;
|
|
175
|
+
|
|
176
|
+
setAmountInput((prev) => ({
|
|
177
|
+
...prev,
|
|
178
|
+
...percentAmounts,
|
|
179
|
+
}));
|
|
180
|
+
initializedTokenKeyRef.current = tokenKey;
|
|
181
|
+
}, [
|
|
182
|
+
amountInStore,
|
|
183
|
+
chainIdInStore,
|
|
184
|
+
getPercentAmounts,
|
|
185
|
+
prefillAmountToken,
|
|
186
|
+
roundingPrecision,
|
|
187
|
+
selectedToken,
|
|
188
|
+
tokenInData?.decimals,
|
|
189
|
+
tokenInStore,
|
|
190
|
+
tokenPriceUsd,
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
if (!tokenInData?.decimals) return;
|
|
195
|
+
|
|
196
|
+
const normalizedAmount = amount.endsWith(".")
|
|
197
|
+
? amount.slice(0, -1)
|
|
198
|
+
: amount;
|
|
199
|
+
|
|
200
|
+
if (!normalizedAmount || normalizedAmount === ".") {
|
|
201
|
+
setAmountIn("0");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
setAmountIn(
|
|
207
|
+
denormalizeValue(normalizedAmount, tokenInData.decimals),
|
|
208
|
+
);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
setAmountIn("0");
|
|
211
|
+
}
|
|
212
|
+
}, [amount, tokenInData?.decimals, setAmountIn]);
|
|
213
|
+
|
|
214
|
+
const numericAmount = getPositiveDecimalValue(amount);
|
|
215
|
+
const hasPositiveAmount = numericAmount !== null;
|
|
216
|
+
const hasUsdValue = !!usdValue && usdValue !== ".";
|
|
217
|
+
const notEnoughBalance = selectedToken
|
|
218
|
+
? hasPositiveAmount &&
|
|
219
|
+
numericAmount !== null &&
|
|
220
|
+
numericAmount > selectedToken.balance
|
|
221
|
+
: true;
|
|
222
|
+
|
|
223
|
+
// Limits validation logic - only for CEX withdrawals
|
|
224
|
+
const currentUsdValue = hasUsdValue
|
|
225
|
+
? (getPositiveDecimalValue(usdValue) ?? 0)
|
|
226
|
+
: 0;
|
|
227
|
+
const minValueForToken =
|
|
228
|
+
isCex && selectedToken
|
|
229
|
+
? EXCHANGE_MIN_LIMIT[
|
|
230
|
+
selectedToken.symbol as keyof typeof EXCHANGE_MIN_LIMIT
|
|
231
|
+
]
|
|
232
|
+
: 0;
|
|
233
|
+
|
|
234
|
+
const isBelowMinAmount =
|
|
235
|
+
isCex &&
|
|
236
|
+
selectedToken &&
|
|
237
|
+
hasPositiveAmount &&
|
|
238
|
+
currentUsdValue > 0 &&
|
|
239
|
+
minValueForToken &&
|
|
240
|
+
numericAmount !== null &&
|
|
241
|
+
numericAmount < minValueForToken;
|
|
242
|
+
const isAboveMaxAmount =
|
|
243
|
+
isCex &&
|
|
244
|
+
selectedToken &&
|
|
245
|
+
hasPositiveAmount &&
|
|
246
|
+
currentUsdValue > 0 &&
|
|
247
|
+
currentUsdValue > +maxUsdAmount;
|
|
248
|
+
|
|
249
|
+
const isAmountInvalid =
|
|
250
|
+
!hasPositiveAmount ||
|
|
251
|
+
isBelowMinAmount ||
|
|
252
|
+
isAboveMaxAmount ||
|
|
253
|
+
notEnoughBalance;
|
|
254
|
+
|
|
255
|
+
if (!selectedToken) {
|
|
256
|
+
return (
|
|
257
|
+
<BodyWrapper>
|
|
258
|
+
<Box textAlign="center" color="fg.subtle" py={8}>
|
|
259
|
+
No token selected
|
|
260
|
+
</Box>
|
|
261
|
+
</BodyWrapper>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return (
|
|
266
|
+
<BodyWrapper>
|
|
267
|
+
<Box mb={4} width="100%" textAlign="left">
|
|
268
|
+
<HeaderTitle>Enter Amount</HeaderTitle>
|
|
269
|
+
<HeaderDescription>
|
|
270
|
+
Available: {formatNumber(selectedToken.balance)}{" "}
|
|
271
|
+
{selectedToken.symbol} (
|
|
272
|
+
{formatUSD(selectedToken.marketValue)})
|
|
273
|
+
</HeaderDescription>
|
|
274
|
+
</Box>
|
|
275
|
+
|
|
276
|
+
<Box
|
|
277
|
+
display={"flex"}
|
|
278
|
+
flexDirection={"column"}
|
|
279
|
+
gap={"8px"}
|
|
280
|
+
width="100%"
|
|
281
|
+
>
|
|
282
|
+
<AmountInput
|
|
283
|
+
value={amountInput}
|
|
284
|
+
onChange={setAmountInput}
|
|
285
|
+
tokenSymbol={selectedToken.symbol}
|
|
286
|
+
tokenPriceUsd={tokenPriceUsd}
|
|
287
|
+
roundingPrecision={roundingPrecision}
|
|
288
|
+
onPercentSelect={getPercentAmounts}
|
|
289
|
+
/>
|
|
290
|
+
</Box>
|
|
291
|
+
|
|
292
|
+
{
|
|
293
|
+
<Box
|
|
294
|
+
textAlign="center"
|
|
295
|
+
color="fg.subtle"
|
|
296
|
+
fontSize="xs"
|
|
297
|
+
h={3}
|
|
298
|
+
m={-1}
|
|
299
|
+
visibility={isAmountInvalid ? "visible" : "hidden"}
|
|
300
|
+
>
|
|
301
|
+
{!hasPositiveAmount
|
|
302
|
+
? "Please enter an amount"
|
|
303
|
+
: isBelowMinAmount
|
|
304
|
+
? `Minimum amount is ${formatNumber(minValueForToken)} ${selectedToken.symbol}`
|
|
305
|
+
: isAboveMaxAmount
|
|
306
|
+
? `Maximum amount is ${formatUSD(maxUsdAmount)} (balance - $${EXCHANGE_MAX_LIMIT_GAP_USD})`
|
|
307
|
+
: notEnoughBalance
|
|
308
|
+
? "Amount exceeds available balance"
|
|
309
|
+
: ""}
|
|
310
|
+
</Box>
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
<Button
|
|
314
|
+
onClick={() =>
|
|
315
|
+
isAmountInvalid ? undefined : setStep(nextStep)
|
|
316
|
+
}
|
|
317
|
+
disabled={isAmountInvalid}
|
|
318
|
+
>
|
|
319
|
+
Continue
|
|
320
|
+
</Button>
|
|
321
|
+
</BodyWrapper>
|
|
322
|
+
);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
export default ChooseAmountStep;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Flex, Skeleton } from "@chakra-ui/react";
|
|
3
|
+
import { useAccount, useSignMessage } from "wagmi";
|
|
4
|
+
import { getUserOperationHash } from "viem/account-abstraction";
|
|
5
|
+
|
|
6
|
+
import { BodyWrapper } from "../../ui/styled";
|
|
7
|
+
import { Button, Input } from "../../ui";
|
|
8
|
+
import QuoteParameters from "../../QuoteParameters";
|
|
9
|
+
import { TransactionDetailRow } from "../../TransactionDetailRow";
|
|
10
|
+
import { useAppDetails, useRouteData } from "@/util/enso-hooks";
|
|
11
|
+
import { ENTRY_POINT_ADDRESS } from "@/util/constants";
|
|
12
|
+
|
|
13
|
+
const SignUserOpStep = ({
|
|
14
|
+
setStep,
|
|
15
|
+
setUserOp,
|
|
16
|
+
nextStep,
|
|
17
|
+
}: {
|
|
18
|
+
nextStep: number;
|
|
19
|
+
setStep: (step: number) => void;
|
|
20
|
+
setUserOp: (userOp: any) => void;
|
|
21
|
+
}) => {
|
|
22
|
+
const { chainIdIn } = useAppDetails();
|
|
23
|
+
const { routeLoading, usdAmountIn, routeData } = useRouteData();
|
|
24
|
+
const { signMessageAsync } = useSignMessage();
|
|
25
|
+
const { address } = useAccount();
|
|
26
|
+
const [isSigning, setIsSigning] = useState(false);
|
|
27
|
+
|
|
28
|
+
const handleSignUserOp = async () => {
|
|
29
|
+
if (!routeData || (routeData as any)?.error) {
|
|
30
|
+
console.error("No valid router data available");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
setIsSigning(true);
|
|
36
|
+
|
|
37
|
+
const userOperation = routeData?.userOp;
|
|
38
|
+
if (!userOperation) {
|
|
39
|
+
console.error("No userOperation found in routeData");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log("Signing userOperation:", userOperation);
|
|
44
|
+
|
|
45
|
+
const userOpHash = getUserOperationHash({
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
userOperation,
|
|
48
|
+
entryPointAddress: ENTRY_POINT_ADDRESS,
|
|
49
|
+
entryPointVersion: "0.7",
|
|
50
|
+
chainId: chainIdIn,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const signature = await signMessageAsync({
|
|
54
|
+
account: address as `0x${string}`,
|
|
55
|
+
message: { raw: userOpHash as `0x${string}` },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const signedUserOp = {
|
|
59
|
+
...userOperation,
|
|
60
|
+
signature,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
console.log("signedUserOp", JSON.stringify(signedUserOp));
|
|
64
|
+
|
|
65
|
+
setUserOp(signedUserOp);
|
|
66
|
+
setStep(nextStep);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("Failed to sign userOperation:", error);
|
|
69
|
+
} finally {
|
|
70
|
+
setIsSigning(false);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<BodyWrapper>
|
|
76
|
+
<Flex
|
|
77
|
+
flexDirection={"column"}
|
|
78
|
+
gap={"16px"}
|
|
79
|
+
alignItems={"center"}
|
|
80
|
+
width={"100%"}
|
|
81
|
+
>
|
|
82
|
+
<Skeleton
|
|
83
|
+
loading={routeLoading}
|
|
84
|
+
width={routeLoading ? "156px" : "auto"}
|
|
85
|
+
>
|
|
86
|
+
<Input
|
|
87
|
+
readOnly
|
|
88
|
+
marginY={"8px"}
|
|
89
|
+
variant={"text"}
|
|
90
|
+
placeholder={"$10.00"}
|
|
91
|
+
value={usdAmountIn}
|
|
92
|
+
/>
|
|
93
|
+
</Skeleton>
|
|
94
|
+
|
|
95
|
+
<QuoteParameters />
|
|
96
|
+
</Flex>
|
|
97
|
+
|
|
98
|
+
<TransactionDetailRow />
|
|
99
|
+
|
|
100
|
+
<Button
|
|
101
|
+
disabled={
|
|
102
|
+
!!(routeData as any)?.message || routeLoading || isSigning
|
|
103
|
+
}
|
|
104
|
+
loading={routeLoading || isSigning}
|
|
105
|
+
onClick={handleSignUserOp}
|
|
106
|
+
>
|
|
107
|
+
{routeLoading
|
|
108
|
+
? "Loading quote"
|
|
109
|
+
: isSigning
|
|
110
|
+
? "Signing..."
|
|
111
|
+
: "Sign Transaction"}
|
|
112
|
+
</Button>
|
|
113
|
+
</BodyWrapper>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default SignUserOpStep;
|