thirdweb 5.87.4 → 5.88.0

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.
Files changed (149) hide show
  1. package/dist/cjs/client/client.js +2 -1
  2. package/dist/cjs/client/client.js.map +1 -1
  3. package/dist/cjs/contract/deployment/zksync/zkDeployContract.js +4 -2
  4. package/dist/cjs/contract/deployment/zksync/zkDeployContract.js.map +1 -1
  5. package/dist/cjs/pay/buyWithCrypto/getQuote.js +1 -24
  6. package/dist/cjs/pay/buyWithCrypto/getQuote.js.map +1 -1
  7. package/dist/cjs/pay/buyWithCrypto/getTransfer.js +1 -13
  8. package/dist/cjs/pay/buyWithCrypto/getTransfer.js.map +1 -1
  9. package/dist/cjs/pay/buyWithFiat/getQuote.js.map +1 -1
  10. package/dist/cjs/pay/utils/commonTypes.js +2 -0
  11. package/dist/cjs/pay/utils/commonTypes.js.map +1 -1
  12. package/dist/cjs/react/core/utils/storage.js +2 -1
  13. package/dist/cjs/react/core/utils/storage.js.map +1 -1
  14. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +50 -200
  15. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
  16. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.js +111 -0
  17. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.js.map +1 -0
  18. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.js +30 -2
  19. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.js.map +1 -1
  20. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.js +24 -0
  21. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.js.map +1 -0
  22. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/BuyTokenInput.js +2 -1
  23. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/BuyTokenInput.js.map +1 -1
  24. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js +18 -6
  25. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js.map +1 -1
  26. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.js +37 -0
  27. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.js.map +1 -0
  28. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.js +1 -1
  29. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.js.map +1 -1
  30. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.js +165 -0
  31. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.js.map +1 -0
  32. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.js +33 -6
  33. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.js.map +1 -1
  34. package/dist/cjs/react/web/ui/ConnectWallet/screens/nativeToken.js +8 -0
  35. package/dist/cjs/react/web/ui/ConnectWallet/screens/nativeToken.js.map +1 -1
  36. package/dist/cjs/react/web/utils/errors.js +16 -0
  37. package/dist/cjs/react/web/utils/errors.js.map +1 -0
  38. package/dist/cjs/version.js +1 -1
  39. package/dist/cjs/wallets/smart/index.js +27 -0
  40. package/dist/cjs/wallets/smart/index.js.map +1 -1
  41. package/dist/cjs/wallets/smart/types.js.map +1 -1
  42. package/dist/esm/client/client.js +2 -1
  43. package/dist/esm/client/client.js.map +1 -1
  44. package/dist/esm/contract/deployment/zksync/zkDeployContract.js +4 -2
  45. package/dist/esm/contract/deployment/zksync/zkDeployContract.js.map +1 -1
  46. package/dist/esm/pay/buyWithCrypto/getQuote.js +1 -24
  47. package/dist/esm/pay/buyWithCrypto/getQuote.js.map +1 -1
  48. package/dist/esm/pay/buyWithCrypto/getTransfer.js +1 -13
  49. package/dist/esm/pay/buyWithCrypto/getTransfer.js.map +1 -1
  50. package/dist/esm/pay/buyWithFiat/getQuote.js.map +1 -1
  51. package/dist/esm/pay/utils/commonTypes.js +1 -1
  52. package/dist/esm/pay/utils/commonTypes.js.map +1 -1
  53. package/dist/esm/react/core/utils/storage.js +1 -0
  54. package/dist/esm/react/core/utils/storage.js.map +1 -1
  55. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +50 -200
  56. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
  57. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.js +108 -0
  58. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.js.map +1 -0
  59. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.js +30 -2
  60. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.js.map +1 -1
  61. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.js +21 -0
  62. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.js.map +1 -0
  63. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/BuyTokenInput.js +2 -1
  64. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/BuyTokenInput.js.map +1 -1
  65. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js +18 -6
  66. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js.map +1 -1
  67. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.js +34 -0
  68. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.js.map +1 -0
  69. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.js +1 -1
  70. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.js.map +1 -1
  71. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.js +162 -0
  72. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.js.map +1 -0
  73. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.js +33 -6
  74. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.js.map +1 -1
  75. package/dist/esm/react/web/ui/ConnectWallet/screens/nativeToken.js +7 -0
  76. package/dist/esm/react/web/ui/ConnectWallet/screens/nativeToken.js.map +1 -1
  77. package/dist/esm/react/web/utils/errors.js +12 -0
  78. package/dist/esm/react/web/utils/errors.js.map +1 -0
  79. package/dist/esm/version.js +1 -1
  80. package/dist/esm/wallets/smart/index.js +27 -0
  81. package/dist/esm/wallets/smart/index.js.map +1 -1
  82. package/dist/esm/wallets/smart/types.js.map +1 -1
  83. package/dist/types/client/client.d.ts.map +1 -1
  84. package/dist/types/contract/deployment/zksync/zkDeployContract.d.ts.map +1 -1
  85. package/dist/types/pay/buyWithCrypto/getQuote.d.ts +2 -2
  86. package/dist/types/pay/buyWithCrypto/getQuote.d.ts.map +1 -1
  87. package/dist/types/pay/buyWithCrypto/getTransfer.d.ts +2 -2
  88. package/dist/types/pay/buyWithCrypto/getTransfer.d.ts.map +1 -1
  89. package/dist/types/pay/buyWithFiat/getQuote.d.ts +4 -0
  90. package/dist/types/pay/buyWithFiat/getQuote.d.ts.map +1 -1
  91. package/dist/types/pay/utils/commonTypes.d.ts +2 -1
  92. package/dist/types/pay/utils/commonTypes.d.ts.map +1 -1
  93. package/dist/types/react/core/utils/storage.d.ts +1 -0
  94. package/dist/types/react/core/utils/storage.d.ts.map +1 -1
  95. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.d.ts.map +1 -1
  96. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.d.ts +25 -0
  97. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.d.ts.map +1 -0
  98. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.d.ts.map +1 -1
  99. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.d.ts +9 -0
  100. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.d.ts.map +1 -0
  101. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/types.d.ts +1 -0
  102. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/types.d.ts.map +1 -1
  103. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/BuyTokenInput.d.ts.map +1 -1
  104. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.d.ts +1 -0
  105. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.d.ts.map +1 -1
  106. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.d.ts +11 -0
  107. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.d.ts.map +1 -0
  108. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.d.ts +1 -0
  109. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.d.ts.map +1 -1
  110. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.d.ts +31 -0
  111. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.d.ts.map +1 -0
  112. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.d.ts.map +1 -1
  113. package/dist/types/react/web/ui/ConnectWallet/screens/nativeToken.d.ts +2 -0
  114. package/dist/types/react/web/ui/ConnectWallet/screens/nativeToken.d.ts.map +1 -1
  115. package/dist/types/react/web/ui/components/text.d.ts +1 -1
  116. package/dist/types/react/web/ui/components/text.d.ts.map +1 -1
  117. package/dist/types/react/web/utils/errors.d.ts +14 -0
  118. package/dist/types/react/web/utils/errors.d.ts.map +1 -0
  119. package/dist/types/version.d.ts +1 -1
  120. package/dist/types/wallets/smart/index.d.ts.map +1 -1
  121. package/dist/types/wallets/smart/types.d.ts +5 -0
  122. package/dist/types/wallets/smart/types.d.ts.map +1 -1
  123. package/package.json +1 -1
  124. package/src/client/client.test.ts +2 -2
  125. package/src/client/client.ts +2 -1
  126. package/src/contract/deployment/zksync/zkDeployContract.ts +5 -2
  127. package/src/pay/buyWithCrypto/getQuote.ts +2 -27
  128. package/src/pay/buyWithCrypto/getTransfer.ts +2 -14
  129. package/src/pay/buyWithFiat/getQuote.ts +5 -0
  130. package/src/pay/utils/commonTypes.ts +3 -1
  131. package/src/react/core/utils/storage.ts +1 -0
  132. package/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +70 -531
  133. package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatScreenContent.tsx +307 -0
  134. package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/PostOnRampSwap.tsx +33 -1
  135. package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/Providers.tsx +57 -0
  136. package/src/react/web/ui/ConnectWallet/screens/Buy/main/types.ts +1 -0
  137. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/BuyTokenInput.tsx +12 -2
  138. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx +21 -6
  139. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.tsx +51 -0
  140. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapFlow.tsx +2 -0
  141. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/SwapScreenContent.tsx +353 -0
  142. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx +43 -6
  143. package/src/react/web/ui/ConnectWallet/screens/nativeToken.ts +8 -0
  144. package/src/react/web/ui/components/text.tsx +1 -1
  145. package/src/react/web/utils/errors.ts +22 -0
  146. package/src/version.ts +1 -1
  147. package/src/wallets/smart/index.ts +28 -0
  148. package/src/wallets/smart/smart-wallet-integration.test.ts +25 -0
  149. package/src/wallets/smart/types.ts +5 -0
