@b3dotfun/sdk 0.0.65 → 0.0.66-alpha.1

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 (67) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +38 -24
  2. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +22 -9
  3. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +3 -1
  4. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +44 -13
  5. package/dist/cjs/anyspend/react/components/AnySpendDepositUpside.d.ts +11 -0
  6. package/dist/cjs/anyspend/react/components/AnySpendDepositUpside.js +41 -0
  7. package/dist/cjs/anyspend/react/components/AnyspendDepositHype.d.ts +1 -1
  8. package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +14 -251
  9. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +2 -10
  10. package/dist/cjs/anyspend/react/components/index.d.ts +1 -0
  11. package/dist/cjs/anyspend/react/components/index.js +4 -1
  12. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +5 -1
  13. package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +16 -10
  14. package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +7 -5
  15. package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +13 -9
  16. package/dist/cjs/anyspend/react/hooks/useCryptoPaymentMethodState.d.ts +42 -0
  17. package/dist/cjs/anyspend/react/hooks/useCryptoPaymentMethodState.js +51 -0
  18. package/dist/cjs/global-account/react/components/B3DynamicModal.js +3 -0
  19. package/dist/cjs/global-account/react/hooks/useAuthentication.js +5 -0
  20. package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +6 -14
  21. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +21 -1
  22. package/dist/esm/anyspend/react/components/AnySpend.js +38 -24
  23. package/dist/esm/anyspend/react/components/AnySpendCustom.js +22 -9
  24. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +3 -1
  25. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +44 -13
  26. package/dist/esm/anyspend/react/components/AnySpendDepositUpside.d.ts +11 -0
  27. package/dist/esm/anyspend/react/components/AnySpendDepositUpside.js +38 -0
  28. package/dist/esm/anyspend/react/components/AnyspendDepositHype.d.ts +1 -1
  29. package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +15 -249
  30. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +2 -10
  31. package/dist/esm/anyspend/react/components/index.d.ts +1 -0
  32. package/dist/esm/anyspend/react/components/index.js +1 -0
  33. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +5 -1
  34. package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +16 -10
  35. package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +7 -5
  36. package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +13 -9
  37. package/dist/esm/anyspend/react/hooks/useCryptoPaymentMethodState.d.ts +42 -0
  38. package/dist/esm/anyspend/react/hooks/useCryptoPaymentMethodState.js +48 -0
  39. package/dist/esm/global-account/react/components/B3DynamicModal.js +3 -0
  40. package/dist/esm/global-account/react/hooks/useAuthentication.js +5 -0
  41. package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +6 -14
  42. package/dist/esm/global-account/react/stores/useModalStore.d.ts +21 -1
  43. package/dist/styles/index.css +1 -1
  44. package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +3 -1
  45. package/dist/types/anyspend/react/components/AnySpendDepositUpside.d.ts +11 -0
  46. package/dist/types/anyspend/react/components/AnyspendDepositHype.d.ts +1 -1
  47. package/dist/types/anyspend/react/components/index.d.ts +1 -0
  48. package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +5 -1
  49. package/dist/types/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +7 -5
  50. package/dist/types/anyspend/react/hooks/useCryptoPaymentMethodState.d.ts +42 -0
  51. package/dist/types/global-account/react/stores/useModalStore.d.ts +21 -1
  52. package/package.json +1 -1
  53. package/src/anyspend/react/components/AnySpend.tsx +50 -29
  54. package/src/anyspend/react/components/AnySpendCustom.tsx +28 -15
  55. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +54 -13
  56. package/src/anyspend/react/components/AnySpendDepositUpside.tsx +81 -0
  57. package/src/anyspend/react/components/AnyspendDepositHype.tsx +36 -526
  58. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +2 -13
  59. package/src/anyspend/react/components/index.ts +1 -0
  60. package/src/anyspend/react/hooks/useAnyspendFlow.ts +24 -12
  61. package/src/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.ts +20 -12
  62. package/src/anyspend/react/hooks/useCryptoPaymentMethodState.ts +71 -0
  63. package/src/global-account/react/components/B3DynamicModal.tsx +3 -0
  64. package/src/global-account/react/hooks/useAuthentication.ts +5 -0
  65. package/src/global-account/react/hooks/useUnifiedChainSwitchAndExecute.ts +6 -12
  66. package/src/global-account/react/stores/useModalStore.ts +22 -0
  67. package/src/styles/index.css +8 -0
