@b3dotfun/sdk 0.0.1-alpha.2 → 0.0.1-alpha.21

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 (115) hide show
  1. package/README.md +328 -230
  2. package/dist/cjs/anyspend/index.native.d.ts +13 -0
  3. package/dist/cjs/anyspend/index.native.js +35 -0
  4. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +2 -1
  5. package/dist/cjs/anyspend/react/components/AnySpend.js +4 -4
  6. package/dist/cjs/anyspend/react/components/AnySpendBuySpin.d.ts +2 -1
  7. package/dist/cjs/anyspend/react/components/AnySpendBuySpin.js +123 -50
  8. package/dist/cjs/anyspend/react/components/common/OrderHistoryItem.js +5 -1
  9. package/dist/cjs/anyspend/react/components/common/TokenBalance.js +1 -1
  10. package/dist/cjs/anyspend/utils/chain.js +3 -0
  11. package/dist/cjs/global-account/react/components/B3DynamicModal.js +2 -2
  12. package/dist/cjs/global-account/react/components/{B3Provider.d.ts → B3Provider/B3Provider.d.ts} +3 -29
  13. package/dist/cjs/global-account/react/components/{B3Provider.js → B3Provider/B3Provider.js} +6 -34
  14. package/dist/cjs/global-account/react/components/{B3Provider.native.d.ts → B3Provider/B3Provider.native.d.ts} +2 -25
  15. package/dist/cjs/global-account/react/components/{B3Provider.native.js → B3Provider/B3Provider.native.js} +5 -28
  16. package/dist/cjs/global-account/react/components/B3Provider/types.d.ts +25 -0
  17. package/dist/cjs/global-account/react/components/B3Provider/types.js +20 -0
  18. package/dist/cjs/global-account/react/components/B3Provider/useB3.d.ts +5 -0
  19. package/dist/cjs/global-account/react/components/B3Provider/useB3.js +17 -0
  20. package/dist/cjs/global-account/react/components/StyleRoot.js +2 -2
  21. package/dist/cjs/global-account/react/components/index.d.ts +8 -6
  22. package/dist/cjs/global-account/react/components/index.js +18 -16
  23. package/dist/cjs/global-account/react/hooks/index.d.ts +1 -1
  24. package/dist/cjs/global-account/react/hooks/index.js +2 -1
  25. package/dist/cjs/global-account/react/hooks/useBestTransactionPath.js +3 -3
  26. package/dist/cjs/global-account/react/hooks/useTokenBalance.js +1 -1
  27. package/dist/cjs/global-account/react/index.native.d.ts +7 -0
  28. package/dist/cjs/global-account/react/index.native.js +21 -0
  29. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +2 -0
  30. package/dist/cjs/global-account/types/chain-networks.d.ts +34 -34
  31. package/dist/cjs/global-account/types/feature-flags.d.ts +5 -5
  32. package/dist/cjs/shared/constants/chains/b3Chain.d.ts +1 -1
  33. package/dist/cjs/shared/constants/chains/supported.d.ts +8 -7
  34. package/dist/cjs/shared/constants/chains/supported.js +8 -1
  35. package/dist/cjs/shared/utils/chains.js +4 -0
  36. package/dist/cjs/shared/utils/number.js +1 -1
  37. package/dist/esm/anyspend/index.native.d.ts +13 -0
  38. package/dist/esm/anyspend/index.native.js +19 -0
  39. package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -1
  40. package/dist/esm/anyspend/react/components/AnySpend.js +4 -4
  41. package/dist/esm/anyspend/react/components/AnySpendBuySpin.d.ts +2 -1
  42. package/dist/esm/anyspend/react/components/AnySpendBuySpin.js +123 -50
  43. package/dist/esm/anyspend/react/components/common/OrderHistoryItem.js +5 -1
  44. package/dist/esm/anyspend/react/components/common/TokenBalance.js +1 -1
  45. package/dist/esm/anyspend/utils/chain.js +3 -0
  46. package/dist/esm/global-account/react/components/B3DynamicModal.js +1 -1
  47. package/dist/esm/global-account/react/components/{B3Provider.d.ts → B3Provider/B3Provider.d.ts} +3 -29
  48. package/dist/esm/global-account/react/components/{B3Provider.js → B3Provider/B3Provider.js} +5 -32
  49. package/dist/esm/global-account/react/components/{B3Provider.native.d.ts → B3Provider/B3Provider.native.d.ts} +2 -25
  50. package/dist/esm/global-account/react/components/{B3Provider.native.js → B3Provider/B3Provider.native.js} +5 -26
  51. package/dist/esm/global-account/react/components/B3Provider/types.d.ts +25 -0
  52. package/dist/esm/global-account/react/components/B3Provider/types.js +17 -0
  53. package/dist/esm/global-account/react/components/B3Provider/useB3.d.ts +5 -0
  54. package/dist/esm/global-account/react/components/B3Provider/useB3.js +14 -0
  55. package/dist/esm/global-account/react/components/StyleRoot.js +1 -1
  56. package/dist/esm/global-account/react/components/index.d.ts +8 -6
  57. package/dist/esm/global-account/react/components/index.js +7 -5
  58. package/dist/esm/global-account/react/hooks/index.d.ts +1 -1
  59. package/dist/esm/global-account/react/hooks/index.js +1 -1
  60. package/dist/esm/global-account/react/hooks/useBestTransactionPath.js +3 -3
  61. package/dist/esm/global-account/react/hooks/useTokenBalance.js +1 -1
  62. package/dist/esm/global-account/react/index.native.d.ts +7 -0
  63. package/dist/esm/global-account/react/index.native.js +11 -0
  64. package/dist/esm/global-account/react/stores/useModalStore.d.ts +2 -0
  65. package/dist/esm/global-account/types/chain-networks.d.ts +34 -34
  66. package/dist/esm/global-account/types/feature-flags.d.ts +5 -5
  67. package/dist/esm/shared/constants/chains/b3Chain.d.ts +1 -1
  68. package/dist/esm/shared/constants/chains/supported.d.ts +8 -7
  69. package/dist/esm/shared/constants/chains/supported.js +7 -0
  70. package/dist/esm/shared/utils/chains.js +4 -0
  71. package/dist/esm/shared/utils/number.js +1 -1
  72. package/dist/styles/index.css +1 -1
  73. package/dist/types/anyspend/index.native.d.ts +13 -0
  74. package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -1
  75. package/dist/types/anyspend/react/components/AnySpendBuySpin.d.ts +2 -1
  76. package/dist/types/global-account/react/components/{B3Provider.d.ts → B3Provider/B3Provider.d.ts} +2 -28
  77. package/dist/types/global-account/react/components/{B3Provider.native.d.ts → B3Provider/B3Provider.native.d.ts} +1 -24
  78. package/dist/types/global-account/react/components/B3Provider/types.d.ts +25 -0
  79. package/dist/types/global-account/react/components/B3Provider/useB3.d.ts +5 -0
  80. package/dist/types/global-account/react/components/index.d.ts +8 -6
  81. package/dist/types/global-account/react/hooks/index.d.ts +1 -1
  82. package/dist/types/global-account/react/index.native.d.ts +7 -0
  83. package/dist/types/global-account/react/stores/useModalStore.d.ts +2 -0
  84. package/dist/types/global-account/types/chain-networks.d.ts +34 -34
  85. package/dist/types/global-account/types/feature-flags.d.ts +5 -5
  86. package/dist/types/shared/constants/chains/b3Chain.d.ts +1 -1
  87. package/dist/types/shared/constants/chains/supported.d.ts +8 -7
  88. package/package.json +26 -24
  89. package/src/anyspend/index.native.ts +24 -0
  90. package/src/anyspend/react/components/AnySpend.tsx +6 -5
  91. package/src/anyspend/react/components/AnySpendBuySpin.tsx +232 -179
  92. package/src/anyspend/react/components/common/OrderHistoryItem.tsx +5 -1
  93. package/src/anyspend/react/components/common/TokenBalance.tsx +1 -1
  94. package/src/anyspend/utils/chain.ts +3 -0
  95. package/src/global-account/react/components/B3DynamicModal.tsx +1 -1
  96. package/src/global-account/react/components/{B3Provider.native.tsx → B3Provider/B3Provider.native.tsx} +4 -45
  97. package/src/global-account/react/components/{B3Provider.tsx → B3Provider/B3Provider.tsx} +4 -53
  98. package/src/global-account/react/components/B3Provider/types.ts +40 -0
  99. package/src/global-account/react/components/B3Provider/useB3.ts +17 -0
  100. package/src/global-account/react/components/StyleRoot.tsx +1 -1
  101. package/src/global-account/react/components/index.ts +8 -6
  102. package/src/global-account/react/hooks/index.ts +1 -1
  103. package/src/global-account/react/hooks/useBestTransactionPath.tsx +3 -3
  104. package/src/global-account/react/hooks/useTokenBalance.tsx +1 -1
  105. package/src/global-account/react/index.native.ts +14 -0
  106. package/src/global-account/react/stores/useModalStore.ts +2 -0
  107. package/src/shared/constants/chains/supported.ts +12 -4
  108. package/src/shared/utils/chains.ts +4 -1
  109. package/src/shared/utils/number.ts +1 -1
  110. package/dist/cjs/styles/index.d.ts +0 -20
  111. package/dist/cjs/styles/index.js +0 -22
  112. package/dist/esm/styles/index.d.ts +0 -20
  113. package/dist/esm/styles/index.js +0 -20
  114. package/dist/types/styles/index.d.ts +0 -20
  115. package/src/styles/index.ts +0 -24
