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,353 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { useState } from "react";
3
+ import type { Chain } from "../../../../../../../chains/types.js";
4
+ import { getCachedChain } from "../../../../../../../chains/utils.js";
5
+ import type { ThirdwebClient } from "../../../../../../../client/client.js";
6
+ import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
7
+ import { getContract } from "../../../../../../../contract/contract.js";
8
+ import { allowance } from "../../../../../../../extensions/erc20/__generated__/IERC20/read/allowance.js";
9
+ import type { GetBuyWithCryptoQuoteParams } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
10
+ import { formatNumber } from "../../../../../../../utils/formatNumber.js";
11
+ import type { Account } from "../../../../../../../wallets/interfaces/wallet.js";
12
+ import type { PayUIOptions } from "../../../../../../core/hooks/connection/ConnectButtonProps.js";
13
+ import { useWalletBalance } from "../../../../../../core/hooks/others/useWalletBalance.js";
14
+ import { useBuyWithCryptoQuote } from "../../../../../../core/hooks/pay/useBuyWithCryptoQuote.js";
15
+ import {
16
+ defaultMessage,
17
+ getErrorMessage,
18
+ } from "../../../../../utils/errors.js";
19
+ import type { PayEmbedConnectOptions } from "../../../../PayEmbed.js";
20
+ import {
21
+ Drawer,
22
+ DrawerOverlay,
23
+ useDrawer,
24
+ } from "../../../../components/Drawer.js";
25
+ import { Spacer } from "../../../../components/Spacer.js";
26
+ import { Spinner } from "../../../../components/Spinner.js";
27
+ import { SwitchNetworkButton } from "../../../../components/SwitchNetwork.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 { ConnectLocale } from "../../../locale/types.js";
33
+ import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js";
34
+ import { EstimatedTimeAndFees } from "../EstimatedTimeAndFees.js";
35
+ import type { SelectedScreen } from "../main/types.js";
36
+ import type { PayerInfo } from "../types.js";
37
+ import { SwapFees } from "./Fees.js";
38
+ import { PayWithCryptoQuoteInfo } from "./PayWithCrypto.js";
39
+
40
+ export function SwapScreenContent(props: {
41
+ setScreen: (screen: SelectedScreen) => void;
42
+ tokenAmount: string;
43
+ toToken: ERC20OrNativeToken;
44
+ toChain: Chain;
45
+ fromChain: Chain;
46
+ fromToken: ERC20OrNativeToken;
47
+ showFromTokenSelector: () => void;
48
+ payer: PayerInfo;
49
+ client: ThirdwebClient;
50
+ payOptions: PayUIOptions;
51
+ isEmbed: boolean;
52
+ onDone: () => void;
53
+ connectOptions: PayEmbedConnectOptions | undefined;
54
+ connectLocale: ConnectLocale;
55
+ setPayer: (payer: PayerInfo) => void;
56
+ activeAccount: Account;
57
+ setTokenAmount: (amount: string) => void;
58
+ setHasEditedAmount: (hasEdited: boolean) => void;
59
+ disableTokenSelection: boolean;
60
+ }) {
61
+ const {
62
+ setScreen,
63
+ payer,
64
+ client,
65
+ toChain,
66
+ tokenAmount,
67
+ toToken,
68
+ fromChain,
69
+ fromToken,
70
+ payOptions,
71
+ disableTokenSelection,
72
+ } = props;
73
+
74
+ const defaultRecipientAddress = (
75
+ props.payOptions as Extract<PayUIOptions, { mode: "direct_payment" }>
76
+ )?.paymentInfo?.sellerAddress;
77
+ const receiverAddress =
78
+ defaultRecipientAddress || props.activeAccount.address;
79
+ const { drawerRef, drawerOverlayRef, isOpen, setIsOpen } = useDrawer();
80
+ const [drawerScreen, setDrawerScreen] = useState<
81
+ "fees" | "receiver" | "payer"
82
+ >("fees");
83
+
84
+ const fromTokenBalanceQuery = useWalletBalance({
85
+ address: payer.account.address,
86
+ chain: fromChain,
87
+ tokenAddress: isNativeToken(fromToken) ? undefined : fromToken.address,
88
+ client,
89
+ });
90
+
91
+ const fromTokenId = isNativeToken(fromToken)
92
+ ? NATIVE_TOKEN_ADDRESS
93
+ : fromToken.address.toLowerCase();
94
+ const toTokenId = isNativeToken(toToken)
95
+ ? NATIVE_TOKEN_ADDRESS
96
+ : toToken.address.toLowerCase();
97
+ const swapRequired =
98
+ !!tokenAmount &&
99
+ !(fromChain.id === toChain.id && fromTokenId === toTokenId);
100
+ const quoteParams: GetBuyWithCryptoQuoteParams | undefined = swapRequired
101
+ ? {
102
+ // wallets
103
+ fromAddress: payer.account.address,
104
+ toAddress: receiverAddress,
105
+ // from
106
+ fromChainId: fromChain.id,
107
+ fromTokenAddress: isNativeToken(fromToken)
108
+ ? NATIVE_TOKEN_ADDRESS
109
+ : fromToken.address,
110
+ // to
111
+ toChainId: toChain.id,
112
+ toTokenAddress: isNativeToken(toToken)
113
+ ? NATIVE_TOKEN_ADDRESS
114
+ : toToken.address,
115
+ toAmount: tokenAmount,
116
+ client,
117
+ purchaseData: payOptions.purchaseData,
118
+ }
119
+ : undefined;
120
+
121
+ const quoteQuery = useBuyWithCryptoQuote(quoteParams, {
122
+ // refetch every 30 seconds
123
+ staleTime: 30 * 1000,
124
+ refetchInterval: 30 * 1000,
125
+ gcTime: 30 * 1000,
126
+ });
127
+
128
+ const allowanceQuery = useQuery({
129
+ queryKey: [
130
+ "allowance",
131
+ payer.account.address,
132
+ quoteQuery.data?.approvalData,
133
+ ],
134
+ queryFn: () => {
135
+ if (!quoteQuery.data?.approvalData) {
136
+ return null;
137
+ }
138
+ return allowance({
139
+ contract: getContract({
140
+ client: props.client,
141
+ address: quoteQuery.data.swapDetails.fromToken.tokenAddress,
142
+ chain: getCachedChain(quoteQuery.data.swapDetails.fromToken.chainId),
143
+ }),
144
+ spender: quoteQuery.data.approvalData.spenderAddress,
145
+ owner: props.payer.account.address,
146
+ });
147
+ },
148
+ enabled: !!quoteQuery.data?.approvalData,
149
+ refetchOnMount: true,
150
+ });
151
+
152
+ const sourceTokenAmount = swapRequired
153
+ ? quoteQuery.data?.swapDetails.fromAmount
154
+ : tokenAmount;
155
+
156
+ const isNotEnoughBalance =
157
+ !!sourceTokenAmount &&
158
+ !!fromTokenBalanceQuery.data &&
159
+ Number(fromTokenBalanceQuery.data.displayValue) < Number(sourceTokenAmount);
160
+
161
+ const disableContinue =
162
+ (swapRequired && !quoteQuery.data) ||
163
+ isNotEnoughBalance ||
164
+ allowanceQuery.isLoading;
165
+ const switchChainRequired =
166
+ props.payer.wallet.getChain()?.id !== fromChain.id;
167
+
168
+ const errorMsg =
169
+ !quoteQuery.isLoading && quoteQuery.error
170
+ ? getErrorMessage(quoteQuery.error)
171
+ : undefined;
172
+
173
+ function showSwapFlow() {
174
+ if (
175
+ (props.payOptions.mode === "direct_payment" ||
176
+ props.payOptions.mode === "fund_wallet") &&
177
+ !isNotEnoughBalance &&
178
+ !swapRequired
179
+ ) {
180
+ // same currency, just direct transfer
181
+ setScreen({
182
+ id: "transfer-flow",
183
+ });
184
+ } else if (
185
+ props.payOptions.mode === "transaction" &&
186
+ !isNotEnoughBalance &&
187
+ !swapRequired
188
+ ) {
189
+ if (payer.account.address !== receiverAddress) {
190
+ // needs transfer from another wallet before executing the transaction
191
+ setScreen({
192
+ id: "transfer-flow",
193
+ });
194
+ } else {
195
+ // has enough balance to just do the transaction directly
196
+ props.onDone();
197
+ }
198
+
199
+ return;
200
+ }
201
+
202
+ if (!quoteQuery.data) {
203
+ return;
204
+ }
205
+
206
+ setScreen({
207
+ id: "swap-flow",
208
+ quote: quoteQuery.data,
209
+ approvalAmount: allowanceQuery.data ?? undefined,
210
+ });
211
+ }
212
+
213
+ function showFees() {
214
+ if (!quoteQuery.data) {
215
+ return;
216
+ }
217
+
218
+ setIsOpen(true);
219
+ setDrawerScreen("fees");
220
+ }
221
+
222
+ return (
223
+ <Container flex="column" gap="md" animate="fadein">
224
+ {isOpen && (
225
+ <>
226
+ <DrawerOverlay ref={drawerOverlayRef} />
227
+ <Drawer ref={drawerRef} close={() => setIsOpen(false)}>
228
+ {drawerScreen === "fees" && quoteQuery.data && (
229
+ <div>
230
+ <Text size="lg" color="primaryText">
231
+ Fees
232
+ </Text>
233
+ <Spacer y="lg" />
234
+ <SwapFees quote={quoteQuery.data} />
235
+ </div>
236
+ )}
237
+ </Drawer>
238
+ </>
239
+ )}
240
+
241
+ {/* Quote info */}
242
+ <div>
243
+ <PayWithCryptoQuoteInfo
244
+ value={sourceTokenAmount || ""}
245
+ chain={fromChain}
246
+ token={fromToken}
247
+ isLoading={quoteQuery.isLoading && !sourceTokenAmount}
248
+ client={client}
249
+ freezeChainAndTokenSelection={disableTokenSelection}
250
+ payerAccount={props.payer.account}
251
+ swapRequired={swapRequired}
252
+ />
253
+ {swapRequired && (
254
+ <EstimatedTimeAndFees
255
+ quoteIsLoading={quoteQuery.isLoading}
256
+ estimatedSeconds={
257
+ quoteQuery.data?.swapDetails.estimated.durationSeconds
258
+ }
259
+ onViewFees={showFees}
260
+ />
261
+ )}
262
+ <Spacer y="md" />
263
+ </div>
264
+
265
+ {/* Error message */}
266
+ {errorMsg && (
267
+ <div>
268
+ {errorMsg.data?.minimumAmountEth ? (
269
+ <Text color="danger" size="sm" center multiline>
270
+ Minimum amount is{" "}
271
+ {formatNumber(Number(errorMsg.data.minimumAmountEth), 6)}{" "}
272
+ <TokenSymbol
273
+ token={toToken}
274
+ chain={toChain}
275
+ size="sm"
276
+ inline
277
+ color="danger"
278
+ />
279
+ </Text>
280
+ ) : (
281
+ <Text color="danger" size="sm" center multiline>
282
+ {errorMsg.message || defaultMessage}
283
+ </Text>
284
+ )}
285
+ </div>
286
+ )}
287
+
288
+ {!errorMsg && isNotEnoughBalance && (
289
+ <div>
290
+ <Text color="danger" size="sm" center multiline>
291
+ Not enough funds.
292
+ </Text>
293
+ <Text color="danger" size="sm" center multiline>
294
+ Try a different wallet or token.
295
+ </Text>
296
+ </div>
297
+ )}
298
+
299
+ {/* Button */}
300
+ {errorMsg?.data?.minimumAmountEth ? (
301
+ <Button
302
+ variant="accent"
303
+ fullWidth
304
+ onClick={() => {
305
+ props.setTokenAmount(
306
+ formatNumber(
307
+ Number(errorMsg.data?.minimumAmountEth),
308
+ 6,
309
+ ).toString(),
310
+ );
311
+ props.setHasEditedAmount(true);
312
+ }}
313
+ >
314
+ Set Minimum
315
+ </Button>
316
+ ) : switchChainRequired &&
317
+ !quoteQuery.isLoading &&
318
+ !allowanceQuery.isLoading &&
319
+ !isNotEnoughBalance &&
320
+ !quoteQuery.error ? (
321
+ <SwitchNetworkButton
322
+ variant="accent"
323
+ fullWidth
324
+ switchChain={async () => {
325
+ await props.payer.wallet.switchChain(fromChain);
326
+ }}
327
+ />
328
+ ) : (
329
+ <Button
330
+ variant={disableContinue ? "outline" : "accent"}
331
+ fullWidth
332
+ data-disabled={disableContinue}
333
+ disabled={disableContinue}
334
+ onClick={async () => {
335
+ if (!disableContinue) {
336
+ showSwapFlow();
337
+ }
338
+ }}
339
+ gap="xs"
340
+ >
341
+ {quoteQuery.isLoading ? (
342
+ <>
343
+ Getting price quote
344
+ <Spinner size="sm" color="accentText" />
345
+ </>
346
+ ) : (
347
+ "Continue"
348
+ )}
349
+ </Button>
350
+ )}
351
+ </Container>
352
+ );
353
+ }
@@ -1,14 +1,18 @@
1
1
  import { CheckCircledIcon } from "@radix-ui/react-icons";