@@ -0,0 +1,307 @@
1
+ import { ChevronDownIcon } from "@radix-ui/react-icons";
2
+ import { useState } from "react";
3
+ import type { Chain } from "../../../../../../../chains/types.js";
4
+ import type { ThirdwebClient } from "../../../../../../../client/client.js";
5
+ import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
6
+ import { isSwapRequiredPostOnramp } from "../../../../../../../pay/buyWithFiat/isSwapRequiredPostOnramp.js";
7
+ import type { FiatProvider } from "../../../../../../../pay/utils/commonTypes.js";
8
+ import { formatNumber } from "../../../../../../../utils/formatNumber.js";
9
+ import {
10
+ type Theme,
11
+ iconSize,
12
+ spacing,
13
+ } from "../../../../../../core/design-system/index.js";
14
+ import type { PayUIOptions } from "../../../../../../core/hooks/connection/ConnectButtonProps.js";
15
+ import { useBuyWithFiatQuote } from "../../../../../../core/hooks/pay/useBuyWithFiatQuote.js";
16
+ import { PREFERRED_FIAT_PROVIDER_STORAGE_KEY } from "../../../../../../core/utils/storage.js";
17
+ import {
18
+ defaultMessage,
19
+ getErrorMessage,
20
+ } from "../../../../../utils/errors.js";
21
+ import {
22
+ Drawer,
23
+ DrawerOverlay,
24
+ useDrawer,
25
+ } from "../../../../components/Drawer.js";
26
+ import { Spacer } from "../../../../components/Spacer.js";
27
+ import { Spinner } from "../../../../components/Spinner.js";
28
+ import { Container } from "../../../../components/basic.js";
29
+ import { Button } from "../../../../components/buttons.js";
30
+ import { Text } from "../../../../components/text.js";
31
+ import { TokenSymbol } from "../../../../components/token/TokenSymbol.js";
32
+ import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js";
33
+ import { EstimatedTimeAndFees } from "../EstimatedTimeAndFees.js";
34
+ import { PayWithCreditCard } from "../PayWIthCreditCard.js";
35
+ import type { SelectedScreen } from "../main/types.js";
36
+ import { openOnrampPopup } from "../openOnRamppopup.js";
37
+ import { FiatFees } from "../swap/Fees.js";
38
+ import { addPendingTx } from "../swap/pendingSwapTx.js";
39
+ import type { PayerInfo } from "../types.js";
40
+ import { Providers } from "./Providers.js";
41
+ import type { CurrencyMeta } from "./currencies.js";
42
+
43
+ export function FiatScreenContent(props: {
44
+ setScreen: (screen: SelectedScreen) => void;
45
+ tokenAmount: string;
46
+ toToken: ERC20OrNativeToken;
47
+ toChain: Chain;
48
+ selectedCurrency: CurrencyMeta;
49
+ showCurrencySelector: () => void;
50
+ payOptions: PayUIOptions;
51
+ theme: "light" | "dark" | Theme;
52
+ client: ThirdwebClient;
53
+ onDone: () => void;
54
+ isEmbed: boolean;
55
+ payer: PayerInfo;
56
+ setTokenAmount: (amount: string) => void;
57
+ setHasEditedAmount: (hasEdited: boolean) => void;
58
+ }) {
59
+ const {
60
+ toToken,
61
+ tokenAmount,
62
+ payer,
63
+ client,
64
+ setScreen,
65
+ toChain,
66
+ showCurrencySelector,
67
+ selectedCurrency,
68
+ } = props;
69
+ const defaultRecipientAddress = (
70
+ props.payOptions as Extract<PayUIOptions, { mode: "direct_payment" }>
71
+ )?.paymentInfo?.sellerAddress;
72
+ const receiverAddress =
73
+ defaultRecipientAddress || props.payer.account.address;
74
+ const { drawerRef, drawerOverlayRef, isOpen, setIsOpen } = useDrawer();
75
+ const [drawerScreen, setDrawerScreen] = useState<"fees" | "providers">(
76
+ "fees",
77
+ );
78
+
79
+ const buyWithFiatOptions = props.payOptions.buyWithFiat;
80
+ const [preferredProvider, setPreferredProvider] = useState<
81
+ FiatProvider | undefined
82
+ >(
83
+ buyWithFiatOptions !== false
84
+ ? buyWithFiatOptions?.preferredProvider ||
85
+ ((localStorage.getItem(
86
+ PREFERRED_FIAT_PROVIDER_STORAGE_KEY,
87
+ ) as FiatProvider | null) ??
88
+ undefined)
89
+ : undefined,
90
+ );
91
+
92
+ const fiatQuoteQuery = useBuyWithFiatQuote(
93
+ buyWithFiatOptions !== false && tokenAmount
94
+ ? {
95
+ fromCurrencySymbol: selectedCurrency.shorthand,
96
+ toChainId: toChain.id,
97
+ toAddress: receiverAddress,
98
+ toTokenAddress: isNativeToken(toToken)
99
+ ? NATIVE_TOKEN_ADDRESS
100
+ : toToken.address,
101
+ toAmount: tokenAmount,
102
+ client,
103
+ isTestMode: buyWithFiatOptions?.testMode,
104
+ purchaseData: props.payOptions.purchaseData,
105
+ fromAddress: payer.account.address,
106
+ preferredProvider: preferredProvider,
107
+ }
108
+ : undefined,
109
+ );
110
+
111
+ function handleSubmit() {
112
+ if (!fiatQuoteQuery.data) {
113
+ return;
114
+ }
115
+
116
+ const hasTwoSteps = isSwapRequiredPostOnramp(fiatQuoteQuery.data);
117
+ let openedWindow: Window | null = null;
118
+
119
+ if (!hasTwoSteps) {
120
+ openedWindow = openOnrampPopup(
121
+ fiatQuoteQuery.data.onRampLink,
122
+ typeof props.theme === "string" ? props.theme : props.theme.type,
123
+ );
124
+
125
+ addPendingTx({
126
+ type: "fiat",
127
+ intentId: fiatQuoteQuery.data.intentId,
128
+ });
129
+ }
130
+
131
+ setScreen({
132
+ id: "fiat-flow",
133
+ quote: fiatQuoteQuery.data,
134
+ openedWindow,
135
+ });
136
+ }
137
+
138
+ function showFees() {
139
+ if (!fiatQuoteQuery.data) {
140
+ return;
141
+ }
142
+
143
+ setDrawerScreen("fees");
144
+ setIsOpen(true);
145
+ }
146
+
147
+ function showProviders() {
148
+ setDrawerScreen("providers");
149
+ setIsOpen(true);
150
+ }
151
+
152
+ const disableSubmit = !fiatQuoteQuery.data;
153
+
154
+ const errorMsg =
155
+ !fiatQuoteQuery.isLoading && fiatQuoteQuery.error
156
+ ? getErrorMessage(fiatQuoteQuery.error)
157
+ : undefined;
158
+
159
+ return (
160
+ <Container flex="column" gap="md" animate="fadein">
161
+ {isOpen && (
162
+ <>
163
+ <DrawerOverlay ref={drawerOverlayRef} />
164
+ <Drawer ref={drawerRef} close={() => setIsOpen(false)}>
165
+ {drawerScreen === "fees" && fiatQuoteQuery.data && (
166
+ <div>
167
+ <Text size="lg" color="primaryText">
168
+ Fees
169
+ </Text>
170
+
171
+ <Spacer y="lg" />
172
+ <FiatFees quote={fiatQuoteQuery.data} />
173
+ </div>
174
+ )}
175
+ {drawerScreen === "providers" && (
176
+ <div>
177
+ <Text size="lg" color="primaryText">
178
+ Providers
179
+ </Text>
180
+ <Spacer y="lg" />
181
+ <Providers
182
+ preferredProvider={
183
+ preferredProvider || fiatQuoteQuery.data?.provider
184
+ }
185
+ onSelect={(provider) => {
186
+ setPreferredProvider(provider);
187
+ // save the pref in local storage
188
+ localStorage.setItem(
189
+ PREFERRED_FIAT_PROVIDER_STORAGE_KEY,
190
+ provider,
191
+ );
192
+ setIsOpen(false);
193
+ }}
194
+ />
195
+ </div>
196
+ )}
197
+ </Drawer>
198
+ </>
199
+ )}
200
+
201
+ <div>
202
+ <PayWithCreditCard
203
+ isLoading={fiatQuoteQuery.isLoading}
204
+ value={fiatQuoteQuery.data?.fromCurrencyWithFees.amount}
205
+ client={client}
206
+ currency={selectedCurrency}
207
+ onSelectCurrency={showCurrencySelector}
208
+ />
209
+ <Container
210
+ bg="tertiaryBg"
211
+ flex="row"
212
+ borderColor="borderColor"
213
+ style={{
214
+ paddingLeft: spacing.md,
215
+ justifyContent: "space-between",
216
+ alignItems: "center",
217
+ borderWidth: "1px",
218
+ borderStyle: "solid",
219
+ borderBottom: "none",
220
+ }}
221
+ >
222
+ <Text size="xs" color="secondaryText">
223
+ Provider
224
+ </Text>
225
+ <Button variant="ghost" onClick={showProviders}>
226
+ <Container flex="row" center="y" gap="xxs" color="secondaryText">
227
+ <Text size="xs">
228
+ {preferredProvider
229
+ ? `${preferredProvider.charAt(0).toUpperCase() + preferredProvider.slice(1).toLowerCase()}`
230
+ : fiatQuoteQuery.data?.provider
231
+ ? `${fiatQuoteQuery.data?.provider.charAt(0).toUpperCase() + fiatQuoteQuery.data?.provider.slice(1).toLowerCase()}`
232
+ : ""}
233
+ </Text>
234
+ <ChevronDownIcon width={iconSize.sm} height={iconSize.sm} />
235
+ </Container>
236
+ </Button>
237
+ </Container>
238
+ {/* Estimated time + View fees button */}
239
+ <EstimatedTimeAndFees
240
+ quoteIsLoading={fiatQuoteQuery.isLoading}
241
+ estimatedSeconds={fiatQuoteQuery.data?.estimatedDurationSeconds}
242
+ onViewFees={showFees}
243
+ />
244
+ <Spacer y="md" />
245
+ </div>
246
+
247
+ {/* Error message */}
248
+ {errorMsg && (
249
+ <div>
250
+ {errorMsg.data?.minimumAmountEth ? (
251
+ <Text color="danger" size="sm" center multiline>
252
+ Minimum amount is{" "}
253
+ {formatNumber(Number(errorMsg.data.minimumAmountEth), 6)}{" "}
254
+ <TokenSymbol
255
+ token={toToken}
256
+ chain={toChain}
257
+ size="sm"
258
+ inline
259
+ color="danger"
260
+ />
261
+ </Text>
262
+ ) : (
263
+ <Text color="danger" size="sm" center multiline>
264
+ {errorMsg.message || defaultMessage}
265
+ </Text>
266
+ )}
267
+ </div>
268
+ )}
269
+
270
+ {errorMsg?.data?.minimumAmountEth ? (
271
+ <Button
272
+ variant="accent"
273
+ fullWidth
274
+ onClick={() => {
275
+ props.setTokenAmount(
276
+ formatNumber(
277
+ Number(errorMsg.data?.minimumAmountEth),
278
+ 6,
279
+ ).toString(),
280
+ );
281
+ props.setHasEditedAmount(true);
282
+ }}
283
+ >
284
+ Set Minimum
285
+ </Button>
286
+ ) : (
287
+ <Button
288
+ variant={disableSubmit ? "outline" : "accent"}
289
+ data-disabled={disableSubmit}
290
+ disabled={disableSubmit}
291
+ fullWidth
292
+ onClick={handleSubmit}
293
+ gap="xs"
294
+ >
295
+ {fiatQuoteQuery.isLoading ? (
296
+ <>
297
+ Getting price quote
298
+ <Spinner size="sm" color="accentText" />
299
+ </>
300
+ ) : (
301
+ "Continue"
302
+ )}
303
+ </Button>
304
+ )}
305
+ </Container>
306
+ );
307
+ }
@@ -1,6 +1,9 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { useEffect, useState } from "react";
3
+ import { getCachedChain } from "../../../../../../../chains/utils.js";
3
4
  import type { ThirdwebClient } from "../../../../../../../client/client.js";
