@b3dotfun/sdk 0.0.1-alpha.23 → 0.0.1-alpha.4

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 (87) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +1 -2
  2. package/dist/cjs/anyspend/react/components/AnySpend.js +4 -4
  3. package/dist/cjs/anyspend/react/components/AnySpendBuySpin.d.ts +1 -2
  4. package/dist/cjs/anyspend/react/components/AnySpendBuySpin.js +51 -124
  5. package/dist/cjs/anyspend/utils/chain.js +0 -3
  6. package/dist/cjs/global-account/react/components/B3DynamicModal.js +2 -2
  7. package/dist/{esm/global-account/react/components/B3Provider → cjs/global-account/react/components}/B3Provider.d.ts +29 -3
  8. package/dist/cjs/global-account/react/components/{B3Provider/B3Provider.js → B3Provider.js} +34 -6
  9. package/dist/cjs/global-account/react/components/{B3Provider/B3Provider.native.d.ts → B3Provider.native.d.ts} +25 -2
  10. package/dist/cjs/global-account/react/components/{B3Provider/B3Provider.native.js → B3Provider.native.js} +28 -5
  11. package/dist/cjs/global-account/react/components/StyleRoot.js +2 -2
  12. package/dist/cjs/global-account/react/components/index.d.ts +6 -8
  13. package/dist/cjs/global-account/react/components/index.js +16 -18
  14. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +0 -2
  15. package/dist/cjs/global-account/types/chain-networks.d.ts +34 -34
  16. package/dist/cjs/global-account/types/feature-flags.d.ts +5 -5
  17. package/dist/cjs/shared/constants/chains/b3Chain.d.ts +1 -1
  18. package/dist/cjs/shared/constants/chains/supported.d.ts +3 -4
  19. package/dist/cjs/shared/constants/chains/supported.js +1 -3
  20. package/dist/cjs/shared/utils/chains.js +0 -4
  21. package/dist/esm/anyspend/react/components/AnySpend.d.ts +1 -2
  22. package/dist/esm/anyspend/react/components/AnySpend.js +4 -4
  23. package/dist/esm/anyspend/react/components/AnySpendBuySpin.d.ts +1 -2
  24. package/dist/esm/anyspend/react/components/AnySpendBuySpin.js +51 -124
  25. package/dist/esm/anyspend/utils/chain.js +0 -3
  26. package/dist/esm/global-account/react/components/B3DynamicModal.js +1 -1
  27. package/dist/{cjs/global-account/react/components/B3Provider → esm/global-account/react/components}/B3Provider.d.ts +29 -3
  28. package/dist/esm/global-account/react/components/{B3Provider/B3Provider.js → B3Provider.js} +32 -5
  29. package/dist/esm/global-account/react/components/{B3Provider/B3Provider.native.d.ts → B3Provider.native.d.ts} +25 -2
  30. package/dist/esm/global-account/react/components/{B3Provider/B3Provider.native.js → B3Provider.native.js} +26 -5
  31. package/dist/esm/global-account/react/components/StyleRoot.js +1 -1
  32. package/dist/esm/global-account/react/components/index.d.ts +6 -8
  33. package/dist/esm/global-account/react/components/index.js +5 -7
  34. package/dist/esm/global-account/react/stores/useModalStore.d.ts +0 -2
  35. package/dist/esm/global-account/types/chain-networks.d.ts +34 -34
  36. package/dist/esm/global-account/types/feature-flags.d.ts +5 -5
  37. package/dist/esm/shared/constants/chains/b3Chain.d.ts +1 -1
  38. package/dist/esm/shared/constants/chains/supported.d.ts +3 -4
  39. package/dist/esm/shared/constants/chains/supported.js +0 -2
  40. package/dist/esm/shared/utils/chains.js +0 -4
  41. package/dist/styles/index.css +1 -1
  42. package/dist/types/anyspend/react/components/AnySpend.d.ts +1 -2
  43. package/dist/types/anyspend/react/components/AnySpendBuySpin.d.ts +1 -2
  44. package/dist/types/global-account/react/components/{B3Provider/B3Provider.d.ts → B3Provider.d.ts} +28 -2
  45. package/dist/types/global-account/react/components/{B3Provider/B3Provider.native.d.ts → B3Provider.native.d.ts} +24 -1
  46. package/dist/types/global-account/react/components/index.d.ts +6 -8
  47. package/dist/types/global-account/react/stores/useModalStore.d.ts +0 -2
  48. package/dist/types/global-account/types/chain-networks.d.ts +34 -34
  49. package/dist/types/global-account/types/feature-flags.d.ts +5 -5
  50. package/dist/types/shared/constants/chains/b3Chain.d.ts +1 -1
  51. package/dist/types/shared/constants/chains/supported.d.ts +3 -4
  52. package/package.json +11 -25
  53. package/src/anyspend/react/components/AnySpend.tsx +5 -6
  54. package/src/anyspend/react/components/AnySpendBuySpin.tsx +180 -233
  55. package/src/anyspend/utils/chain.ts +0 -3
  56. package/src/global-account/react/components/B3DynamicModal.tsx +1 -1
  57. package/src/global-account/react/components/{B3Provider/B3Provider.native.tsx → B3Provider.native.tsx} +45 -4
  58. package/src/global-account/react/components/{B3Provider/B3Provider.tsx → B3Provider.tsx} +53 -4
  59. package/src/global-account/react/components/StyleRoot.tsx +1 -1
  60. package/src/global-account/react/components/index.ts +6 -8
  61. package/src/global-account/react/stores/useModalStore.ts +0 -2
  62. package/src/shared/constants/chains/supported.ts +0 -2
  63. package/src/shared/utils/chains.ts +1 -4
  64. package/dist/cjs/anyspend/index.native.d.ts +0 -13
  65. package/dist/cjs/anyspend/index.native.js +0 -35
  66. package/dist/cjs/global-account/react/components/B3Provider/types.d.ts +0 -25
  67. package/dist/cjs/global-account/react/components/B3Provider/types.js +0 -20
  68. package/dist/cjs/global-account/react/components/B3Provider/useB3.d.ts +0 -5
  69. package/dist/cjs/global-account/react/components/B3Provider/useB3.js +0 -17
  70. package/dist/cjs/global-account/react/index.native.d.ts +0 -7
  71. package/dist/cjs/global-account/react/index.native.js +0 -21
  72. package/dist/esm/anyspend/index.native.d.ts +0 -13
  73. package/dist/esm/anyspend/index.native.js +0 -19
  74. package/dist/esm/global-account/react/components/B3Provider/types.d.ts +0 -25
  75. package/dist/esm/global-account/react/components/B3Provider/types.js +0 -17
  76. package/dist/esm/global-account/react/components/B3Provider/useB3.d.ts +0 -5
  77. package/dist/esm/global-account/react/components/B3Provider/useB3.js +0 -14
  78. package/dist/esm/global-account/react/index.native.d.ts +0 -7
  79. package/dist/esm/global-account/react/index.native.js +0 -11
  80. package/dist/types/anyspend/index.native.d.ts +0 -13
  81. package/dist/types/global-account/react/components/B3Provider/types.d.ts +0 -25
  82. package/dist/types/global-account/react/components/B3Provider/useB3.d.ts +0 -5
  83. package/dist/types/global-account/react/index.native.d.ts +0 -7
  84. package/src/anyspend/index.native.ts +0 -24
  85. package/src/global-account/react/components/B3Provider/types.ts +0 -40
  86. package/src/global-account/react/components/B3Provider/useB3.ts +0 -17
  87. package/src/global-account/react/index.native.ts +0 -14