@@ -1,4 +1,5 @@
1
1
  import { B3_TOKEN, OrderType } from "@b3dotfun/sdk/anyspend";
2
+ import { baseMainnet } from "@b3dotfun/sdk/shared/constants/chains/supported";
2
3
  import { EthIcon } from "./icons/EthIcon";
3
4
  import { SolIcon } from "./icons/SolIcon";
4
5
  import { UsdcIcon } from "./icons/USDCIcon";
@@ -19,7 +20,6 @@ import { ArrowRight, Loader2 } from "lucide-react";
19
20
  import { useCallback, useEffect, useState } from "react";
20
21
  import { toast } from "sonner";
21
22
  import { createPublicClient, encodeFunctionData, erc20Abi, formatUnits, http } from "viem";
22
- import { base } from "viem/chains";
23
23
  import { useAccount, useWaitForTransactionReceipt, useWriteContract } from "wagmi";
24
24
  import { AnySpendCustom } from "./AnySpendCustom";
25
25
 
@@ -52,6 +52,20 @@ const SPIN_WHEEL_ABI = [
52
52
  outputs: [],
53
53
  stateMutability: "payable",
54
54
  type: "function"
55
+ },
56
+ {
57
+ inputs: [],
58
+ name: "getWheelInfo",
59
+ outputs: [
60
+ { internalType: "address", name: "creator_", type: "address" },
61
+ { internalType: "uint256", name: "startTime_", type: "uint256" },
62
+ { internalType: "uint256", name: "endTime_", type: "uint256" },
63
+ { internalType: "uint256", name: "totalPrizesAvailable_", type: "uint256" },
64
+ { internalType: "uint256", name: "prizesRequestedCount_", type: "uint256" },
65
+ { internalType: "enum SpinWheelV2.WheelState", name: "state_", type: "uint8" }
66
+ ],
67
+ stateMutability: "view",
68
+ type: "function"
55
69
  }