2
2
  import { useState } from "react";
3
3
  import type { Chain } from "../../../../../../../chains/types.js";
4
+ import { getCachedChain } from "../../../../../../../chains/utils.js";
4
5
  import type { ThirdwebClient } from "../../../../../../../client/client.js";
5
6
  import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
6
7
  import { getContract } from "../../../../../../../contract/contract.js";
8
+ import { allowance } from "../../../../../../../extensions/erc20/__generated__/IERC20/read/allowance.js";
9
+ import { approve } from "../../../../../../../extensions/erc20/write/approve.js";
7
10
  import { transfer } from "../../../../../../../extensions/erc20/write/transfer.js";
8
11
  import { getBuyWithCryptoTransfer } from "../../../../../../../pay/buyWithCrypto/getTransfer.js";
9
12
  import { sendAndConfirmTransaction } from "../../../../../../../transaction/actions/send-and-confirm-transaction.js";
10
13
  import { sendTransaction } from "../../../../../../../transaction/actions/send-transaction.js";
11
14
  import { prepareTransaction } from "../../../../../../../transaction/prepare-transaction.js";
15
+ import type { Address } from "../../../../../../../utils/address.js";
12
16
  import { toWei } from "../../../../../../../utils/units.js";
13
17
  import { iconSize } from "../../../../../../core/design-system/index.js";