@@ -33,6 +33,7 @@ import { base, mainnet } from "viem/chains";
33
33
  import { components } from "../../types/api";
34
34
  import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPaymentMethod";
35
35
  import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi";
36
+ import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
36
37
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
37
38
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
38
39
  import { CryptoPaySection } from "./common/CryptoPaySection";
@@ -174,11 +175,17 @@ function AnySpendInner({
174
175
  setActivePanel(targetPanel);
175
176
  }, [activePanel]);
176
177
  const [customRecipients, setCustomRecipients] = useState<RecipientOption[]>([]);
177
- // Add state for selected payment method
178
- const [selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod] = useState<CryptoPaymentMethodType>(
179
- CryptoPaymentMethodType.NONE,
180
- );
181
- // Add state for selected fiat payment method
178
+
179
+ // Payment method state with dual-state system (auto + explicit user selection)
180
+ const {
181
+ cryptoPaymentMethod,
182
+ setCryptoPaymentMethod,
183
+ selectedCryptoPaymentMethod,
184
+ setSelectedCryptoPaymentMethod,
185
+ effectiveCryptoPaymentMethod,
186
+ resetPaymentMethods,
187
+ } = useCryptoPaymentMethodState();
188
+
182
189
  const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState<FiatPaymentMethod>(FiatPaymentMethod.NONE);
183
190
  // const [newRecipientAddress, setNewRecipientAddress] = useState("");
184
191
  // const recipientInputRef = useRef<HTMLInputElement>(null);
@@ -197,7 +204,7 @@ function AnySpendInner({
197
204
  });
198
205
  const [selectedSrcToken, setSelectedSrcToken] = useState<components["schemas"]["Token"]>(srcTokenFromUrl);
199
206
  const { data: srcTokenMetadata } = useTokenData(selectedSrcToken?.chainId, selectedSrcToken?.address);
200
- const [srcAmount, setSrcAmount] = useState<string>(searchParams.get("fromAmount") || "0.01");
207
+ const [srcAmount, setSrcAmount] = useState<string>(searchParams.get("fromAmount") || "0");
201
208
 
202
209
  // State for onramp amount
203
210
  const [srcAmountOnRamp, setSrcAmountOnRamp] = useState<string>(searchParams.get("fromAmount") || "5");