56
70
  ] as const;
57
71
 
@@ -62,6 +76,37 @@ interface PaymentConfig {
62
76
  entryModule: string;
63
77
  }
64
78
 
79
+ interface WheelInfo {
80
+ creator_: string;
81
+ startTime_: bigint;
82
+ endTime_: bigint;
83
+ totalPrizesAvailable_: bigint;
84
+ prizesRequestedCount_: bigint;
85
+ state_: number;
86
+ }
87
+
88
+ type WheelStatus = "not_started" | "active" | "ended" | "sold_out";
89
+
90
+ function getWheelStatus(wheelInfo: WheelInfo): WheelStatus {
91
+ const now = BigInt(Math.floor(Date.now() / 1000));
92
+ console.log("@@anyspend-buy-spin:now:", now);
93
+ console.log("@@anyspend-buy-spin:wheelInfo:", wheelInfo);
94
+
95
+ if (now < wheelInfo.startTime_) {
96
+ return "not_started";
97
+ }
98
+
99
+ if (now > wheelInfo.endTime_) {
100
+ return "ended";
101
+ }
102
+
103
+ if (wheelInfo.totalPrizesAvailable_ <= wheelInfo.prizesRequestedCount_) {
104
+ return "sold_out";
105
+ }
106
+
107
+ return "active";
108
+ }
109
+
65
110
  function generateEncodedDataForBuyEntriesAndSpin(user: string, quantity: string): string {
66
111
  invariant(BigInt(quantity) > 0, "Quantity must be greater than zero");
67
112
  console.log("@@anyspend-buy-spin:encoded-data:", { user, quantity });
@@ -74,7 +119,7 @@ function generateEncodedDataForBuyEntriesAndSpin(user: string, quantity: string)
74
119
  }
75
120
 
76
121
  const basePublicClient = createPublicClient({
77
- chain: base,
122
+ chain: baseMainnet,
78
123
  transport: http()
79
124
  });
80
125
 