@@ -1,5 +1,4 @@
1
1
  import { B3_TOKEN, OrderType } from "@b3dotfun/sdk/anyspend";
2
- import { baseMainnet } from "@b3dotfun/sdk/shared/constants/chains/supported";
3
2
  import { EthIcon } from "./icons/EthIcon";
4
3
  import { SolIcon } from "./icons/SolIcon";
5
4
  import { UsdcIcon } from "./icons/USDCIcon";
@@ -20,6 +19,7 @@ import { ArrowRight, Loader2 } from "lucide-react";
20
19
  import { useCallback, useEffect, useState } from "react";
21
20
  import { toast } from "sonner";
22
21
  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,20 +52,6 @@ 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"
69
55
  }
70
56
  ] as const;
71
57
 
@@ -76,37 +62,6 @@ interface PaymentConfig {
76
62
  entryModule: string;
77
63
  }
78
64
 
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
-
110
65
  function generateEncodedDataForBuyEntriesAndSpin(user: string, quantity: string): string {
111
66
  invariant(BigInt(quantity) > 0, "Quantity must be greater than zero");
112
67
  console.log("@@anyspend-buy-spin:encoded-data:", { user, quantity });
@@ -119,7 +74,7 @@ function generateEncodedDataForBuyEntriesAndSpin(user: string, quantity: string)
119
74
  }