14
18
  import type { PayUIOptions } from "../../../../../../core/hooks/connection/ConnectButtonProps.js";
@@ -248,13 +252,46 @@ export function TransferConfirmationScreen(
248
252
  purchaseData: payOptions?.purchaseData,
249
253
  });
250
254
 
251
- if (transferResponse.approval) {
252
- setStep("approve");
253
- // approve the transfer
254
- await sendAndConfirmTransaction({
255
- account: props.payer.account,
256
- transaction: transferResponse.approval,
255
+ if (transferResponse.approvalData) {
256
+ // check allowance
257
+ const prevAllowance = await allowance({
258
+ contract: getContract({
259
+ client: client,
260
+ address: transferResponse.approvalData.tokenAddress,
261
+ chain: getCachedChain(
262
+ transferResponse.approvalData.chainId,
263
+ ),
264
+ }),
265
+ spender: transferResponse.approvalData
266
+ .spenderAddress as Address,
267
+ owner: payer.account.address,
257
268
  });
269
+
270
+ if (
271
+ prevAllowance <
272
+ BigInt(transferResponse.approvalData.amountWei)
273
+ ) {
274
+ setStep("approve");
275
+ const transaction = approve({
276
+ contract: getContract({
277
+ client: client,
278
+ address: transferResponse.approvalData.tokenAddress,
279
+ chain: getCachedChain(
280
+ transferResponse.approvalData.chainId,
281
+ ),
282
+ }),
283
+ spender: transferResponse.approvalData
284
+ .spenderAddress as Address,
285
+ amountWei: BigInt(
286
+ transferResponse.approvalData.amountWei,
287
+ ),
288
+ });
289
+ // approve the transfer
290
+ await sendAndConfirmTransaction({
291
+ account: props.payer.account,
292
+ transaction,
293
+ });
294
+ }
258
295
  }
259
296
 
260
297
  setStep("transfer");
@@ -1,4 +1,5 @@
1
1
  import { NATIVE_TOKEN_ADDRESS } from "../../../../../constants/addresses.js";
2
+ import { type Address, getAddress } from "../../../../../utils/address.js";
2
3
  import type { TokenInfo } from "../../../../core/utils/defaultTokens.js";
3
4
 
4
5
  export type NativeToken = { nativeToken: true };
@@ -17,4 +18,11 @@ export function isNativeToken(
17
18
  );
18
19
  }
19
20
 
21
+ export function getTokenAddress(token: TokenInfo | NativeToken): Address {
22
+ if (isNativeToken(token)) {
23
+ return NATIVE_TOKEN_ADDRESS;
24
+ }
25
+ return getAddress(token.address);
26
+ }
27
+
20
28
  export type ERC20OrNativeToken = TokenInfo | NativeToken;
@@ -3,7 +3,7 @@ import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.
3
3
  import { type Theme, fontSize } from "../../../core/design-system/index.js";
4
4
  import { StyledAnchor, StyledSpan } from "../design-system/elements.js";
5
5
 
6
- type TextProps = {
6
+ export type TextProps = {
7
7
  theme?: Theme;
8
8
  color?: keyof Theme["colors"];
9
9
  center?: boolean;
@@ -0,0 +1,22 @@
1
+ type ApiError = {
2
+ code: string;
3
+ message?: string;
4
+ data?: {
5
+ minimumAmountUSDCents?: string;
6
+ requestedAmountUSDCents?: string;
7
+ minimumAmountWei?: string;
8
+ minimumAmountEth?: string;
9
+ };
10
+ };
11
+
12
+ export const defaultMessage = "Unable to get price quote";
13
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
14
+ export function getErrorMessage(err: any): ApiError {
15
+ if (typeof err.error === "object" && err.error.code) {
16
+ return err.error;
17
+ }
18
+ return {
19
+ code: "UNABLE_TO_GET_PRICE_QUOTE",
20
+ message: defaultMessage,
21
+ };
22
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.87.4";
1
+ export const version = "5.88.0";
@@ -6,6 +6,9 @@ import type { ThirdwebClient } from "../../client/client.js";
6
6
  import { type ThirdwebContract, getContract } from "../../contract/contract.js";
7
7
  import { allowance } from "../../extensions/erc20/__generated__/IERC20/read/allowance.js";
8
8
  import { approve } from "../../extensions/erc20/write/approve.js";
9
+ import { isActiveSigner } from "../../extensions/erc4337/__generated__/IAccountPermissions/read/isActiveSigner.js";
10
+ import { addSessionKey } from "../../extensions/erc4337/account/addSessionKey.js";
11
+ import { sendTransaction } from "../../transaction/actions/send-transaction.js";
9
12
  import { toSerializableTransaction } from "../../transaction/actions/to-serializable-transaction.js";
10
13
  import type { WaitForReceiptOptions } from "../../transaction/actions/wait-for-tx-receipt.js";
11
14
  import {
@@ -16,6 +19,7 @@ import type { PreparedTransaction } from "../../transaction/prepare-transaction.
16
19
  import { readContract } from "../../transaction/read-contract.js";
17
20
  import { getAddress } from "../../utils/address.js";
18
21
  import { isZkSyncChain } from "../../utils/any-evm/zksync/isZkSyncChain.js";
22
+ import { isContractDeployed } from "../../utils/bytecode/is-contract-deployed.js";
19
23
  import type { Hex } from "../../utils/encoding/hex.js";
20
24
  import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js";
21
25
  import { parseTypedData } from "../../utils/signatures/helpers/parse-typed-data.js";
@@ -164,6 +168,30 @@ export async function connectSmartAccount(
164
168
  adminAccountToSmartAccountMap.set(personalAccount, account);
165
169
  smartAccountToAdminAccountMap.set(account, personalAccount);
166
170
 
171
+ if (options.sessionKey) {
172
+ let hasSessionKey = false;
173
+ // check if already added
174
+ const accountDeployed = await isContractDeployed(accountContract);
175
+ if (accountDeployed) {
176
+ hasSessionKey = await isActiveSigner({
177
+ contract: accountContract,
178
+ signer: options.sessionKey.address,
179
+ });
180
+ }
181
+ if (!hasSessionKey) {
182
+ const transaction = addSessionKey({
183
+ account: personalAccount,
184
+ contract: accountContract,
185
+ permissions: options.sessionKey.permissions,
186
+ sessionKeyAddress: options.sessionKey.address,
187
+ });
188
+ await sendTransaction({
189
+ account: account,
190
+ transaction,
191
+ });
192
+ }
193
+ }
194
+
167
195
  return [account, chain] as const;
168
196
  }
169
197
 
@@ -15,6 +15,7 @@ import {
15
15
  } from "../../exports/extensions/erc4337.js";
16
16
  import { balanceOf } from "../../extensions/erc1155/__generated__/IERC1155/read/balanceOf.js";
17
17
  import { claimTo } from "../../extensions/erc1155/drops/write/claimTo.js";
18
+ import { isActiveSigner } from "../../extensions/erc4337/__generated__/IAccountPermissions/read/isActiveSigner.js";
18
19
  import { setContractURI } from "../../extensions/marketplace/__generated__/IMarketplace/write/setContractURI.js";
19
20
  import { estimateGasCost } from "../../transaction/actions/estimate-gas-cost.js";
20
21
  import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js";
@@ -458,5 +459,29 @@ describe.runIf(process.env.TW_SECRET_KEY).sequential(
458
459
  }),
459
460
  ).rejects.toThrowError(/AA21 didn't pay prefund/);
460
461
  });
462
+
463
+ it("can use a session key right after connecting", async () => {
464
+ const sessionKey = await generateAccount({ client });
465
+ const wallet = smartWallet({
466
+ chain,
467
+ gasless: true,
468
+ sessionKey: {
469
+ address: sessionKey.address,
470
+ permissions: {
471
+ approvedTargets: "*",
472
+ },
473
+ },
474
+ });
475
+ await wallet.connect({
476
+ client: TEST_CLIENT,
477
+ personalAccount,
478
+ });
479
+
480
+ const isSigner = await isActiveSigner({
481
+ contract: accountContract,
482
+ signer: sessionKey.address,
483
+ });
484
+ expect(isSigner).toEqual(true);
485
+ });
461
486
  },
462
487
  );
@@ -3,6 +3,7 @@ import type * as ox__TypedData from "ox/TypedData";
3
3
  import type { Chain } from "../../chains/types.js";
4
4
  import type { ThirdwebClient } from "../../client/client.js";
5
5
  import type { ThirdwebContract } from "../../contract/contract.js";
6
+ import type { AccountPermissions } from "../../extensions/erc4337/account/types.js";
6
7
  import type { PreparedTransaction } from "../../transaction/prepare-transaction.js";
7
8
  import type { TransactionReceipt } from "../../transaction/types.js";
8
9
  import type { Hex } from "../../utils/encoding/hex.js";
@@ -21,6 +22,10 @@ export type SmartWalletOptions = Prettify<
21
22
  {
22
23
  chain: Chain; // TODO consider making default chain optional
23
24
  factoryAddress?: string;
25
+ sessionKey?: {
26
+ address: string;
27
+ permissions: AccountPermissions;
28
+ };
24
29
  overrides?: {
25
30
  bundlerUrl?: string;
26
31
  accountAddress?: string;