5
+ import { getContract } from "../../../../../../../contract/contract.js";
6
+ import { allowance } from "../../../../../../../extensions/erc20/__generated__/IERC20/read/allowance.js";
4
7
  import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
5
8
  import type { BuyWithCryptoStatus } from "../../../../../../../pay/buyWithCrypto/getStatus.js";
6
9
  import { getPostOnRampQuote } from "../../../../../../../pay/buyWithFiat/getPostOnRampQuote.js";
@@ -43,11 +46,38 @@ export function PostOnRampSwap(props: {
43
46
  refetchOnWindowFocus: false,
44
47
  });
45
48
 
49
+ const allowanceQuery = useQuery({
50
+ queryKey: [
51
+ "allowance",
52
+ props.payer.account.address,
53
+ postOnRampQuoteQuery.data?.approvalData,
54
+ ],
55
+ queryFn: () => {
56
+ if (!postOnRampQuoteQuery.data?.approvalData) {
57
+ return null;
58
+ }
59
+ return allowance({
60
+ contract: getContract({
61
+ client: props.client,
62
+ address: postOnRampQuoteQuery.data.swapDetails.fromToken.tokenAddress,
63
+ chain: getCachedChain(
64
+ postOnRampQuoteQuery.data.swapDetails.fromToken.chainId,
65
+ ),
66
+ }),
67
+ spender: postOnRampQuoteQuery.data.approvalData.spenderAddress,
68
+ owner: props.payer.account.address,
69
+ });
70
+ },
71
+ enabled: !!postOnRampQuoteQuery.data?.approvalData,
72
+ refetchOnMount: true,
73
+ });
74
+
46
75
  useEffect(() => {
47
76
  if (
48
77
  postOnRampQuoteQuery.data &&
49
78
  !lockedOnRampQuote &&
50
- !postOnRampQuoteQuery.isRefetching
79
+ !postOnRampQuoteQuery.isRefetching &&
80
+ !allowanceQuery.isLoading
51
81
  ) {
52
82
  setLockedOnRampQuote(postOnRampQuoteQuery.data);
53
83
  }
@@ -55,6 +85,7 @@ export function PostOnRampSwap(props: {
55
85
  postOnRampQuoteQuery.data,
56
86
  lockedOnRampQuote,
57
87
  postOnRampQuoteQuery.isRefetching,
88
+ allowanceQuery.isLoading,
58
89
  ]);
59
90
 
60
91
  if (postOnRampQuoteQuery.isError) {
@@ -133,6 +164,7 @@ export function PostOnRampSwap(props: {
133
164
  transactionMode={props.transactionMode}
134
165
  isEmbed={props.isEmbed}
135
166
  onSuccess={props.onSuccess}
167
+ approvalAmount={allowanceQuery.data ?? undefined}
136
168
  />
137
169
  );
138
170
  }
@@ -0,0 +1,57 @@
1
+ import {
2
+ type FiatProvider,
3
+ FiatProviders,
4
+ } from "../../../../../../../pay/utils/commonTypes.js";
5
+ import { Container } from "../../../../components/basic.js";
6
+ import { Button } from "../../../../components/buttons.js";
7
+ import { Link } from "../../../../components/text.js";
8
+ /**
9
+ * @internal
10
+ */
11
+ export function Providers(props: {
12
+ preferredProvider?: FiatProvider;
13
+ onSelect: (provider: FiatProvider) => void;
14
+ }) {
15
+ return (
16
+ <Container
17
+ expand
18
+ flex="column"
19
+ gap="sm"
20
+ style={{
21
+ alignItems: "flex-start",
22
+ }}
23
+ >
24
+ {FiatProviders.map((provider) => {
25
+ return (
26
+ <Container
27
+ key={provider}
28
+ flex="row"
29
+ expand
30
+ style={{
31
+ justifyContent: "space-between",
32
+ }}
33
+ >
34
+ <Button
35
+ fullWidth
36
+ onClick={() => props.onSelect(provider)}
37
+ variant={"link"}
38
+ >
39
+ <Link
40
+ color={
41
+ props.preferredProvider === provider
42
+ ? "primaryText"
43
+ : "secondaryText"
44
+ }
45
+ size="sm"
46
+ hoverColor="primaryText"
47
+ >
48
+ {provider.charAt(0).toUpperCase() +
49
+ provider.slice(1).toLowerCase()}
50
+ </Link>
51
+ </Button>
52
+ </Container>
53
+ );
54
+ })}
55
+ </Container>
56
+ );
57
+ }
@@ -34,6 +34,7 @@ export type SelectedScreen =
34
34
  | {
35
35
  id: "swap-flow";
36
36
  quote: BuyWithCryptoQuote;
37
+ approvalAmount?: bigint;
37
38
  }
38
39
  | {
39
40
  id: "fiat-flow";
@@ -19,6 +19,7 @@ import { Text } from "../../../../components/text.js";
19
19
  import { TokenSymbol } from "../../../../components/token/TokenSymbol.js";
20
20
  import type { ERC20OrNativeToken } from "../../nativeToken.js";
21
21
  import { getBuyTokenAmountFontSize } from "../utils.js";
22
+ import { FiatValue } from "./FiatValue.js";
22
23
 
23
24
  /**
24
25
  * @internal
@@ -35,7 +36,6 @@ export function BuyTokenInput(props: {
35
36
  freezeChainAndToken?: boolean;
36
37
  }) {
37
38
  const { name } = useChainName(props.chain);
38
-
39
39
  const getWidth = () => {
40
40
  let chars = props.value.replace(".", "").length;
41
41
  const hasDot = props.value.includes(".");
@@ -122,9 +122,19 @@ export function BuyTokenInput(props: {
122
122
  </Container>
123
123
  </div>
124
124
 
125
+ <Container flex="row" center="both">
126
+ <FiatValue
127
+ tokenAmount={props.value}
128
+ token={props.token}
129
+ chain={props.chain}
130
+ client={props.client}
131
+ size="md"
132
+ />
133
+ </Container>
134
+
125
135
  {!props.hideTokenSelector && (
126
136
  <>
127
- <Spacer y="sm" />
137
+ <Spacer y="md" />
128
138
 
129
139
  {/* Token / Chain selector */}
130
140
  <Container flex="row" center="x">
@@ -3,6 +3,8 @@ import { useState } from "react";
3
3
  import { trackPayEvent } from "../../../../../../../analytics/track/pay.js";
4
4
  import type { Chain } from "../../../../../../../chains/types.js";
5
5
  import type { ThirdwebClient } from "../../../../../../../client/client.js";
6
+ import { getContract } from "../../../../../../../contract/contract.js";
7
+ import { approve } from "../../../../../../../extensions/erc20/write/approve.js";
6
8
  import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
7
9
  import { sendTransaction } from "../../../../../../../transaction/actions/send-transaction.js";
8
10
  import { waitForReceipt } from "../../../../../../../transaction/actions/wait-for-tx-receipt.js";
@@ -51,9 +53,13 @@ export function SwapConfirmationScreen(props: {
51
53
  fromTokenSymbol: string;
52
54
  isFiatFlow: boolean;
53
55
  payer: PayerInfo;
56
+ preApprovedAmount?: bigint;
54
57
  }) {
55
- const isApprovalRequired = props.quote.approval !== undefined;
56
- const initialStep = isApprovalRequired ? "approval" : "swap";
58
+ const needsApproval =
59
+ props.quote.approvalData &&
60
+ props.preApprovedAmount !== undefined &&
61
+ props.preApprovedAmount < BigInt(props.quote.approvalData.amountWei);
62
+ const initialStep = needsApproval ? "approval" : "swap";
57
63
 
58
64
  const [step, setStep] = useState<"approval" | "swap">(initialStep);
59
65
  const [status, setStatus] = useState<
@@ -136,7 +142,7 @@ export function SwapConfirmationScreen(props: {
136
142
  <Spacer y="xl" />
137
143
 
138
144
  {/* Show 2 steps - Approve and confirm */}
139
- {isApprovalRequired && (
145
+ {needsApproval && (
140
146
  <>
141
147
  <Spacer y="sm" />
142
148
  <Container
@@ -187,7 +193,7 @@ export function SwapConfirmationScreen(props: {
187
193
  fullWidth
188
194
  disabled={status === "pending"}
189
195
  onClick={async () => {
190
- if (step === "approval" && props.quote.approval) {
196
+ if (step === "approval" && props.quote.approvalData) {
191
197
  try {
192
198
  setStatus("pending");
193
199
 
@@ -204,13 +210,22 @@ export function SwapConfirmationScreen(props: {
204
210
  dstChainId: props.quote.swapDetails.toToken.chainId,
205
211
  });
206
212
 
213
+ const transaction = approve({
214
+ contract: getContract({
215
+ client: props.client,
216
+ address: props.quote.swapDetails.fromToken.tokenAddress,
217
+ chain: props.fromChain,
218
+ }),
219
+ spender: props.quote.approvalData.spenderAddress,
220
+ amountWei: BigInt(props.quote.approvalData.amountWei),
221
+ });
222
+
207
223
  const tx = await sendTransaction({
208
224
  account: props.payer.account,
209
- transaction: props.quote.approval,
225
+ transaction,
210
226
  });
211
227
 
212
228
  await waitForReceipt({ ...tx, maxBlocksWaitTime: 50 });
213
- // props.onQuoteFinalized(props.quote);
214
229
 
215
230
  trackPayEvent({
216
231
  event: "swap_approval_success",
@@ -0,0 +1,51 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import type { Chain } from "../../../../../../../chains/types.js";
3
+ import type { ThirdwebClient } from "../../../../../../../client/client.js";
4
+ import { convertCryptoToFiat } from "../../../../../../../pay/convert/cryptoToFiat.js";
5
+ import { formatNumber } from "../../../../../../../utils/formatNumber.js";
6
+ import { fontSize } from "../../../../../../core/design-system/index.js";
7
+ import { Skeleton } from "../../../../components/Skeleton.js";
8
+ import { Text } from "../../../../components/text.js";
9
+ import type { TextProps } from "../../../../components/text.js";
10
+ import { useDebouncedValue } from "../../../../hooks/useDebouncedValue.js";
11
+ import type { ERC20OrNativeToken } from "../../nativeToken.js";
12
+ import { getTokenAddress } from "../../nativeToken.js";
13
+
14
+ export function FiatValue(
15
+ props: {
16
+ tokenAmount: string;
17
+ token: ERC20OrNativeToken;
18
+ chain: Chain;
19
+ client: ThirdwebClient;
20
+ } & TextProps,
21
+ ) {
22
+ const deferredTokenAmount = useDebouncedValue(props.tokenAmount, 500);
23
+ const cryptoToFiatQuery = useQuery({
24
+ queryKey: [
25
+ "cryptoToFiat",
26
+ props.chain.id,
27
+ getTokenAddress(props.token),
28
+ deferredTokenAmount,
29
+ ],
30
+ queryFn: () =>
31
+ convertCryptoToFiat({
32
+ client: props.client,
33
+ chain: props.chain,
34
+ fromTokenAddress: getTokenAddress(props.token),
35
+ fromAmount: Number(deferredTokenAmount),
36
+ to: "USD",
37
+ }),
38
+ });
39
+
40
+ if (cryptoToFiatQuery.isLoading) {
41
+ return <Skeleton width={"50px"} height={fontSize.lg} />;
42
+ }
43
+
44
+ return (
45
+ <Text {...props}>
46
+ {cryptoToFiatQuery.data?.result
47
+ ? `$${formatNumber(cryptoToFiatQuery.data.result, 2)}`
48
+ : "$0.00"}
49
+ </Text>
50
+ );
51
+ }
@@ -22,6 +22,7 @@ type SwapFlowProps = {
22
22
  transactionMode: boolean;
23
23
  isEmbed: boolean;
24
24
  onSuccess: ((status: BuyWithCryptoStatus) => void) | undefined;
25
+ approvalAmount?: bigint;
25
26
  };
26
27
 
27
28
  export function SwapFlow(props: SwapFlowProps) {
@@ -109,6 +110,7 @@ export function SwapFlow(props: SwapFlowProps) {
109
110
  quote={quote}
110
111
  isFiatFlow={props.isFiatFlow}
111
112
  payer={props.payer}
113
+ preApprovedAmount={props.approvalAmount}
112
114
  />
113
115
  );
114
116
  }