120
75
 
121
76
  const basePublicClient = createPublicClient({
122
- chain: baseMainnet,
77
+ chain: base,
123
78
  transport: http()
124
79
  });
125
80
 
@@ -130,7 +85,6 @@ export function AnySpendBuySpin({
130
85
  spinwheelContractAddress,
131
86
  chainId,
132
87
  recipientAddress,
133
- prefillQuantity,
134
88
  onSuccess
135
89
  }: {
136
90
  isMainnet?: boolean;
@@ -139,7 +93,6 @@ export function AnySpendBuySpin({
139
93
  spinwheelContractAddress: string;
140
94
  chainId: number;
141
95
  recipientAddress: string;
142
- prefillQuantity?: string;
143
96
  onSuccess?: (txHash?: string) => void;
144
97
  }) {
145
98
  const hasMounted = useHasMounted();
@@ -149,7 +102,6 @@ export function AnySpendBuySpin({
149
102
  const [paymentConfig, setPaymentConfig] = useState<PaymentConfig | null>(null);
150
103
  const [isLoadingConfig, setIsLoadingConfig] = useState(true);
151
104
  const [configError, setConfigError] = useState<string>("");
152
- const [wheelInfo, setWheelInfo] = useState<WheelInfo | null>(null);
153
105
 
154
106
  // Fetch B3 token balance
155
107
  const {
@@ -168,34 +120,24 @@ export function AnySpendBuySpin({
168
120
  // State for direct buying flow (when user has B3 tokens)
169
121
  const [isBuying, setIsBuying] = useState(false);
170
122
  const [buyingTxHash, setBuyingTxHash] = useState<string>("");
171
- const {
172
- isLoading: isTxPending,
173
- isSuccess: isTxSuccess,
174
- isError: isTxError,
175
- error: txError
176
- } = useWaitForTransactionReceipt({
123
+ const [showSuccessModal, setShowSuccessModal] = useState(false);
124
+
125
+ // Wait for transaction confirmation
126
+ const { isLoading: isTxPending, isSuccess: isTxSuccess } = useWaitForTransactionReceipt({
177
127
  hash: buyingTxHash as `0x${string}`,
178
128
  query: {
179
129
  structuralSharing: false
180
130
  }
181
131
  });
182
132
 
183
- // Handle transaction status
133
+ // Show success modal when transaction is confirmed
184
134
  useEffect(() => {
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);
135
+ if (isTxSuccess && buyingTxHash) {
136
+ setShowAmountPrompt(false);
137
+ setShowSuccessModal(true);
196
138
  setIsBuying(false);
197
139
  }
198
- }, [isTxSuccess, isTxError, buyingTxHash, onSuccess, setB3ModalOpen, txError]);
140
+ }, [isTxSuccess, buyingTxHash]);
199
141
 
200
142
  // Spin quantity state
201
143
  const [userSpinQuantity, setUserSpinQuantity] = useState<string>("");
@@ -204,21 +146,12 @@ export function AnySpendBuySpin({
204
146
  const [validationError, setValidationError] = useState<string>("");
205
147
  const [displayQuantity, setDisplayQuantity] = useState<string>("");
206
148
  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]);
216
149
 
217
150
  // Calculate total cost
218
151
  const totalCost =
219
152
  paymentConfig && userSpinQuantity ? paymentConfig.pricePerEntry * BigInt(userSpinQuantity) : BigInt(0);
220
153
 
221
- // Fetch payment configuration and wheel info
154
+ // Fetch payment configuration
222
155
  const fetchPaymentConfig = useCallback(async () => {
223
156
  if (!basePublicClient || !spinwheelContractAddress) return;
224
157
 
@@ -228,7 +161,7 @@ export function AnySpendBuySpin({
228
161
 
229
162
  console.log("@@anyspend-buy-spin:fetch-config:", { spinwheelContractAddress, chainId });
230
163
 
231
- const [config, entryModuleAddress, wheelInfo] = await Promise.all([
164
+ const [config, entryModuleAddress] = await Promise.all([
232
165
  basePublicClient.readContract({
233
166
  address: spinwheelContractAddress as `0x${string}`,
234
167
  abi: SPIN_WHEEL_ABI,
@@ -238,11 +171,6 @@ export function AnySpendBuySpin({
238
171
  address: spinwheelContractAddress as `0x${string}`,
239
172
  abi: SPIN_WHEEL_ABI,
240
173
  functionName: "entryModule"
241
- }),
242
- basePublicClient.readContract({
243
- address: spinwheelContractAddress as `0x${string}`,
244
- abi: SPIN_WHEEL_ABI,
245
- functionName: "getWheelInfo"
246
174
  })
247
175
  ]);
248
176
 
@@ -253,17 +181,13 @@ export function AnySpendBuySpin({
253
181
  entryModule: entryModuleAddress
254
182
  };
255
183
 
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
-
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
+ });
265
190
  setPaymentConfig(paymentConfig);
266
- setWheelInfo(wheelInfoData);
267
191
  } catch (error) {
268
192
  console.error("@@anyspend-buy-spin:config-error:", error);
269
193
  setConfigError("Failed to load spin wheel configuration");
@@ -282,7 +206,6 @@ export function AnySpendBuySpin({
282
206
  useEffect(() => {
283
207
  const timer = setTimeout(() => {
284
208
  setDebouncedQuantity(displayQuantity);
285
- setDebouncedUserSpinQuantity(userSpinQuantity);
286
209
  }, 500);
287
210
 
288
211
  return () => clearTimeout(timer);
@@ -315,23 +238,13 @@ export function AnySpendBuySpin({
315
238
  }
316
239
 
317
240
  // Check maximum entries per user (0 means no limit)
318
- if (paymentConfig && paymentConfig.maxEntriesPerUser > 0n && BigInt(numValue) > paymentConfig.maxEntriesPerUser) {
241
+ if (paymentConfig && paymentConfig.maxEntriesPerUser > BigInt(0) && BigInt(numValue) > paymentConfig.maxEntriesPerUser) {
319
242
  setIsQuantityValid(false);
320
243
  setUserSpinQuantity("");
321
244
  setValidationError(`Maximum ${paymentConfig.maxEntriesPerUser.toString()} spins allowed`);
322
245
  return;
323
246
  }
324
247
 
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
-
335
248
  setUserSpinQuantity(value);
336
249
  setIsQuantityValid(true);
337
250
  setValidationError("");
@@ -386,7 +299,7 @@ export function AnySpendBuySpin({
386
299
  } catch (error) {
387
300
  console.error("@@anyspend-buy-spin:error:", error);
388
301
  toast.error("Spin purchase failed. Please try again.");
389
- setB3ModalOpen(false);
302
+ setShowSuccessModal(false);
390
303
  } finally {
391
304
  setIsBuying(false);
392
305
  }
@@ -465,40 +378,6 @@ export function AnySpendBuySpin({
465
378
  // Render quantity input prompt
466
379
  if (showAmountPrompt) {
467
380
  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();
502
381
 
503
382
  return (
504
383
  <StyleRoot>
@@ -512,7 +391,7 @@ export function AnySpendBuySpin({
512
391
  filter: hasMounted ? "blur(0px)" : "blur(10px)"
513
392
  }}
514
393
  transition={{ duration: 0.3, delay: 0, ease: "easeInOut" }}
515
- className={`flex justify-center ${isActive ? "mb-4" : ""}`}
394
+ className="mb-4 flex justify-center"
516
395
  >
517
396
  <img
518
397
  alt="B3 Token"
@@ -534,33 +413,15 @@ export function AnySpendBuySpin({
534
413
  transition={{ duration: 0.3, delay: 0.1, ease: "easeInOut" }}
535
414
  className="text-center"
536
415
  >
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
- )}
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>
564
425
  </motion.div>
565
426
  </div>
566
427
 
@@ -574,77 +435,163 @@ export function AnySpendBuySpin({
574
435
  transition={{ duration: 0.3, delay: 0.2, ease: "easeInOut" }}
575
436
  className="bg-b3-react-background w-full p-6"
576
437
  >
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>
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>
585
445
 
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>
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"}
598
457
  </div>
458
+ </div>
599
459
 
600
- {!isQuantityValid && displayQuantity && <p className="text-as-red text-sm">{validationError}</p>}
460
+ {!isQuantityValid && displayQuantity && <p className="text-as-red text-sm">{validationError}</p>}
601
461
 
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>
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>
610
469
  </div>
611
470
  </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
+ }
612
512
 
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>
638
-
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>
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>
646
561
  </div>
647
- ) : null}
562
+ </motion.div>
563
+ </div>
564
+
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>
648
595
  </motion.div>
649
596
  </div>
650
597
  </StyleRoot>
@@ -311,9 +311,6 @@ 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
- }
317
314
  if (EVM_CHAINS[chainId]) {
318
315
  return EVM_CHAINS[chainId].viem.blockExplorers?.default.url + "/tx/" + txHash;
319
316
  }
@@ -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/useB3";
11
+ import { useB3 } from "./B3Provider";
12
12
  import { ManageAccount } from "./ManageAccount/ManageAccount";
13
13
  import { RequestPermissions } from "./RequestPermissions/RequestPermissions";
14
14
  import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow";
@@ -6,7 +6,6 @@ import { Account } from "thirdweb/wallets";
6
6
  // import { RelayKitProviderWrapper } from "./RelayKitProviderWrapper";
7
7
 
8
8
  import { User } from "@b3dotfun/sdk/global-account/types/b3-api.types";
9
- import { B3Context, B3ContextType } from "./types";
10
9
 
11
10
  /**
12
11
  * Default permissions configuration for B3 provider
@@ -18,6 +17,51 @@ const DEFAULT_PERMISSIONS = {
18
17
  endDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365) // 1 year from now
19
18
  };
20
19
 
20
+ /**
21
+ * Context type for B3Provider
22
+ */
23
+
24
+ export interface B3ContextType {
25
+ account?: Account;
26
+ user?: User;
27
+ setAccount: (account: Account) => void;
28
+ setUser: (user?: User) => void;
29
+ initialized: boolean;
30
+ ready: boolean;
31
+ environment?: "development" | "production";
32
+ defaultPermissions?: PermissionsConfig;
33
+ theme: "light" | "dark";
34
+ }
35
+
36
+ /**
37
+ * Context for B3 provider
38
+ */
39
+ export const B3Context = createContext<B3ContextType>({
40
+ account: undefined,
41
+ user: undefined,
42
+ setAccount: () => {},
43
+ setUser: () => {},
44
+ initialized: false,
45
+ ready: false,
46
+ environment: "development",
47
+ theme: "light"
48
+ });
49
+
50
+ /**
51
+ * Hook to access the B3 context
52
+ * @throws Error if used outside a B3Provider
53
+ */
54
+ export function useB3() {
55
+ const context = useContext(B3Context);
56
+
57
+ if (!context.initialized) {
58
+ throw new Error("useB3 must be used within a B3Provider");
59
+ }
60
+
61
+ // Return a stable reference
62
+ return useMemo(() => context, [context]);
63
+ }
64
+
21
65
  // Create queryClient instance
22
66
  const queryClient = new QueryClient();
23
67
 
@@ -81,9 +125,6 @@ export function InnerProvider({
81
125
  <B3Context.Provider
82
126
  value={{
83
127
  account: effectiveAccount,
84
- automaticallySetFirstEoa: false,
85
- setWallet: () => {},
86
- wallet: undefined,
87
128
  setAccount,
88
129
  user,
89
130
  setUser,