@@ -467,8 +474,9 @@ function AnySpendInner({
467
474
  // Auto-select crypto payment method based on available wallets and balance
468
475
  useAutoSelectCryptoPaymentMethod({
469
476
  paymentType: activeTab,
477
+ cryptoPaymentMethod,
478
+ setCryptoPaymentMethod,
470
479
  selectedCryptoPaymentMethod,
471
- setSelectedCryptoPaymentMethod,
472
480
  hasEnoughBalance,
473
481
  isBalanceLoading,
474
482
  });
@@ -602,9 +610,9 @@ function AnySpendInner({
602
610
  // Add orderId and payment method to URL for persistence
603
611
  const params = new URLSearchParams(searchParams.toString()); // Preserve existing params
604
612
  params.set("orderId", orderId);
605
- if (selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
606
- console.log("Setting cryptoPaymentMethod in URL:", selectedCryptoPaymentMethod);
607
- params.set("cryptoPaymentMethod", selectedCryptoPaymentMethod);
613
+ if (effectiveCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
614
+ console.log("Setting cryptoPaymentMethod in URL:", effectiveCryptoPaymentMethod);
615
+ params.set("cryptoPaymentMethod", effectiveCryptoPaymentMethod);
608
616
  } else {
609
617
  console.log("Payment method is NONE, not setting in URL");
610
618
  }
@@ -672,7 +680,7 @@ function AnySpendInner({
672
680
  // For crypto: check payment method first, then recipient
673
681
 
674
682
  // If no payment method selected, show "Choose payment method"
675
- if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
683
+ if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
676
684
  return { text: "Choose payment method", disable: false, error: false, loading: false };
677
685
  }
678
686
 
@@ -681,12 +689,12 @@ function AnySpendInner({
681
689
 
682
690
  // If payment method selected, show appropriate action
683
691
  if (
684
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
685
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET
692
+ effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
693
+ effectiveCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET
686
694
  ) {
687
695
  return { text: "Swap", disable: false, error: false, loading: false };
688
696
  }
689
- if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO) {
697
+ if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO) {
690
698
  return { text: "Continue to payment", disable: false, error: false, loading: false };
691
699
  }
692
700
  }
@@ -701,7 +709,7 @@ function AnySpendInner({
701
709
  isCreatingOnrampOrder,
702
710
  anyspendQuote,
703
711
  activeTab,
704
- selectedCryptoPaymentMethod,
712
+ effectiveCryptoPaymentMethod,
705
713
  selectedFiatPaymentMethod,
706
714
  ]);
707
715
 
@@ -735,7 +743,7 @@ function AnySpendInner({
735
743
  // For crypto: check payment method first, then recipient
736
744
 
737
745
  // If no payment method selected, show payment method selection
738
- if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
746
+ if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
739
747
  console.log("No payment method selected, showing selection panel");
740
748
  navigateToPanel(PanelView.CRYPTO_PAYMENT_METHOD, "forward");
741
749
  return;
@@ -751,12 +759,12 @@ function AnySpendInner({
751
759
 
752
760
  // If payment method is selected, create order with payment method info
753
761
  if (
754
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
755
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ||
756
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO
762
+ effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
763
+ effectiveCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ||
764
+ effectiveCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO
757
765
  ) {
758
- console.log("Creating crypto order with payment method:", selectedCryptoPaymentMethod);
759
- await handleCryptoSwap(selectedCryptoPaymentMethod);
766
+ console.log("Creating crypto order with payment method:", effectiveCryptoPaymentMethod);
767
+ await handleCryptoSwap(effectiveCryptoPaymentMethod);
760
768
  return;
761
769
  }
762
770
  }
@@ -962,13 +970,17 @@ function AnySpendInner({
962
970
  relayTxs={oat.data.relayTxs}
963
971
  executeTx={oat.data.executeTx}
964
972
  refundTxs={oat.data.refundTxs}
965
- selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
966
- onPaymentMethodChange={setSelectedCryptoPaymentMethod}
973
+ selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
974
+ onPaymentMethodChange={method => {
975
+ // When user explicitly changes payment method, set it as selected
976
+ setSelectedCryptoPaymentMethod(method);
977
+ }}
967
978
  points={oat.data.points || undefined}
968
979
  onBack={() => {
969
980
  setOrderId(undefined);
970
981
  navigateBack();
971
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.NONE); // Reset payment method when going back
982
+ // Reset payment methods when going back
983
+ resetPaymentMethods();
972
984
  }}
973
985
  />
974
986
  )}
@@ -1000,7 +1012,12 @@ function AnySpendInner({
1000
1012
  {/* Tab section */}
1001
1013
  <TabSection
1002
1014
  activeTab={activeTab}
1003
- setActiveTab={setActiveTab}
1015
+ setActiveTab={tab => {
1016
+ setActiveTab(tab);
1017
+ // Reset payment methods when switching tabs
1018
+ resetPaymentMethods();
1019
+ setSelectedFiatPaymentMethod(FiatPaymentMethod.NONE);
1020
+ }}
1004
1021
  setSelectedCryptoPaymentMethod={setSelectedCryptoPaymentMethod}
1005
1022
  setSelectedFiatPaymentMethod={setSelectedFiatPaymentMethod}
1006
1023
  />
@@ -1017,7 +1034,7 @@ function AnySpendInner({
1017
1034
  setSrcAmount={setSrcAmount}
1018
1035
  isSrcInputDirty={isSrcInputDirty}
1019
1036
  setIsSrcInputDirty={setIsSrcInputDirty}
1020
- selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
1037
+ selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
1021
1038
  onSelectCryptoPaymentMethod={() => navigateToPanel(PanelView.CRYPTO_PAYMENT_METHOD, "forward")}
1022
1039
  anyspendQuote={anyspendQuote}
1023
1040
  onTokenSelect={onTokenSelect}
@@ -1120,7 +1137,7 @@ function AnySpendInner({
1120
1137
  anyspendQuote={anyspendQuote}
1121
1138
  onShowPointsDetail={() => navigateToPanel(PanelView.POINTS_DETAIL, "forward")}
1122
1139
  onShowFeeDetail={() => navigateToPanel(PanelView.FEE_DETAIL, "forward")}
1123
- selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
1140
+ selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
1124
1141
  />
1125
1142
  )}
1126
1143
  </div>
@@ -1209,11 +1226,15 @@ function AnySpendInner({
1209
1226
  <CryptoPaymentMethod
1210
1227
  globalAddress={globalAddress}
1211
1228
  globalWallet={globalWallet}
1212
- selectedPaymentMethod={selectedCryptoPaymentMethod}
1213
- setSelectedPaymentMethod={setSelectedCryptoPaymentMethod}
1229
+ selectedPaymentMethod={effectiveCryptoPaymentMethod}
1230
+ setSelectedPaymentMethod={method => {
1231
+ // When user explicitly selects a payment method, save it
1232
+ setSelectedCryptoPaymentMethod(method);
1233
+ }}
1214
1234
  isCreatingOrder={isCreatingOrder}
1215
1235
  onBack={navigateBack}
1216
1236
  onSelectPaymentMethod={(method: CryptoPaymentMethodType) => {
1237
+ // When user explicitly selects a payment method, save it and go back
1217
1238
  setSelectedCryptoPaymentMethod(method);
1218
1239
  navigateBack();
1219
1240
  }}
@@ -45,6 +45,7 @@ import { toast } from "sonner";
45
45
  import { base } from "viem/chains";
46
46
  import { useFeatureFlags } from "../contexts/FeatureFlagsContext";
47
47
  import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi";
48
+ import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
48
49
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
49
50
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
50
51
  import { FeeBreakDown } from "./common/FeeBreakDown";
@@ -250,10 +251,11 @@ function AnySpendCustomInner({
250
251
  );
251
252
  const [activeTab, setActiveTab] = useState<"crypto" | "fiat">(activeTabProps);
252
253
 
253
- // Add state for selected payment methods
254
- const [selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod] = useState<CryptoPaymentMethodType>(
255
- CryptoPaymentMethodType.NONE,
256
- );
254
+ // Payment method state with dual-state system (auto + explicit user selection)
255
+ // Note: AnySpendCustom doesn't use auto-selection, only explicit user selection
256
+ const { setSelectedCryptoPaymentMethod, effectiveCryptoPaymentMethod, resetPaymentMethods } =
257
+ useCryptoPaymentMethodState();
258
+
257
259
  const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState<FiatPaymentMethod>(FiatPaymentMethod.NONE);
258
260
 
259
261
  // Get current user's wallet
@@ -550,7 +552,7 @@ function AnySpendCustomInner({
550
552
  }
551
553
 
552
554
  // Check payment method selection for crypto tab
553
- if (activeTab === "crypto" && selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
555
+ if (activeTab === "crypto" && effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
554
556
  setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD);
555
557
  return;
556
558
  }
@@ -693,12 +695,17 @@ function AnySpendCustomInner({
693
695
  relayTxs={oat.data.relayTxs}
694
696
  executeTx={oat.data.executeTx}
695
697
  refundTxs={oat.data.refundTxs}
696
- cryptoPaymentMethod={activeTab === "fiat" ? CryptoPaymentMethodType.NONE : selectedCryptoPaymentMethod}
697
- selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
698
- onPaymentMethodChange={setSelectedCryptoPaymentMethod}
698
+ cryptoPaymentMethod={activeTab === "fiat" ? CryptoPaymentMethodType.NONE : effectiveCryptoPaymentMethod}
699
+ selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
700
+ onPaymentMethodChange={method => {
701
+ // When user explicitly changes payment method, set it as selected
702
+ setSelectedCryptoPaymentMethod(method);
703
+ }}
699
704
  onBack={() => {
700
705
  setOrderId(undefined);
701
706
  setActivePanel(PanelView.CONFIRM_ORDER);
707
+ // Reset payment methods when going back
708
+ resetPaymentMethods();
702
709
  // Remove orderId from URL when canceling
703
710
  const params = new URLSearchParams(searchParams.toString());
704
711
  params.delete("orderId");
@@ -832,7 +839,8 @@ function AnySpendCustomInner({
832
839
  )}
833
840
  onClick={() => {
834
841
  setActiveTab("crypto");
835
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.NONE);
842
+ // Reset payment methods when switching tabs
843
+ resetPaymentMethods();
836
844
  setSelectedFiatPaymentMethod(FiatPaymentMethod.NONE);
837
845
  }}
838
846
  >
@@ -846,7 +854,8 @@ function AnySpendCustomInner({
846
854
  )}
847
855
  onClick={() => {
848
856
  setActiveTab("fiat");
849
- setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.NONE);
857
+ // Reset payment methods when switching tabs
858
+ resetPaymentMethods();
850
859
  setSelectedFiatPaymentMethod(FiatPaymentMethod.NONE);
851
860
  }}
852
861
  >
@@ -902,7 +911,7 @@ function AnySpendCustomInner({
902
911
  className="text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors hover:text-blue-700"
903
912
  onClick={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
904
913
  >
905
- {selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ? (
914
+ {effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ? (
906
915
  <>
907
916
  {connectedAddress ? (
908
917
  <>
@@ -915,7 +924,7 @@ function AnySpendCustomInner({
915
924
  )}
916
925
  <ChevronRight className="h-4 w-4" />
917
926
  </>
918
- ) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (
927
+ ) : effectiveCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (
919
928
  <>
920
929
  Transfer crypto
921
930
  <ChevronRight className="h-4 w-4" />
@@ -1043,7 +1052,7 @@ function AnySpendCustomInner({
1043
1052
  </div>
1044
1053
  ) : !recipientAddress ? (
1045
1054
  "Select recipient"
1046
- ) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE ? (
1055
+ ) : effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE ? (
1047
1056
  "Choose payment method"
1048
1057
  ) : anyspendQuote ? (
1049
1058
  <>
@@ -1239,11 +1248,15 @@ function AnySpendCustomInner({
1239
1248
  <CryptoPaymentMethod
1240
1249
  globalAddress={currentWallet?.wallet?.address}
1241
1250
  globalWallet={currentWallet?.wallet}
1242
- selectedPaymentMethod={selectedCryptoPaymentMethod}
1243
- setSelectedPaymentMethod={setSelectedCryptoPaymentMethod}
1251
+ selectedPaymentMethod={effectiveCryptoPaymentMethod}
1252
+ setSelectedPaymentMethod={method => {
1253
+ // When user explicitly selects a payment method, save it
1254
+ setSelectedCryptoPaymentMethod(method);
1255
+ }}
1244
1256
  isCreatingOrder={isCreatingOrder}
1245
1257
  onBack={() => setActivePanel(PanelView.CONFIRM_ORDER)}
1246
1258
  onSelectPaymentMethod={(method: CryptoPaymentMethodType) => {
1259
+ // When user explicitly selects a payment method, save it and go back
1247
1260
  setSelectedCryptoPaymentMethod(method);
1248
1261
  setActivePanel(PanelView.CONFIRM_ORDER);
1249
1262
  }}
@@ -3,6 +3,7 @@ import { GetQuoteResponse } from "@b3dotfun/sdk/anyspend/types/api_req_res";
3
3
  import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
4
4
  import { Button, ShinyButton, StyleRoot, TransitionPanel, useAccountWallet } from "@b3dotfun/sdk/global-account/react";
5
5
  import { cn } from "@b3dotfun/sdk/shared/utils/cn";
6
+ import { formatUnits } from "@b3dotfun/sdk/shared/utils/number";
6
7
  import invariant from "invariant";
7
8
  import { ArrowDown, Loader2 } from "lucide-react";
8
9
  import { motion } from "motion/react";
@@ -46,7 +47,9 @@ export interface AnySpendCustomExactInProps {
46
47
  onTokenSelect?: (token: components["schemas"]["Token"], event: { preventDefault: () => void }) => void;
47
48
  customUsdInputValues?: string[];
48
49
  preferEoa?: boolean;
49
- customExactInConfig: CustomExactInConfig;
50
+ customExactInConfig?: CustomExactInConfig;
51
+ orderType?: "hype_duel" | "custom_exact_in";
52
+ minDestinationAmount?: number;
50
53
  header?: ({
51
54
  anyspendPrice,
52
55
  isLoadingAnyspendPrice,
@@ -81,9 +84,11 @@ function AnySpendCustomExactInInner({
81
84
  customUsdInputValues,
82
85
  preferEoa,
83
86
  customExactInConfig,
87
+ orderType = "custom_exact_in",
88
+ minDestinationAmount,
84
89
  header,
85
90
  }: AnySpendCustomExactInProps) {
86
- const actionLabel = customExactInConfig.action ?? "Custom Execution";
91
+ const actionLabel = customExactInConfig?.action ?? "Custom Execution";
87
92
 
88
93
  const DESTINATION_TOKEN_DETAILS = {
89
94
  SYMBOL: destinationToken.symbol ?? "TOKEN",
@@ -108,6 +113,7 @@ function AnySpendCustomExactInInner({
108
113
  isSrcInputDirty,
109
114
  setIsSrcInputDirty,
110
115
  selectedCryptoPaymentMethod,
116
+ effectiveCryptoPaymentMethod,
111
117
  setSelectedCryptoPaymentMethod,
112
118
  selectedFiatPaymentMethod,
113
119
  setSelectedFiatPaymentMethod,
@@ -139,7 +145,7 @@ function AnySpendCustomExactInInner({
139
145
  destinationTokenChainId: destinationChainId,
140
146
  slippage: SLIPPAGE_PERCENT,
141
147
  disableUrlParamManagement: true,
142
- orderType: "custom_exact_in",
148
+ orderType,
143
149
  });
144
150
 
145
151
  const { connectedEOAWallet } = useAccountWallet();
@@ -160,6 +166,14 @@ function AnySpendCustomExactInInner({
160
166
  const expectedDstAmountRaw = anyspendQuote?.data?.currencyOut?.amount ?? "0";
161
167
 
162
168
  const buildCustomPayload = (_recipient: string | undefined) => {
169
+ if (!customExactInConfig) {
170
+ // For hype_duel or other simple order types
171
+ return {
172
+ expectedDstAmount: expectedDstAmountRaw,
173
+ };
174
+ }
175
+
176
+ // For custom_exact_in with custom config
163
177
  return {
164
178
  amount: expectedDstAmountRaw,
165
179
  expectedDstAmount: expectedDstAmountRaw,
@@ -183,18 +197,42 @@ function AnySpendCustomExactInInner({
183
197
  if (!anyspendQuote || !anyspendQuote.success)
184
198
  return { text: "Get quote error", disable: true, error: true, loading: false };
185
199
 
200
+ // Check minimum destination amount if specified
201
+ // Check minimum destination amount if specified
202
+ if (
203
+ minDestinationAmount &&
204
+ anyspendQuote.data?.currencyOut?.amount &&
205
+ anyspendQuote.data.currencyOut.currency &&
206
+ anyspendQuote.data.currencyOut.currency.decimals != null
207
+ ) {
208
+ const rawAmountInWei = BigInt(anyspendQuote.data.currencyOut.amount);
209
+ const decimals = anyspendQuote.data.currencyOut.currency.decimals;
210
+ const actualAmount = parseFloat(formatUnits(rawAmountInWei.toString(), decimals));
211
+
212
+ if (actualAmount < minDestinationAmount) {
213
+ return {
214
+ text: `Minimum ${minDestinationAmount} ${DESTINATION_TOKEN_DETAILS.SYMBOL} deposit`,
215
+ disable: true,
216
+ error: true,
217
+ loading: false,
218
+ };
219
+ }
220
+ }
221
+
186
222
  if (paymentType === "crypto") {
187
- if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
223
+ if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
188
224
  return { text: "Choose payment method", disable: false, error: false, loading: false };
189
225
  }
190
226
  if (
191
227
  !hasEnoughBalance &&
192
228
  !isBalanceLoading &&
193
- selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET
229
+ effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET
194
230
  ) {
195
231
  return { text: "Insufficient balance", disable: true, error: true, loading: false };
196
232
  }
197
- return { text: `Execute ${actionLabel}`, disable: false, error: false, loading: false };
233
+ // Use different text based on order type
234
+ const buttonText = orderType === "hype_duel" ? "Continue to deposit" : `Execute ${actionLabel}`;
235
+ return { text: buttonText, disable: false, error: false, loading: false };
198
236
  }
199
237
 
200
238
  if (paymentType === "fiat") {
@@ -213,11 +251,14 @@ function AnySpendCustomExactInInner({
213
251
  selectedRecipientOrDefault,
214
252
  anyspendQuote,
215
253
  paymentType,
216
- selectedCryptoPaymentMethod,
254
+ effectiveCryptoPaymentMethod,
217
255
  selectedFiatPaymentMethod,
218
256
  hasEnoughBalance,
219
257
  isBalanceLoading,
220
258
  actionLabel,
259
+ minDestinationAmount,
260
+ DESTINATION_TOKEN_DETAILS.SYMBOL,
261
+ orderType,
221
262
  ]);
222
263
 
223
264
  const onMainButtonClick = async () => {
@@ -229,7 +270,7 @@ function AnySpendCustomExactInInner({
229
270
  }
230
271
 
231
272
  if (paymentType === "crypto") {
232
- if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
273
+ if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
233
274
  setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD);
234
275
  return;
235
276
  }
@@ -270,7 +311,7 @@ function AnySpendCustomExactInInner({
270
311
  setSrcAmount={setSrcAmount}
271
312
  isSrcInputDirty={isSrcInputDirty}
272
313
  setIsSrcInputDirty={setIsSrcInputDirty}
273
- selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
314
+ selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
274
315
  onSelectCryptoPaymentMethod={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
275
316
  anyspendQuote={anyspendQuote}
276
317
  onTokenSelect={onTokenSelect}
@@ -383,7 +424,7 @@ function AnySpendCustomExactInInner({
383
424
 
384
425
  createOrder({
385
426
  recipientAddress: selectedRecipientOrDefault,
386
- orderType: "custom_exact_in",
427
+ orderType,
387
428
  srcChain: selectedSrcChainId,
388
429
  dstChain: selectedDstChainId,
389
430
  srcToken: selectedSrcToken,
@@ -434,7 +475,7 @@ function AnySpendCustomExactInInner({
434
475
 
435
476
  createOnrampOrder({
436
477
  recipientAddress: selectedRecipientOrDefault,
437
- orderType: "custom_exact_in",
478
+ orderType,
438
479
  dstChain: selectedDstChainId,
439
480
  dstToken: selectedDstToken,
440
481
  srcFiatAmount: srcAmount,
@@ -465,8 +506,8 @@ function AnySpendCustomExactInInner({
465
506
  relayTxs={oat.data.relayTxs}
466
507
  executeTx={oat.data.executeTx}
467
508
  refundTxs={oat.data.refundTxs}
468
- cryptoPaymentMethod={paymentType === "fiat" ? CryptoPaymentMethodType.NONE : selectedCryptoPaymentMethod}
469
- selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
509
+ cryptoPaymentMethod={paymentType === "fiat" ? CryptoPaymentMethodType.NONE : effectiveCryptoPaymentMethod}
510
+ selectedCryptoPaymentMethod={effectiveCryptoPaymentMethod}
470
511
  onPaymentMethodChange={setSelectedCryptoPaymentMethod}
471
512
  onBack={() => {
472
513
  setOrderId(undefined);
@@ -0,0 +1,81 @@
1
+ import { components } from "@b3dotfun/sdk/anyspend/types/api";
2
+ import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
3
+ import { base } from "viem/chains";
4
+ import { AnySpendCustomExactIn } from "./AnySpendCustomExactIn";
5
+
6
+ const DEPOSIT_FOR_FUNCTION_ABI = JSON.stringify([
7
+ {
8
+ inputs: [
9
+ {
10
+ internalType: "address",
11
+ name: "user",
12
+ type: "address",
13
+ },
14
+ {
15
+ internalType: "uint256",
16
+ name: "amount",
17
+ type: "uint256",
18
+ },
19
+ ],
20
+ name: "depositFor",
21
+ outputs: [],
22
+ stateMutability: "nonpayable",
23
+ type: "function",
24
+ },
25
+ ]);
26
+
27
+ export function AnySpendDepositUpside({
28
+ loadOrder,
29
+ mode = "modal",
30
+ recipientAddress,
31
+ sourceTokenAddress,
32
+ sourceTokenChainId,
33
+ depositContractAddress,
34
+ token,
35
+ onSuccess,
36
+ }: {
37
+ loadOrder?: string;
38
+ mode?: "modal" | "page";
39
+ recipientAddress: string;
40
+ sourceTokenAddress?: string;
41
+ sourceTokenChainId?: number;
42
+ depositContractAddress: string;
43
+ token: components["schemas"]["Token"];
44
+ onSuccess?: (amount: string) => void;
45
+ }) {
46
+ if (!recipientAddress) return null;
47
+
48
+ const header = () => (
49
+ <>
50
+ <div className="from-b3-react-background to-as-on-surface-1 w-full rounded-t-lg bg-gradient-to-t">
51
+ <div className="mb-1 flex w-full flex-col items-center gap-2">
52
+ <span className="font-sf-rounded text-2xl font-semibold">Swap & Deposit {token.symbol}</span>
53
+ </div>
54
+ </div>
55
+ </>
56
+ );
57
+
58
+ const customExactInConfig = {
59
+ functionAbi: DEPOSIT_FOR_FUNCTION_ABI,
60
+ functionName: "depositFor",
61
+ functionArgs: [normalizeAddress(recipientAddress), "{{amount_out}}"],
62
+ to: depositContractAddress,
63
+ spenderAddress: depositContractAddress,
64
+ action: `deposit ${token.symbol}`,
65
+ };
66
+
67
+ return (
68
+ <AnySpendCustomExactIn
69
+ loadOrder={loadOrder}
70
+ mode={mode}
71
+ recipientAddress={recipientAddress}
72
+ sourceTokenAddress={sourceTokenAddress}
73
+ sourceTokenChainId={sourceTokenChainId}
74
+ destinationToken={token}
75
+ destinationChainId={base.id}
76
+ customExactInConfig={customExactInConfig}
77
+ header={header}
78
+ onSuccess={onSuccess}
79
+ />
80
+ );
81
+ }