@@ -85,6 +130,7 @@ export function AnySpendBuySpin({
85
130
  spinwheelContractAddress,
86
131
  chainId,
87
132
  recipientAddress,
133
+ prefillQuantity,
88
134
  onSuccess
89
135
  }: {
90
136
  isMainnet?: boolean;
@@ -93,6 +139,7 @@ export function AnySpendBuySpin({
93
139
  spinwheelContractAddress: string;
94
140
  chainId: number;
95
141
  recipientAddress: string;
142
+ prefillQuantity?: string;
96
143
  onSuccess?: (txHash?: string) => void;
97
144
  }) {
98
145
  const hasMounted = useHasMounted();
@@ -102,6 +149,7 @@ export function AnySpendBuySpin({
102
149
  const [paymentConfig, setPaymentConfig] = useState<PaymentConfig | null>(null);
103
150
  const [isLoadingConfig, setIsLoadingConfig] = useState(true);
104
151
  const [configError, setConfigError] = useState<string>("");
152
+ const [wheelInfo, setWheelInfo] = useState<WheelInfo | null>(null);
105
153
 
106
154
  // Fetch B3 token balance
107
155
  const {
@@ -120,24 +168,34 @@ export function AnySpendBuySpin({
120
168
  // State for direct buying flow (when user has B3 tokens)
121
169
  const [isBuying, setIsBuying] = useState(false);
122
170
  const [buyingTxHash, setBuyingTxHash] = useState<string>("");
123
- const [showSuccessModal, setShowSuccessModal] = useState(false);
124
-
125
- // Wait for transaction confirmation
126
- const { isLoading: isTxPending, isSuccess: isTxSuccess } = useWaitForTransactionReceipt({
171
+ const {
172
+ isLoading: isTxPending,
173
+ isSuccess: isTxSuccess,
174
+ isError: isTxError,
175
+ error: txError
176
+ } = useWaitForTransactionReceipt({
127
177
  hash: buyingTxHash as `0x${string}`,
128
178
  query: {
129
179
  structuralSharing: false
130
180
  }
131
181
  });
132
182
 
133
- // Show success modal when transaction is confirmed
183
+ // Handle transaction status
134
184
  useEffect(() => {
135
- if (isTxSuccess && buyingTxHash) {
136
- setShowAmountPrompt(false);
137
- setShowSuccessModal(true);
185
+ if (!buyingTxHash) return;
186
+
187
+ if (isTxSuccess) {
188
+ setB3ModalOpen(false);
189
+ onSuccess?.(buyingTxHash);
190
+ toast.success("Spin purchase transaction confirmed!");
191
+ setIsBuying(false);
192
+ } else if (isTxError) {
193
+ console.error("@@anyspend-buy-spin:tx-error:", txError);
194
+ toast.error("Transaction failed. Please try again.");
195
+ setB3ModalOpen(false);
138
196
  setIsBuying(false);
139
197
  }
140
- }, [isTxSuccess, buyingTxHash]);
198
+ }, [isTxSuccess, isTxError, buyingTxHash, onSuccess, setB3ModalOpen, txError]);
141
199
 
142
200
  // Spin quantity state
143
201
  const [userSpinQuantity, setUserSpinQuantity] = useState<string>("");
@@ -146,12 +204,21 @@ export function AnySpendBuySpin({
146
204
  const [validationError, setValidationError] = useState<string>("");
147
205
  const [displayQuantity, setDisplayQuantity] = useState<string>("");
148
206
  const [debouncedQuantity, setDebouncedQuantity] = useState<string>("");
207
+ const [debouncedUserSpinQuantity, setDebouncedUserSpinQuantity] = useState<string>("");
208
+
209
+ useEffect(() => {
210
+ if (prefillQuantity && wheelInfo) {
211
+ const remainingSpins = wheelInfo.totalPrizesAvailable_ - wheelInfo.prizesRequestedCount_;
212
+ const adjustedQuantity = BigInt(prefillQuantity) > remainingSpins ? remainingSpins.toString() : prefillQuantity;
213
+ validateAndSetQuantity(adjustedQuantity);
214
+ }
215
+ }, [prefillQuantity, wheelInfo]);
149
216
 
150
217
  // Calculate total cost
151
218
  const totalCost =
152
219
  paymentConfig && userSpinQuantity ? paymentConfig.pricePerEntry * BigInt(userSpinQuantity) : BigInt(0);
153
220
 
154
- // Fetch payment configuration
221
+ // Fetch payment configuration and wheel info
155
222
  const fetchPaymentConfig = useCallback(async () => {
156
223
  if (!basePublicClient || !spinwheelContractAddress) return;
157
224
 
@@ -161,7 +228,7 @@ export function AnySpendBuySpin({
161
228
 
162
229
  console.log("@@anyspend-buy-spin:fetch-config:", { spinwheelContractAddress, chainId });
163
230
 
164
- const [config, entryModuleAddress] = await Promise.all([
231
+ const [config, entryModuleAddress, wheelInfo] = await Promise.all([
165
232
  basePublicClient.readContract({
166
233
  address: spinwheelContractAddress as `0x${string}`,
167
234
  abi: SPIN_WHEEL_ABI,
@@ -171,6 +238,11 @@ export function AnySpendBuySpin({
171
238
  address: spinwheelContractAddress as `0x${string}`,
172
239
  abi: SPIN_WHEEL_ABI,
173
240
  functionName: "entryModule"
241
+ }),
242
+ basePublicClient.readContract({
243
+ address: spinwheelContractAddress as `0x${string}`,
244
+ abi: SPIN_WHEEL_ABI,
245
+ functionName: "getWheelInfo"
174
246
  })
175
247
  ]);
176
248
 
@@ -181,13 +253,17 @@ export function AnySpendBuySpin({
181
253
  entryModule: entryModuleAddress
182
254
  };
183
255
 
184
- console.log("@@anyspend-buy-spin:config-fetched:", {
185
- pricePerEntry: paymentConfig.pricePerEntry.toString(),
186
- maxEntriesPerUser: paymentConfig.maxEntriesPerUser.toString(),
187
- paymentRecipient: paymentConfig.paymentRecipient,
188
- entryModule: paymentConfig.entryModule
189
- });
256
+ const wheelInfoData: WheelInfo = {
257
+ creator_: wheelInfo[0],
258
+ startTime_: wheelInfo[1],
259
+ endTime_: wheelInfo[2],
260
+ totalPrizesAvailable_: wheelInfo[3],
261
+ prizesRequestedCount_: wheelInfo[4],
262
+ state_: wheelInfo[5]
263
+ };
264
+
190
265
  setPaymentConfig(paymentConfig);
266
+ setWheelInfo(wheelInfoData);
191
267
  } catch (error) {
192
268
  console.error("@@anyspend-buy-spin:config-error:", error);
193
269
  setConfigError("Failed to load spin wheel configuration");
@@ -206,6 +282,7 @@ export function AnySpendBuySpin({
206
282
  useEffect(() => {
207
283
  const timer = setTimeout(() => {
208
284
  setDebouncedQuantity(displayQuantity);
285
+ setDebouncedUserSpinQuantity(userSpinQuantity);
209
286
  }, 500);
210
287
 
211
288
  return () => clearTimeout(timer);
@@ -245,6 +322,16 @@ export function AnySpendBuySpin({
245
322
  return;
246
323
  }
247
324
 
325
+ // Check if quantity exceeds remaining entries
326
+ if (wheelInfo && BigInt(numValue) > wheelInfo.totalPrizesAvailable_ - wheelInfo.prizesRequestedCount_) {
327
+ setIsQuantityValid(false);
328
+ setUserSpinQuantity("");
329
+ setValidationError(
330
+ `Only ${(wheelInfo.totalPrizesAvailable_ - wheelInfo.prizesRequestedCount_).toString()} spins remaining`
331
+ );
332
+ return;
333
+ }
334
+
248
335
  setUserSpinQuantity(value);
249
336
  setIsQuantityValid(true);
250
337
  setValidationError("");
@@ -299,7 +386,7 @@ export function AnySpendBuySpin({
299
386
  } catch (error) {
300
387
  console.error("@@anyspend-buy-spin:error:", error);
301
388
  toast.error("Spin purchase failed. Please try again.");
302
- setShowSuccessModal(false);
389
+ setB3ModalOpen(false);
303
390
  } finally {
304
391
  setIsBuying(false);
305
392
  }
@@ -378,6 +465,40 @@ export function AnySpendBuySpin({
378
465
  // Render quantity input prompt
379
466
  if (showAmountPrompt) {
380
467
  const pricePerEntry = formatUnits(paymentConfig.pricePerEntry, 18);
468
+ const remainingEntries = wheelInfo ? wheelInfo.totalPrizesAvailable_ - wheelInfo.prizesRequestedCount_ : 0n;
469
+ const wheelStatus = wheelInfo ? getWheelStatus(wheelInfo) : null;
470
+ const isSoldOut = wheelStatus === "sold_out";
471
+ const isActive = wheelStatus === "active";
472
+
473
+ const getStatusMessage = () => {
474
+ if (!wheelInfo) return null;
475
+
476
+ const formatDate = (timestamp: bigint) => {
477
+ return new Date(Number(timestamp) * 1000).toLocaleString();
478
+ };
479
+
480
+ switch (wheelStatus) {
481
+ case "not_started":
482
+ return {
483
+ title: "Spin Wheel Not Started",
484
+ message: `Starts at ${formatDate(wheelInfo.startTime_)}`
485
+ };
486
+ case "ended":
487
+ return {
488
+ title: "Spin Wheel Ended",
489
+ message: `Ended at ${formatDate(wheelInfo.endTime_)}`
490
+ };
491
+ case "sold_out":
492
+ return {
493
+ title: "All Spins Have Been Claimed",
494
+ message: "Stay tuned for the next spin wheel event!"
495
+ };
496
+ default:
497
+ return null;
498
+ }
499
+ };
500
+
501
+ const statusInfo = getStatusMessage();
381
502
 
382
503
  return (
383
504
  <StyleRoot>
@@ -391,7 +512,7 @@ export function AnySpendBuySpin({
391
512
  filter: hasMounted ? "blur(0px)" : "blur(10px)"
392
513
  }}
393
514
  transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
394
- className="mb-4 flex justify-center"
515
+ className={`flex justify-center ${isActive ? "mb-4" : ""}`}
395
516
  >
396
517
  <img
397
518
  alt="B3 Token"
@@ -413,15 +534,33 @@ export function AnySpendBuySpin({
413
534
  transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
414
535
  className="text-center"
415
536
  >
416
- <h2 className="font-sf-rounded text-as-primary mb-2 text-2xl font-bold">
417
- {(() => {
418
- const hasEnoughBalance = b3RawBalance && totalCost <= b3RawBalance;
419
- return hasEnoughBalance || !debouncedQuantity ? "Buy Spins" : `Swap & Buy Spins`;
420
- })()}
421
- </h2>
422
- <div className="bg-as-on-surface-2/50 inline-flex items-center gap-2 rounded-full border border-white/10 px-3 py-1 backdrop-blur-sm">
423
- <p className="text-as-primary/80 text-sm">{pricePerEntry} $B3 per spin</p>
424
- </div>
537
+ {isActive ? (
538
+ <>
539
+ <h2 className="font-sf-rounded text-as-primary mb-4 text-2xl font-bold">
540
+ {(() => {
541
+ const hasEnoughBalance = b3RawBalance && totalCost <= b3RawBalance;
542
+ return hasEnoughBalance || !debouncedQuantity ? "Buy Spins" : `Swap & Buy Spins`;
543
+ })()}
544
+ </h2>
545
+ {wheelInfo && (
546
+ <div className="inline-flex items-center gap-2">
547
+ <div className="bg-as-brand/10 border-as-brand/10 inline-flex items-center rounded-full border px-3 py-1">
548
+ <p className="text-as-brand text-sm font-medium">{pricePerEntry} $B3 per spin</p>
549
+ </div>
550
+ <div className="bg-as-brand/10 border-as-brand/10 inline-flex items-center rounded-full border px-3 py-1">
551
+ <p className="text-as-brand text-sm font-medium">{remainingEntries.toString()} remaining</p>
552
+ </div>
553
+ </div>
554
+ )}
555
+ </>
556
+ ) : (
557
+ statusInfo && (
558
+ <div className="text-center">
559
+ <p className="text-as-primary text-lg font-semibold">{statusInfo.title}</p>
560
+ <p className="text-as-primary/70 mt-2 text-sm">{statusInfo.message}</p>
561
+ </div>
562
+ )
563
+ )}
425
564
  </motion.div>
426
565
  </div>
427
566
 
@@ -435,163 +574,77 @@ export function AnySpendBuySpin({
435
574
  transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
436
575
  className="bg-b3-react-background w-full p-6"
437
576
  >
438
- <div className="space-y-4">
439
- <div className="flex items-center justify-between">
440
- <p className="text-as-primary/70 text-sm font-medium">Number of spins</p>
441
- <span className="text-as-primary/50 flex items-center gap-1 text-sm">
442
- Available: {isBalanceLoading ? <Loader2 className="h-3 w-3 animate-spin" /> : `${b3Balance} B3`}
443
- </span>
444
- </div>
577
+ {isActive ? (
578
+ <div className="space-y-4">
579
+ <div className="flex items-center justify-between">
580
+ <p className="text-as-primary/70 text-sm font-medium">Number of spins</p>
581
+ <span className="text-as-primary/50 flex items-center gap-1 text-sm">
582
+ Available: {isBalanceLoading ? <Loader2 className="h-3 w-3 animate-spin" /> : `${b3Balance} B3`}
583
+ </span>
584
+ </div>
445
585
 
446
- <div className="relative">
447
- <Input
448
- onFocus={onFocusQuantityInput}
449
- type="text"
450
- placeholder="1"
451
- value={displayQuantity}
452
- onChange={e => validateAndSetQuantity(e.target.value)}
453
- className={`h-14 px-4 pr-20 text-lg ${!isQuantityValid && displayQuantity ? "border-as-red" : "border-b3-react-border"}`}
454
- />
455
- <div className="font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70">
456
- {displayQuantity === "1" ? "Spin" : "Spins"}
586
+ <div className="relative">
587
+ <Input
588
+ onFocus={onFocusQuantityInput}
589
+ type="text"
590
+ placeholder="1"
591
+ value={displayQuantity}
592
+ onChange={e => validateAndSetQuantity(e.target.value)}
593
+ className={`h-14 px-4 pr-20 text-lg ${!isQuantityValid && displayQuantity ? "border-as-red" : "border-b3-react-border"}`}
594
+ />
595
+ <div className="font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70">
596
+ {displayQuantity === "1" ? "Spin" : "Spins"}
597
+ </div>
457
598
  </div>
458
- </div>
459
599
 
460
- {!isQuantityValid && displayQuantity && <p className="text-as-red text-sm">{validationError}</p>}
600
+ {!isQuantityValid && displayQuantity && <p className="text-as-red text-sm">{validationError}</p>}
461
601
 
462
- <div className="bg-as-on-surface-2/30 rounded-lg border border-white/10 p-4 backdrop-blur-sm">
463
- <div className="flex items-center justify-between">
464
- <span className="text-as-primary/70 text-sm font-medium">Total Cost:</span>
465
- <div className="flex items-center gap-2">
466
- <span className="text-as-primary text-lg font-bold">
467
- {displayQuantity && isQuantityValid ? formatUnits(totalCost, 18) : "0"} B3
468
- </span>
602
+ <div className="bg-as-on-surface-2/30 rounded-lg border border-white/10 p-4 backdrop-blur-sm">
603
+ <div className="flex items-center justify-between">
604
+ <span className="text-as-primary/70 text-sm font-medium">Total Cost:</span>
605
+ <div className="flex items-center gap-2">
606
+ <span className="text-as-primary text-lg font-bold">
607
+ {displayQuantity && isQuantityValid ? formatUnits(totalCost, 18) : "0"} B3
608
+ </span>
609
+ </div>
469
610
  </div>
470
611
  </div>
471
- </div>
472
- </div>
473
-
474
- <div className="mt-4">
475
- {(() => {
476
- const hasEnoughBalance = b3RawBalance && totalCost <= b3RawBalance;
477
-
478
- if (!hasEnoughBalance && debouncedQuantity) {
479
- return (
480
- <div className="bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5">
481
- <div className="flex items-center justify-center gap-2">
482
- <span className="text-as-primary text-sm font-semibold">Swap & buy from any token</span>
483
- <TextLoop>
484
- <EthIcon className="h-8 w-8" />
485
- <SolIcon className="h-8 w-8" />
486
- <UsdcIcon className="h-8 w-8" />
487
- </TextLoop>
488
- <ArrowRight className="text-as-primary h-4 w-4" />
489
- <img src="https://cdn.b3.fun/b3-coin-3d.png" className="h-7 w-7" alt="B3 Token" />
490
- </div>
491
- <p className="text-as-primary/50 text-sm font-medium">
492
- No problem, we'll help you swap to B3 for your spins!
493
- </p>
494
- </div>
495
- );
496
- }
497
- })()}
498
- </div>
499
-
500
- <Button
501
- onClick={confirmQuantity}
502
- disabled={!isQuantityValid || !displayQuantity || isBuying || isTxPending}
503
- className="bg-as-brand hover:bg-as-brand/90 text-as-primary mt-4 h-14 w-full rounded-xl text-lg font-medium"
504
- >
505
- {isBuying ? "Buying..." : isTxPending ? "Confirming..." : "Continue"}
506
- </Button>
507
- </motion.div>
508
- </div>
509
- </StyleRoot>
510
- );
511
- }
512
612
 
513
- // Success Modal for Direct Buying
514
- if (showSuccessModal) {
515
- return (
516
- <StyleRoot>
517
- <div className="bg-b3-react-background flex w-full flex-col items-center">
518
- <div className="w-full p-4">
519
- <motion.div
520
- initial={false}
521
- animate={{
522
- opacity: hasMounted ? 1 : 0,
523
- y: hasMounted ? 0 : 20,
524
- filter: hasMounted ? "blur(0px)" : "blur(10px)"
525
- }}
526
- transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
527
- className="relative mx-auto mb-4 size-[120px]"
528
- >
529
- <div className="absolute inset-0 scale-95 rounded-[50%] bg-gradient-to-br from-green-500/30 to-blue-500/30 blur-xl"></div>
530
- <GlareCardRounded className="overflow-hidden rounded-full border-none bg-gradient-to-br from-green-500/10 to-blue-500/10 backdrop-blur-sm">
531
- <img
532
- alt="B3 Token"
533
- loading="lazy"
534
- width="120"
535
- height="120"
536
- decoding="async"
537
- data-nimg="1"
538
- className="size-full shrink-0 bg-transparent text-transparent"
539
- src="https://cdn.b3.fun/b3-coin-3d.png"
540
- />
541
- <div className="absolute inset-0 rounded-[50%] border border-white/20"></div>
542
- </GlareCardRounded>
543
- </motion.div>
544
- <motion.div
545
- initial={false}
546
- animate={{
547
- opacity: hasMounted ? 1 : 0,
548
- y: hasMounted ? 0 : 20,
549
- filter: hasMounted ? "blur(0px)" : "blur(10px)"
550
- }}
551
- transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
552
- className="text-center"
553
- >
554
- <h2 className="font-sf-rounded mb-3 bg-gradient-to-r from-green-400 to-blue-500 bg-clip-text text-3xl font-bold text-transparent">
555
- 🎉 Purchase Complete!
556
- </h2>
557
- <div className="bg-as-on-surface-2/50 inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 backdrop-blur-sm">
558
- <span className="text-as-primary/80 text-sm font-medium">
559
- {userSpinQuantity} Spin{userSpinQuantity !== "1" ? "s" : ""} • {formatUnits(totalCost, 18)} B3
560
- </span>
561
- </div>
562
- </motion.div>
563
- </div>
613
+ <div className="mt-4">
614
+ {(() => {
615
+ const hasEnoughBalance = b3RawBalance && totalCost <= b3RawBalance;
616
+
617
+ if (!hasEnoughBalance && debouncedQuantity) {
618
+ return (
619
+ <div className="bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5">
620
+ <div className="flex items-center justify-center gap-2">
621
+ <span className="text-as-primary text-sm font-semibold">Swap & buy from any token</span>
622
+ <TextLoop>
623
+ <EthIcon className="h-8 w-8" />
624
+ <SolIcon className="h-8 w-8" />
625
+ <UsdcIcon className="h-8 w-8" />
626
+ </TextLoop>
627
+ <ArrowRight className="text-as-primary h-4 w-4" />
628
+ <img src="https://cdn.b3.fun/b3-coin-3d.png" className="h-7 w-7" alt="B3 Token" />
629
+ </div>
630
+ <p className="text-as-primary/50 text-sm font-medium">
631
+ No problem, we'll help you swap to B3 for your spins!
632
+ </p>
633
+ </div>
634
+ );
635
+ }
636
+ })()}
637
+ </div>
564
638
 
565
- <motion.div
566
- initial={false}
567
- animate={{
568
- opacity: hasMounted ? 1 : 0,
569
- y: hasMounted ? 0 : 20,
570
- filter: hasMounted ? "blur(0px)" : "blur(10px)"
571
- }}
572
- transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
573
- className="bg-b3-react-background w-full p-6"
574
- >
575
- <div className="mb-6">
576
- <a
577
- href={`https://basescan.org/tx/${buyingTxHash}`}
578
- target="_blank"
579
- rel="noopener noreferrer"
580
- className="text-as-primary/70 hover:text-as-primary block break-all text-center font-mono text-sm underline transition-colors"
581
- >
582
- View transaction
583
- </a>
584
- </div>
585
-
586
- <Button
587
- onClick={() => {
588
- setB3ModalOpen(false);
589
- onSuccess?.(buyingTxHash);
590
- }}
591
- className="bg-as-brand hover:bg-as-brand/90 text-as-primary h-14 w-full rounded-xl text-lg font-medium"
592
- >
593
- Done
594
- </Button>
639
+ <Button
640
+ onClick={confirmQuantity}
641
+ disabled={!isQuantityValid || !displayQuantity || isBuying || isTxPending}
642
+ className="bg-as-brand hover:bg-as-brand/90 text-as-primary mt-4 h-14 w-full rounded-xl text-lg font-medium"
643
+ >
644
+ {isBuying ? "Buying..." : isTxPending ? "Confirming..." : "Continue"}
645
+ </Button>
646
+ </div>
647
+ ) : null}
595
648
  </motion.div>
596
649
  </div>
597
650
  </StyleRoot>
@@ -155,7 +155,11 @@ export function OrderHistoryItem({ order, onSelectOrder, mode }: OrderHistoryIte
155
155
  <img src={dstToken.metadata.logoURI} alt={dstToken.symbol} className="h-6 w-6 rounded-full" />
156
156
  <div className="text-as-primary overflow-hidden text-ellipsis whitespace-nowrap text-xl font-semibold">
157
157
  {formatTokenAmount(
158
- actualDstAmount ? BigInt(actualDstAmount) : expectedDstAmount ? BigInt(expectedDstAmount) : 0n,
158
+ actualDstAmount
159
+ ? BigInt(actualDstAmount)
160
+ : expectedDstAmount
161
+ ? BigInt(expectedDstAmount)
162
+ : BigInt(0),
159
163
  dstToken.decimals
160
164
  )}{" "}
161
165
  {dstToken.symbol}
@@ -21,7 +21,7 @@ export function TokenBalance({
21
21
 
22
22
  // Calculate the amount based on percentage of balance
23
23
  // Multiply first, then divide to avoid BigInt truncation
24
- const amount = percentage === 100 ? rawBalance : (rawBalance * BigInt(percentage)) / 100n;
24
+ const amount = percentage === 100 ? rawBalance : (rawBalance * BigInt(percentage)) / BigInt(100);
25
25
 
26
26
  onChangeInput(formatUnits(amount, token.decimals));
27
27
  };
@@ -311,6 +311,9 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string)
311
311
  }
312
312
 
313
313
  export function getExplorerTxUrl(chainId: number, txHash: string) {
314
+ if (chainId === b3.id) {
315
+ return "https://explorer.b3.fun/b3/tx/" + txHash;
316
+ }
314
317
  if (EVM_CHAINS[chainId]) {
315
318
  return EVM_CHAINS[chainId].viem.blockExplorers?.default.url + "/tx/" + txHash;
316
319
  }
@@ -8,7 +8,7 @@ import {
8
8
  } from "@b3dotfun/sdk/anyspend/react";
9
9
  import { useIsMobile, useModalStore } from "@b3dotfun/sdk/global-account/react";
10
10
  import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
11
- import { useB3 } from "./B3Provider";
11
+ import { useB3 } from "./B3Provider/useB3";
12
12
  import { ManageAccount } from "./ManageAccount/ManageAccount";
13
13
  import { RequestPermissions } from "./RequestPermissions/RequestPermissions";
14
14
  import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow";