@b3dotfun/sdk 0.0.63-test.0 → 0.0.63-test.0-alpha.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 (66) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  2. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +275 -0
  3. package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  4. package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.js +288 -0
  5. package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  6. package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.js +33 -0
  7. package/dist/cjs/anyspend/react/components/common/OrderDetails.js +10 -2
  8. package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
  9. package/dist/cjs/anyspend/react/components/index.d.ts +5 -1
  10. package/dist/cjs/anyspend/react/components/index.js +11 -3
  11. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  12. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +30 -8
  13. package/dist/cjs/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  14. package/dist/cjs/anyspend/react/hooks/useAnyspendQuote.js +1 -1
  15. package/dist/cjs/anyspend/types/api.d.ts +665 -3
  16. package/dist/cjs/anyspend/utils/orderPayload.js +4 -0
  17. package/dist/cjs/global-account/react/components/B3DynamicModal.js +10 -1
  18. package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
  19. package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +2 -2
  20. package/dist/cjs/global-account/react/hooks/useAuthentication.js +7 -2
  21. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +31 -1
  22. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  23. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +269 -0
  24. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  25. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +285 -0
  26. package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  27. package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.js +30 -0
  28. package/dist/esm/anyspend/react/components/common/OrderDetails.js +10 -2
  29. package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
  30. package/dist/esm/anyspend/react/components/index.d.ts +5 -1
  31. package/dist/esm/anyspend/react/components/index.js +5 -1
  32. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  33. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +30 -8
  34. package/dist/esm/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  35. package/dist/esm/anyspend/react/hooks/useAnyspendQuote.js +1 -1
  36. package/dist/esm/anyspend/types/api.d.ts +665 -3
  37. package/dist/esm/anyspend/utils/orderPayload.js +4 -0
  38. package/dist/esm/global-account/react/components/B3DynamicModal.js +11 -2
  39. package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
  40. package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +2 -2
  41. package/dist/esm/global-account/react/hooks/useAuthentication.js +7 -2
  42. package/dist/esm/global-account/react/stores/useModalStore.d.ts +31 -1
  43. package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
  44. package/dist/types/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
  45. package/dist/types/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
  46. package/dist/types/anyspend/react/components/index.d.ts +5 -1
  47. package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
  48. package/dist/types/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
  49. package/dist/types/anyspend/types/api.d.ts +665 -3
  50. package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -2
  51. package/dist/types/global-account/react/stores/useModalStore.d.ts +31 -1
  52. package/package.json +3 -3
  53. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +595 -0
  54. package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +522 -0
  55. package/src/anyspend/react/components/AnySpendStakeUpsideExactIn.tsx +73 -0
  56. package/src/anyspend/react/components/common/OrderDetails.tsx +10 -2
  57. package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +2 -3
  58. package/src/anyspend/react/components/index.ts +5 -1
  59. package/src/anyspend/react/hooks/useAnyspendFlow.ts +38 -8
  60. package/src/anyspend/react/hooks/useAnyspendQuote.ts +1 -1
  61. package/src/anyspend/types/api.ts +669 -1
  62. package/src/anyspend/utils/orderPayload.ts +5 -1
  63. package/src/global-account/react/components/B3DynamicModal.tsx +11 -1
  64. package/src/global-account/react/components/SignInWithB3/steps/LoginStep.tsx +2 -2
  65. package/src/global-account/react/hooks/useAuthentication.ts +10 -2
  66. package/src/global-account/react/stores/useModalStore.ts +34 -0
@@ -8,9 +8,9 @@ export declare function useAuthentication(partnerId: string): {
8
8
  isConnected: boolean;
9
9
  wallet: import("thirdweb/dist/types/wallets/in-app/core/wallet/types").EcosystemWallet;
10
10
  preAuthenticate: typeof preAuthenticate;
11
- connect: (wallet: Wallet) => Promise<void>;
11
+ connect: (_walleAutoConnectedWith: Wallet, allConnectedWallets: Wallet[]) => Promise<void>;
12
12
  isAuthenticating: boolean;
13
- onConnect: (wallet: Wallet) => Promise<void>;
13
+ onConnect: (_walleAutoConnectedWith: Wallet, allConnectedWallets: Wallet[]) => Promise<void>;
14
14
  user: {
15
15
  email?: string | undefined;
16
16
  username?: string | undefined;
@@ -210,6 +210,20 @@ export interface AnySpendStakeB3Props extends BaseModalProps {
210
210
  /** Callback function called when the stake is successful */
211
211
  onSuccess?: () => void;
212
212
  }
213
+ /**
214
+ * Props for the AnySpend Stake B3 (Custom Exact In) modal
215
+ * Handles B3 token staking operations using the custom exact in flow
216
+ */
217
+ export interface AnySpendStakeB3ExactInProps extends BaseModalProps {
218
+ /** Modal type identifier */
219
+ type: "anySpendStakeB3ExactIn";
220
+ /** Recipient address to stake B3 for */
221
+ recipientAddress: string;
222
+ /** Stake amount */
223
+ stakeAmount?: string;
224
+ /** Callback function called when the stake is successful */
225
+ onSuccess?: () => void;
226
+ }
213
227
  /**
214
228
  * Props for the AnySpend Stake Contract modal
215
229
  * Handles token staking operations to a given contract
@@ -230,6 +244,22 @@ export interface AnySpendStakeUpsideProps extends BaseModalProps {
230
244
  /** Callback function called when the stake is successful */
231
245
  onSuccess?: () => void;
232
246
  }
247
+ /**
248
+ * Props for the AnySpend Stake Upside (Exact In) modal
249
+ * Handles token staking operations using the custom exact in flow
250
+ */
251
+ export interface AnySpendStakeUpsideExactInProps extends BaseModalProps {
252
+ /** Modal type identifier */
253
+ type: "anySpendStakeUpsideExactIn";
254
+ /** Recipient address to stake tokens for */
255
+ recipientAddress: string;
256
+ /** Staking contract address */
257
+ stakingContractAddress: string;
258
+ /** Token to stake */
259
+ token: components["schemas"]["Token"];
260
+ /** Callback function called when the stake is successful */
261
+ onSuccess?: () => void;
262
+ }
233
263
  /**
234
264
  * Props for the AnySpend Buy Spin modal
235
265
  * Handles spin wheel entry purchases
@@ -324,7 +354,7 @@ export interface AvatarEditorModalProps extends BaseModalProps {
324
354
  /**
325
355
  * Union type of all possible modal content types
326
356
  */
327
- export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeUpsideProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps;
357
+ export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps;
328
358
  /**
329
359
  * State interface for the modal store
330
360
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.0.63-test.0",
3
+ "version": "0.0.63-test.0-alpha.0",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -358,7 +358,7 @@
358
358
  "postcss-prefix-selector": "^2.1.1",
359
359
  "tailwindcss": "3.4.1",
360
360
  "tailwindcss-animate": "^1.0.7",
361
- "thirdweb": "5.111.3",
361
+ "thirdweb": "5.112.0",
362
362
  "tsc-alias": "^1.8.16",
363
363
  "tsc-watch": "^7.1.1",
364
364
  "vitest": "^3.2.4"
@@ -381,7 +381,7 @@
381
381
  "react": "^18.0.0 || ^19.0.0",
382
382
  "react-dom": "^18.0.0 || ^19.0.0",
383
383
  "react-native-mmkv": "^3.2.0",
384
- "thirdweb": "5.111.3",
384
+ "thirdweb": "5.112.0",
385
385
  "three": "^0.175.0",
386
386
  "viem": "2.37.9",
387
387
  "wagmi": "2.16.9"
@@ -0,0 +1,595 @@
1
+ import { components } from "@b3dotfun/sdk/anyspend/types/api";
2
+ import { GetQuoteResponse } from "@b3dotfun/sdk/anyspend/types/api_req_res";
3
+ import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
4
+ import { Button, ShinyButton, StyleRoot, TransitionPanel, useAccountWallet } from "@b3dotfun/sdk/global-account/react";
5
+ import { cn } from "@b3dotfun/sdk/shared/utils/cn";
6
+ import invariant from "invariant";
7
+ import { ArrowDown, Loader2 } from "lucide-react";
8
+ import { motion } from "motion/react";
9
+ import { useEffect, useMemo, useRef } from "react";
10
+ import { toast } from "sonner";
11
+ import { useActiveWallet, useSetActiveWallet } from "thirdweb/react";
12
+ import { useGlobalWalletState } from "../../utils";
13
+ import { PanelView, useAnyspendFlow } from "../hooks/useAnyspendFlow";
14
+ import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
15
+ import { CryptoPaySection } from "./common/CryptoPaySection";
16
+ import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
17
+ import { CryptoReceiveSection } from "./common/CryptoReceiveSection";
18
+ import { FeeDetailPanel } from "./common/FeeDetailPanel";
19
+ import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaymentMethod";
20
+ import { OrderDetails } from "./common/OrderDetails";
21
+ import { PanelOnramp } from "./common/PanelOnramp";
22
+ import { PointsDetailPanel } from "./common/PointsDetailPanel";
23
+ import { RecipientSelection } from "./common/RecipientSelection";
24
+
25
+ const SLIPPAGE_PERCENT = 3;
26
+
27
+ type CustomExactInConfig = {
28
+ functionAbi: string;
29
+ functionName: string;
30
+ functionArgs: string[];
31
+ to: string;
32
+ spenderAddress?: string;
33
+ action?: string;
34
+ };
35
+
36
+ export interface AnySpendCustomExactInProps {
37
+ loadOrder?: string;
38
+ mode?: "modal" | "page";
39
+ recipientAddress: string;
40
+ paymentType?: "crypto" | "fiat";
41
+ sourceTokenAddress?: string;
42
+ sourceTokenChainId?: number;
43
+ destinationToken: components["schemas"]["Token"];
44
+ destinationChainId: number;
45
+ onSuccess?: (amount: string) => void;
46
+ mainFooter?: React.ReactNode;
47
+ onTokenSelect?: (token: components["schemas"]["Token"], event: { preventDefault: () => void }) => void;
48
+ customUsdInputValues?: string[];
49
+ preferEoa?: boolean;
50
+ customExactInConfig: CustomExactInConfig;
51
+ header?: ({
52
+ anyspendPrice,
53
+ isLoadingAnyspendPrice,
54
+ }: {
55
+ anyspendPrice: GetQuoteResponse | undefined;
56
+ isLoadingAnyspendPrice: boolean;
57
+ }) => React.JSX.Element;
58
+ }
59
+
60
+ export function AnySpendCustomExactIn(props: AnySpendCustomExactInProps) {
61
+ const fingerprintConfig = getFingerprintConfig();
62
+
63
+ return (
64
+ <AnySpendFingerprintWrapper fingerprint={fingerprintConfig}>
65
+ <AnySpendCustomExactInInner {...props} />
66
+ </AnySpendFingerprintWrapper>
67
+ );
68
+ }
69
+
70
+ function AnySpendCustomExactInInner({
71
+ loadOrder,
72
+ mode = "modal",
73
+ recipientAddress,
74
+ paymentType = "crypto",
75
+ sourceTokenAddress,
76
+ sourceTokenChainId,
77
+ destinationToken,
78
+ destinationChainId,
79
+ onSuccess,
80
+ mainFooter,
81
+ onTokenSelect,
82
+ customUsdInputValues,
83
+ preferEoa,
84
+ customExactInConfig,
85
+ header,
86
+ }: AnySpendCustomExactInProps) {
87
+ const actionLabel = customExactInConfig.action ?? "Custom Execution";
88
+
89
+ const DESTINATION_TOKEN_DETAILS = {
90
+ SYMBOL: destinationToken.symbol ?? "TOKEN",
91
+ LOGO_URI: destinationToken.metadata?.logoURI ?? "",
92
+ };
93
+
94
+ const {
95
+ activePanel,
96
+ setActivePanel,
97
+ orderId,
98
+ setOrderId,
99
+ oat,
100
+ selectedSrcChainId,
101
+ setSelectedSrcChainId,
102
+ selectedSrcToken,
103
+ setSelectedSrcToken,
104
+ selectedDstToken,
105
+ selectedDstChainId,
106
+ srcAmount,
107
+ setSrcAmount,
108
+ dstAmount,
109
+ isSrcInputDirty,
110
+ setIsSrcInputDirty,
111
+ selectedCryptoPaymentMethod,
112
+ setSelectedCryptoPaymentMethod,
113
+ selectedFiatPaymentMethod,
114
+ setSelectedFiatPaymentMethod,
115
+ selectedRecipientAddress,
116
+ setSelectedRecipientAddress,
117
+ recipientName,
118
+ globalAddress,
119
+ hasEnoughBalance,
120
+ isBalanceLoading,
121
+ anyspendQuote,
122
+ isLoadingAnyspendQuote,
123
+ activeInputAmountInWei,
124
+ geoData,
125
+ coinbaseAvailablePaymentMethods,
126
+ stripeWeb2Support,
127
+ createOrder,
128
+ isCreatingOrder,
129
+ createOnrampOrder,
130
+ isCreatingOnrampOrder,
131
+ } = useAnyspendFlow({
132
+ paymentType,
133
+ recipientAddress,
134
+ loadOrder,
135
+ isDepositMode: true,
136
+ onTransactionSuccess: onSuccess,
137
+ sourceTokenAddress,
138
+ sourceTokenChainId,
139
+ destinationTokenAddress: destinationToken.address,
140
+ destinationTokenChainId: destinationChainId,
141
+ slippage: SLIPPAGE_PERCENT,
142
+ disableUrlParamManagement: true,
143
+ orderType: "custom_exact_in",
144
+ });
145
+
146
+ const { connectedEOAWallet } = useAccountWallet();
147
+ const setActiveWallet = useSetActiveWallet();
148
+ const activeWallet = useActiveWallet();
149
+ const setGlobalAccountWallet = useGlobalWalletState(state => state.setGlobalAccountWallet);
150
+ const appliedPreferEoa = useRef(false);
151
+
152
+ useEffect(() => {
153
+ if (preferEoa && !appliedPreferEoa.current) {
154
+ if (connectedEOAWallet) {
155
+ appliedPreferEoa.current = true;
156
+ setGlobalAccountWallet(activeWallet);
157
+ setActiveWallet(connectedEOAWallet);
158
+ }
159
+ }
160
+ }, [preferEoa, connectedEOAWallet, setActiveWallet, activeWallet, setGlobalAccountWallet]);
161
+
162
+ const selectedRecipientOrDefault = selectedRecipientAddress ?? recipientAddress;
163
+
164
+ const expectedDstAmountRaw = anyspendQuote?.data?.currencyOut?.amount ?? "0";
165
+
166
+ const buildCustomPayload = (_recipient: string | undefined) => {
167
+ return {
168
+ amount: expectedDstAmountRaw,
169
+ expectedDstAmount: expectedDstAmountRaw,
170
+ functionAbi: customExactInConfig.functionAbi,
171
+ functionName: customExactInConfig.functionName,
172
+ functionArgs: customExactInConfig.functionArgs,
173
+ to: normalizeAddress(customExactInConfig.to),
174
+ spenderAddress: customExactInConfig.spenderAddress
175
+ ? normalizeAddress(customExactInConfig.spenderAddress)
176
+ : undefined,
177
+ action: customExactInConfig.action,
178
+ };
179
+ };
180
+
181
+ const btnInfo: { text: string; disable: boolean; error: boolean; loading: boolean } = useMemo(() => {
182
+ if (activeInputAmountInWei === "0") return { text: "Enter an amount", disable: true, error: false, loading: false };
183
+ if (isLoadingAnyspendQuote) return { text: "Loading quote...", disable: true, error: false, loading: true };
184
+ if (isCreatingOrder || isCreatingOnrampOrder)
185
+ return { text: "Creating order...", disable: true, error: false, loading: true };
186
+ if (!selectedRecipientOrDefault) return { text: "Select recipient", disable: false, error: false, loading: false };
187
+ if (!anyspendQuote || !anyspendQuote.success)
188
+ return { text: "Get quote error", disable: true, error: true, loading: false };
189
+
190
+ if (paymentType === "crypto") {
191
+ if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
192
+ return { text: "Choose payment method", disable: false, error: false, loading: false };
193
+ }
194
+ if (
195
+ !hasEnoughBalance &&
196
+ !isBalanceLoading &&
197
+ selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET
198
+ ) {
199
+ return { text: "Insufficient balance", disable: true, error: true, loading: false };
200
+ }
201
+ return { text: `Execute ${actionLabel}`, disable: false, error: false, loading: false };
202
+ }
203
+
204
+ if (paymentType === "fiat") {
205
+ if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
206
+ return { text: "Select payment method", disable: false, error: false, loading: false };
207
+ }
208
+ return { text: "Buy", disable: false, error: false, loading: false };
209
+ }
210
+
211
+ return { text: "Continue", disable: false, error: false, loading: false };
212
+ }, [
213
+ activeInputAmountInWei,
214
+ isLoadingAnyspendQuote,
215
+ isCreatingOrder,
216
+ isCreatingOnrampOrder,
217
+ selectedRecipientOrDefault,
218
+ anyspendQuote,
219
+ paymentType,
220
+ selectedCryptoPaymentMethod,
221
+ selectedFiatPaymentMethod,
222
+ hasEnoughBalance,
223
+ isBalanceLoading,
224
+ actionLabel,
225
+ ]);
226
+
227
+ const onMainButtonClick = async () => {
228
+ if (btnInfo.disable) return;
229
+
230
+ if (!selectedRecipientOrDefault) {
231
+ setActivePanel(PanelView.RECIPIENT_SELECTION);
232
+ return;
233
+ }
234
+
235
+ if (paymentType === "crypto") {
236
+ if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
237
+ setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD);
238
+ return;
239
+ }
240
+ await handleCryptoOrder();
241
+ } else if (paymentType === "fiat") {
242
+ if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
243
+ setActivePanel(PanelView.FIAT_PAYMENT_METHOD);
244
+ return;
245
+ }
246
+ await handleFiatOrder();
247
+ }
248
+ };
249
+
250
+ const headerContent = header ? (
251
+ header({ anyspendPrice: anyspendQuote, isLoadingAnyspendPrice: isLoadingAnyspendQuote })
252
+ ) : (
253
+ <div className="mb-4 flex flex-col items-center gap-3 text-center">
254
+ <div>
255
+ <h1 className="text-as-primary text-xl font-bold">{actionLabel}</h1>
256
+ <p className="text-as-secondary text-sm">Pay from any token to execute a custom exact-in transaction.</p>
257
+ </div>
258
+ </div>
259
+ );
260
+
261
+ const mainView = (
262
+ <div className="mx-auto flex w-[460px] max-w-full flex-col items-center gap-2">
263
+ {headerContent}
264
+
265
+ <div className="relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2">
266
+ <div className="relative flex w-full max-w-[calc(100vw-32px)] flex-col gap-2">
267
+ {paymentType === "crypto" ? (
268
+ <CryptoPaySection
269
+ selectedSrcChainId={selectedSrcChainId}
270
+ setSelectedSrcChainId={setSelectedSrcChainId}
271
+ selectedSrcToken={selectedSrcToken}
272
+ setSelectedSrcToken={setSelectedSrcToken}
273
+ srcAmount={srcAmount}
274
+ setSrcAmount={setSrcAmount}
275
+ isSrcInputDirty={isSrcInputDirty}
276
+ setIsSrcInputDirty={setIsSrcInputDirty}
277
+ selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
278
+ onSelectCryptoPaymentMethod={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
279
+ anyspendQuote={anyspendQuote}
280
+ onTokenSelect={onTokenSelect}
281
+ />
282
+ ) : (
283
+ <motion.div
284
+ initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
285
+ animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
286
+ transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
287
+ >
288
+ <PanelOnramp
289
+ srcAmountOnRamp={srcAmount}
290
+ setSrcAmountOnRamp={setSrcAmount}
291
+ selectedPaymentMethod={selectedFiatPaymentMethod}
292
+ setActivePanel={setActivePanel}
293
+ _recipientAddress={selectedRecipientOrDefault}
294
+ destinationToken={selectedDstToken}
295
+ destinationChainId={selectedDstChainId}
296
+ dstTokenSymbol={DESTINATION_TOKEN_DETAILS.SYMBOL}
297
+ hideDstToken
298
+ destinationAmount={dstAmount}
299
+ onDestinationTokenChange={() => {}}
300
+ onDestinationChainChange={() => {}}
301
+ fiatPaymentMethodIndex={PanelView.FIAT_PAYMENT_METHOD}
302
+ recipientSelectionPanelIndex={PanelView.RECIPIENT_SELECTION}
303
+ anyspendQuote={anyspendQuote}
304
+ onShowPointsDetail={() => setActivePanel(PanelView.POINTS_DETAIL)}
305
+ onShowFeeDetail={() => setActivePanel(PanelView.FEE_DETAIL)}
306
+ customUsdInputValues={customUsdInputValues}
307
+ />
308
+ </motion.div>
309
+ )}
310
+
311
+ <div
312
+ className={cn("relative -my-1 flex h-0 items-center justify-center", paymentType === "fiat" && "hidden")}
313
+ >
314
+ <Button
315
+ variant="ghost"
316
+ className={cn(
317
+ "swap-direction-button border-as-stroke bg-as-surface-primary z-10 h-10 w-10 cursor-default rounded-xl border-2 sm:h-8 sm:w-8 sm:rounded-xl",
318
+ )}
319
+ >
320
+ <div className="relative flex items-center justify-center transition-opacity">
321
+ <ArrowDown className="text-as-primary/50 h-5 w-5" />
322
+ </div>
323
+ </Button>
324
+ </div>
325
+
326
+ {paymentType === "crypto" && (
327
+ <CryptoReceiveSection
328
+ isDepositMode={false}
329
+ isBuyMode={true}
330
+ selectedRecipientAddress={selectedRecipientOrDefault}
331
+ recipientName={recipientName || undefined}
332
+ onSelectRecipient={() => setActivePanel(PanelView.RECIPIENT_SELECTION)}
333
+ dstAmount={dstAmount}
334
+ dstToken={selectedDstToken}
335
+ dstTokenSymbol={DESTINATION_TOKEN_DETAILS.SYMBOL}
336
+ dstTokenLogoURI={DESTINATION_TOKEN_DETAILS.LOGO_URI}
337
+ selectedDstChainId={selectedDstChainId}
338
+ setSelectedDstChainId={() => {}}
339
+ setSelectedDstToken={() => {}}
340
+ isSrcInputDirty={isSrcInputDirty}
341
+ onChangeDstAmount={value => {
342
+ setIsSrcInputDirty(false);
343
+ setSrcAmount(value);
344
+ }}
345
+ anyspendQuote={anyspendQuote}
346
+ onShowPointsDetail={() => setActivePanel(PanelView.POINTS_DETAIL)}
347
+ onShowFeeDetail={() => setActivePanel(PanelView.FEE_DETAIL)}
348
+ />
349
+ )}
350
+ </div>
351
+ </div>
352
+
353
+ <motion.div
354
+ initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
355
+ animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
356
+ transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
357
+ className={cn("mt-4 flex w-full max-w-[460px] flex-col gap-2")}
358
+ >
359
+ <ShinyButton
360
+ accentColor={"hsl(var(--as-brand))"}
361
+ disabled={btnInfo.disable}
362
+ onClick={onMainButtonClick}
363
+ className={cn(
364
+ "as-main-button relative w-full",
365
+ btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand",
366
+ )}
367
+ textClassName={cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white")}
368
+ >
369
+ <div className="flex items-center justify-center gap-2">
370
+ {btnInfo.loading && <Loader2 className="h-4 w-4 animate-spin" />}
371
+ {btnInfo.text}
372
+ </div>
373
+ </ShinyButton>
374
+ </motion.div>
375
+
376
+ {mainFooter ? mainFooter : null}
377
+ </div>
378
+ );
379
+
380
+ const handleCryptoOrder = async () => {
381
+ try {
382
+ invariant(anyspendQuote, "Relay price is not found");
383
+ invariant(selectedRecipientOrDefault, "Recipient address is not found");
384
+
385
+ const srcAmountBigInt = BigInt(activeInputAmountInWei);
386
+ const payload = buildCustomPayload(selectedRecipientOrDefault);
387
+
388
+ createOrder({
389
+ recipientAddress: selectedRecipientOrDefault,
390
+ orderType: "custom_exact_in",
391
+ srcChain: selectedSrcChainId,
392
+ dstChain: selectedDstChainId,
393
+ srcToken: selectedSrcToken,
394
+ dstToken: selectedDstToken,
395
+ srcAmount: srcAmountBigInt.toString(),
396
+ expectedDstAmount: expectedDstAmountRaw,
397
+ creatorAddress: globalAddress,
398
+ payload,
399
+ });
400
+ } catch (err: any) {
401
+ console.error(err);
402
+ toast.error("Failed to create order: " + err.message);
403
+ }
404
+ };
405
+
406
+ const handleFiatOrder = async () => {
407
+ try {
408
+ invariant(anyspendQuote, "Relay price is not found");
409
+ invariant(selectedRecipientOrDefault, "Recipient address is not found");
410
+
411
+ if (!srcAmount || parseFloat(srcAmount) <= 0) {
412
+ toast.error("Please enter a valid amount");
413
+ return;
414
+ }
415
+
416
+ let vendor: "coinbase" | "stripe" | "stripe-web2";
417
+ let paymentMethodString = "";
418
+
419
+ if (selectedFiatPaymentMethod === FiatPaymentMethod.COINBASE_PAY) {
420
+ if (coinbaseAvailablePaymentMethods.length === 0) {
421
+ toast.error("Coinbase Pay not available");
422
+ return;
423
+ }
424
+ vendor = "coinbase";
425
+ paymentMethodString = coinbaseAvailablePaymentMethods[0]?.id || "";
426
+ } else if (selectedFiatPaymentMethod === FiatPaymentMethod.STRIPE) {
427
+ if (!stripeWeb2Support || !stripeWeb2Support.isSupport) {
428
+ toast.error("Stripe not available");
429
+ return;
430
+ }
431
+ vendor = "stripe-web2";
432
+ } else {
433
+ toast.error("Please select a payment method");
434
+ return;
435
+ }
436
+
437
+ const payload = buildCustomPayload(selectedRecipientOrDefault);
438
+
439
+ createOnrampOrder({
440
+ recipientAddress: selectedRecipientOrDefault,
441
+ orderType: "custom_exact_in",
442
+ dstChain: selectedDstChainId,
443
+ dstToken: selectedDstToken,
444
+ srcFiatAmount: srcAmount,
445
+ onramp: {
446
+ vendor,
447
+ paymentMethod: paymentMethodString,
448
+ country: geoData?.country || "US",
449
+ redirectUrl: window.location.origin,
450
+ },
451
+ expectedDstAmount: expectedDstAmountRaw,
452
+ creatorAddress: globalAddress,
453
+ payload,
454
+ });
455
+ } catch (err: any) {
456
+ console.error(err);
457
+ toast.error("Failed to create order: " + err.message);
458
+ }
459
+ };
460
+
461
+ const orderDetailsView = (
462
+ <div className={"mx-auto w-[460px] max-w-full"}>
463
+ <div className="relative flex flex-col gap-4">
464
+ {oat && (
465
+ <OrderDetails
466
+ mode={mode}
467
+ order={oat.data.order}
468
+ depositTxs={oat.data.depositTxs}
469
+ relayTxs={oat.data.relayTxs}
470
+ executeTx={oat.data.executeTx}
471
+ refundTxs={oat.data.refundTxs}
472
+ cryptoPaymentMethod={paymentType === "fiat" ? CryptoPaymentMethodType.NONE : selectedCryptoPaymentMethod}
473
+ selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
474
+ onPaymentMethodChange={setSelectedCryptoPaymentMethod}
475
+ onBack={() => {
476
+ setOrderId(undefined);
477
+ setActivePanel(PanelView.MAIN);
478
+ }}
479
+ disableUrlParamManagement
480
+ points={oat.data.points || undefined}
481
+ />
482
+ )}
483
+ </div>
484
+ </div>
485
+ );
486
+
487
+ const loadingView = (
488
+ <div className="mx-auto flex w-full flex-col items-center gap-4 p-5">
489
+ <div className="text-as-primary">Loading order details...</div>
490
+ </div>
491
+ );
492
+
493
+ const recipientSelectionView = (
494
+ <RecipientSelection
495
+ initialValue={selectedRecipientOrDefault || ""}
496
+ onBack={() => setActivePanel(PanelView.MAIN)}
497
+ onConfirm={address => {
498
+ setSelectedRecipientAddress(address);
499
+ setActivePanel(PanelView.MAIN);
500
+ }}
501
+ />
502
+ );
503
+
504
+ const cryptoPaymentMethodView = (
505
+ <CryptoPaymentMethod
506
+ globalAddress={globalAddress}
507
+ globalWallet={undefined}
508
+ selectedPaymentMethod={selectedCryptoPaymentMethod}
509
+ setSelectedPaymentMethod={setSelectedCryptoPaymentMethod}
510
+ isCreatingOrder={isCreatingOrder}
511
+ onBack={() => setActivePanel(PanelView.MAIN)}
512
+ onSelectPaymentMethod={(method: CryptoPaymentMethodType) => {
513
+ setSelectedCryptoPaymentMethod(method);
514
+ setActivePanel(PanelView.MAIN);
515
+ }}
516
+ />
517
+ );
518
+
519
+ const fiatPaymentMethodView = (
520
+ <FiatPaymentMethodComponent
521
+ selectedPaymentMethod={selectedFiatPaymentMethod}
522
+ setSelectedPaymentMethod={setSelectedFiatPaymentMethod}
523
+ onBack={() => setActivePanel(PanelView.MAIN)}
524
+ onSelectPaymentMethod={(method: FiatPaymentMethod) => {
525
+ setSelectedFiatPaymentMethod(method);
526
+ setActivePanel(PanelView.MAIN);
527
+ }}
528
+ srcAmountOnRamp={srcAmount}
529
+ />
530
+ );
531
+
532
+ const pointsDetailView = (
533
+ <PointsDetailPanel
534
+ pointsAmount={anyspendQuote?.data?.pointsAmount || 0}
535
+ onBack={() => setActivePanel(PanelView.MAIN)}
536
+ />
537
+ );
538
+
539
+ const feeDetailView = anyspendQuote?.data?.fee ? (
540
+ <FeeDetailPanel
541
+ fee={anyspendQuote.data.fee}
542
+ transactionAmountUsd={
543
+ paymentType === "fiat"
544
+ ? parseFloat(srcAmount)
545
+ : anyspendQuote.data.currencyIn?.amountUsd
546
+ ? Number(anyspendQuote.data.currencyIn.amountUsd)
547
+ : undefined
548
+ }
549
+ onBack={() => setActivePanel(PanelView.MAIN)}
550
+ />
551
+ ) : null;
552
+
553
+ return (
554
+ <StyleRoot>
555
+ <div
556
+ className={cn(
557
+ "anyspend-container font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6",
558
+ mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl",
559
+ )}
560
+ >
561
+ <TransitionPanel
562
+ activeIndex={
563
+ orderId
564
+ ? oat
565
+ ? PanelView.ORDER_DETAILS
566
+ : PanelView.LOADING
567
+ : activePanel === PanelView.ORDER_DETAILS
568
+ ? PanelView.MAIN
569
+ : activePanel
570
+ }
571
+ className={cn("rounded-2xl", {
572
+ "mt-0": mode === "modal",
573
+ })}
574
+ variants={{
575
+ enter: { x: 300, opacity: 0 },
576
+ center: { x: 0, opacity: 1 },
577
+ exit: { x: -300, opacity: 0 },
578
+ }}
579
+ transition={{ type: "spring", stiffness: 300, damping: 30 }}
580
+ >
581
+ {[
582
+ <div key="main-view">{mainView}</div>,
583
+ <div key="crypto-payment-method-view">{cryptoPaymentMethodView}</div>,
584
+ <div key="fiat-payment-method-view">{fiatPaymentMethodView}</div>,
585
+ <div key="recipient-selection-view">{recipientSelectionView}</div>,
586
+ <div key="order-details-view">{orderDetailsView}</div>,
587
+ <div key="loading-view">{loadingView}</div>,
588
+ <div key="points-detail-view">{pointsDetailView}</div>,
589
+ <div key="fee-detail-view">{feeDetailView}</div>,
590
+ ]}
591
+ </TransitionPanel>
592
+ </div>
593
+ </StyleRoot>
594
+ );
595
+ }