@b3dotfun/sdk 0.0.79 → 0.0.80-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 (53) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +23 -10
  2. package/dist/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.js +9 -2
  3. package/dist/cjs/anyspend/react/components/AnySpendCustom.d.ts +2 -0
  4. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +75 -19
  5. package/dist/cjs/anyspend/react/components/AnySpendNFT.js +6 -9
  6. package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +1 -3
  7. package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.d.ts +6 -1
  8. package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +40 -13
  9. package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +13 -7
  10. package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +5 -3
  11. package/dist/cjs/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
  12. package/dist/cjs/anyspend/react/hooks/useGeoOnrampOptions.js +4 -2
  13. package/dist/cjs/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
  14. package/dist/cjs/anyspend/react/hooks/useStripeSupport.js +1 -0
  15. package/dist/cjs/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +5 -3
  16. package/dist/cjs/shared/utils/ipfs.d.ts +3 -3
  17. package/dist/cjs/shared/utils/ipfs.js +8 -8
  18. package/dist/esm/anyspend/react/components/AnySpend.js +23 -10
  19. package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.js +9 -2
  20. package/dist/esm/anyspend/react/components/AnySpendCustom.d.ts +2 -0
  21. package/dist/esm/anyspend/react/components/AnySpendCustom.js +76 -20
  22. package/dist/esm/anyspend/react/components/AnySpendNFT.js +7 -10
  23. package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +1 -3
  24. package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.d.ts +6 -1
  25. package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +39 -12
  26. package/dist/esm/anyspend/react/components/common/PanelOnramp.js +14 -8
  27. package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +5 -3
  28. package/dist/esm/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
  29. package/dist/esm/anyspend/react/hooks/useGeoOnrampOptions.js +4 -2
  30. package/dist/esm/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
  31. package/dist/esm/anyspend/react/hooks/useStripeSupport.js +1 -0
  32. package/dist/esm/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +5 -3
  33. package/dist/esm/shared/utils/ipfs.d.ts +3 -3
  34. package/dist/esm/shared/utils/ipfs.js +8 -8
  35. package/dist/styles/index.css +1 -1
  36. package/dist/types/anyspend/react/components/AnySpendCustom.d.ts +2 -0
  37. package/dist/types/anyspend/react/components/common/FiatPaymentMethod.d.ts +6 -1
  38. package/dist/types/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
  39. package/dist/types/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
  40. package/dist/types/shared/utils/ipfs.d.ts +3 -3
  41. package/package.json +1 -1
  42. package/src/anyspend/react/components/AnySpend.tsx +24 -10
  43. package/src/anyspend/react/components/AnySpendCollectorClubPurchase.tsx +9 -1
  44. package/src/anyspend/react/components/AnySpendCustom.tsx +113 -59
  45. package/src/anyspend/react/components/AnySpendNFT.tsx +49 -48
  46. package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +1 -4
  47. package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +53 -21
  48. package/src/anyspend/react/components/common/PanelOnramp.tsx +36 -40
  49. package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +53 -16
  50. package/src/anyspend/react/hooks/useGeoOnrampOptions.ts +5 -2
  51. package/src/anyspend/react/hooks/useStripeSupport.ts +1 -0
  52. package/src/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.tsx +5 -7
  53. package/src/shared/utils/ipfs.ts +8 -8
@@ -7,9 +7,18 @@ import { ChevronLeft, ChevronRight, Loader2 } from "lucide-react";
7
7
  export enum FiatPaymentMethod {
8
8
  NONE = "none",
9
9
  COINBASE_PAY = "coinbase_pay",
10
- STRIPE = "stripe",
10
+ STRIPE = "stripe", // Stripe redirect (one-click buy URL)
11
+ STRIPE_WEB2 = "stripe_web2", // Stripe embedded payment
11
12
  }
12
13
 
14
+ // Shared display config for fiat payment methods
15
+ export const FIAT_PAYMENT_METHOD_DISPLAY: Record<FiatPaymentMethod, { icon: string; label: string } | null> = {
16
+ [FiatPaymentMethod.COINBASE_PAY]: { icon: "C", label: "Coinbase Pay" },
17
+ [FiatPaymentMethod.STRIPE]: { icon: "S", label: "Pay via Stripe" },
18
+ [FiatPaymentMethod.STRIPE_WEB2]: { icon: "S", label: "Pay with Card" },
19
+ [FiatPaymentMethod.NONE]: null,
20
+ };
21
+
13
22
  interface FiatPaymentMethodProps {
14
23
  selectedPaymentMethod: FiatPaymentMethod;
15
24
  setSelectedPaymentMethod: (method: FiatPaymentMethod) => void;
@@ -25,30 +34,34 @@ export function FiatPaymentMethodComponent({
25
34
  onSelectPaymentMethod,
26
35
  srcAmountOnRamp,
27
36
  }: FiatPaymentMethodProps) {
37
+ // Load geo-based onramp options like in PanelOnramp
38
+ const {
39
+ coinbaseAvailablePaymentMethods,
40
+ stripeOnrampSupport,
41
+ stripeWeb2Support,
42
+ isLoading: isLoadingGeoOnramp,
43
+ } = useGeoOnrampOptions(srcAmountOnRamp);
44
+
28
45
  // Helper function to get fees from API data
29
46
  const getFeeFromApi = (paymentMethod: FiatPaymentMethod): string | null => {
30
47
  switch (paymentMethod) {
31
48
  case FiatPaymentMethod.COINBASE_PAY:
32
49
  // Coinbase doesn't provide fee info in API, return null
33
50
  return null;
34
- case FiatPaymentMethod.STRIPE:
35
- // Get fee from Stripe API response
51
+ case FiatPaymentMethod.STRIPE_WEB2:
52
+ // Get fee from Stripe Web2 API response
36
53
  if (stripeWeb2Support && "formattedFeeUsd" in stripeWeb2Support) {
37
54
  return stripeWeb2Support.formattedFeeUsd;
38
55
  }
39
56
  return null;
57
+ case FiatPaymentMethod.STRIPE:
58
+ // Stripe redirect doesn't have fee info from API
59
+ return null;
40
60
  default:
41
61
  return null; // No fee when no payment method selected
42
62
  }
43
63
  };
44
64
 
45
- // Load geo-based onramp options like in PanelOnramp
46
- const {
47
- coinbaseAvailablePaymentMethods,
48
- stripeWeb2Support,
49
- isLoading: isLoadingGeoOnramp,
50
- } = useGeoOnrampOptions(srcAmountOnRamp);
51
-
52
65
  // Generate payment methods based on geo availability (like in PanelOnrampPayment)
53
66
  const availablePaymentMethods = [];
54
67
 
@@ -65,15 +78,28 @@ export function FiatPaymentMethodComponent({
65
78
  });
66
79
  }
67
80
 
68
- // Add Stripe if available
69
- if (stripeWeb2Support && stripeWeb2Support.isSupport) {
70
- const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE);
81
+ // Add Stripe redirect (one-click) if available - redirects to Stripe checkout
82
+ if (stripeOnrampSupport) {
83
+ const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE_WEB2); // Use same fee estimate
71
84
  availablePaymentMethods.push({
72
85
  id: FiatPaymentMethod.STRIPE,
73
- name: "Stripe",
74
- description: "Credit or debit card payment",
75
- badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : "Standard Fee",
76
- badgeColor: "bg-yellow-100 text-yellow-800",
86
+ name: "Pay via Stripe",
87
+ description: "Redirects to Stripe checkout",
88
+ badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : undefined,
89
+ badgeColor: "bg-gray-100 text-gray-800",
90
+ available: true,
91
+ });
92
+ }
93
+
94
+ // Add Stripe Web2 (embedded) if available - embedded card form
95
+ if (stripeWeb2Support && stripeWeb2Support.isSupport) {
96
+ const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE_WEB2);
97
+ availablePaymentMethods.push({
98
+ id: FiatPaymentMethod.STRIPE_WEB2,
99
+ name: "Pay with Card",
100
+ description: "Fast checkout",
101
+ badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : undefined,
102
+ badgeColor: "bg-gray-100 text-gray-800",
77
103
  available: true,
78
104
  });
79
105
  }
@@ -144,16 +170,22 @@ export function FiatPaymentMethodComponent({
144
170
  >
145
171
  {/* Icon - matching PanelOnramp style */}
146
172
  <div className="flex h-12 w-12 items-center justify-center rounded-full bg-blue-600 text-2xl text-white">
147
- {method.id === FiatPaymentMethod.COINBASE_PAY ? "C" : "S"}
173
+ {method.id === FiatPaymentMethod.COINBASE_PAY
174
+ ? "C"
175
+ : method.id === FiatPaymentMethod.STRIPE || method.id === FiatPaymentMethod.STRIPE_WEB2
176
+ ? "S"
177
+ : "?"}
148
178
  </div>
149
179
 
150
180
  {/* Content */}
151
181
  <div className="flex flex-1 flex-col items-start text-left">
152
182
  <div className="flex items-center gap-2">
153
183
  <span className="text-as-primary text-base font-semibold">{method.name}</span>
154
- <span className={cn("rounded-full px-2 py-1 text-xs font-medium", method.badgeColor)}>
155
- {method.badge}
156
- </span>
184
+ {method.badge && (
185
+ <span className={cn("rounded-full px-2 py-1 text-xs font-medium", method.badgeColor)}>
186
+ {method.badge}
187
+ </span>
188
+ )}
157
189
  </div>
158
190
  <span className="text-as-primary/60 text-sm">{method.description}</span>
159
191
  </div>
@@ -8,8 +8,7 @@ import { formatAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
8
8
  import { ChevronRight, Info, Wallet } from "lucide-react";
9
9
  import { useRef } from "react";
10
10
 
11
- import { useFeatureFlags } from "../../contexts/FeatureFlagsContext";
12
- import { FiatPaymentMethod } from "./FiatPaymentMethod";
11
+ import { FIAT_PAYMENT_METHOD_DISPLAY, FiatPaymentMethod } from "./FiatPaymentMethod";
13
12
  import { OrderTokenAmountFiat } from "./OrderTokenAmountFiat";
14
13
  import { PointsBadge } from "./PointsBadge";
15
14
 
@@ -54,8 +53,6 @@ export function PanelOnramp({
54
53
  onShowFeeDetail?: () => void;
55
54
  customUsdInputValues?: string[];
56
55
  }) {
57
- const featureFlags = useFeatureFlags();
58
-
59
56
  // Helper function to get fees from anyspend quote
60
57
  const getFeeFromApi = (paymentMethod: FiatPaymentMethod): number | null => {
61
58
  // Try to get fee from anyspend quote first (most accurate)
@@ -84,11 +81,16 @@ export function PanelOnramp({
84
81
  const getTotalAmount = (paymentMethod: FiatPaymentMethod): number => {
85
82
  const baseAmount = parseFloat(srcAmountOnRamp) || 5;
86
83
 
87
- // Try to get from anyspend quote first (most accurate)
84
+ // For stripeweb2_fee, use the originalAmount
88
85
  if (anyspendQuote?.data?.fee?.type === "stripeweb2_fee") {
89
86
  return Number(anyspendQuote.data.fee.originalAmount) / 1e6; // Convert from wei to USD
90
87
  }
91
88
 
89
+ // Use currencyIn.amountUsd from quote when available (includes fees, most accurate for custom orders)
90
+ if (anyspendQuote?.data?.currencyIn?.amountUsd) {
91
+ return Number(anyspendQuote.data.currencyIn.amountUsd);
92
+ }
93
+
92
94
  const fee = getFeeFromApi(paymentMethod);
93
95
 
94
96
  // For Coinbase or when fee is available, calculate manually
@@ -139,32 +141,28 @@ export function PanelOnramp({
139
141
  className="text-as-tertiarry flex h-7 items-center gap-1 text-sm"
140
142
  onClick={() => setActivePanel(fiatPaymentMethodIndex)} // PanelView.FIAT_PAYMENT_METHOD
141
143
  >
142
- {selectedPaymentMethod === FiatPaymentMethod.COINBASE_PAY ? (
143
- <>
144
- <div className="flex items-center gap-2">
145
- <div className="flex h-5 w-5 items-center justify-center rounded-full bg-blue-600">
146
- <span className="text-xs font-bold text-white">C</span>
147
- </div>
148
- Coinbase Pay
149
- </div>
150
- <ChevronRight className="h-4 w-4" />
151
- </>
152
- ) : selectedPaymentMethod === FiatPaymentMethod.STRIPE ? (
153
- <>
154
- <div className="flex items-center gap-2">
155
- <div className="flex h-5 w-5 items-center justify-center rounded-full bg-blue-600">
156
- <span className="text-xs font-bold text-white">S</span>
157
- </div>
158
- Stripe
159
- </div>
160
- <ChevronRight className="h-4 w-4" />
161
- </>
162
- ) : (
163
- <>
164
- Select payment method
165
- <ChevronRight className="h-4 w-4" />
166
- </>
167
- )}
144
+ {(() => {
145
+ const config = selectedPaymentMethod ? FIAT_PAYMENT_METHOD_DISPLAY[selectedPaymentMethod] : null;
146
+ if (config) {
147
+ return (
148
+ <>
149
+ <div className="flex items-center gap-2">
150
+ <div className="flex h-5 w-5 items-center justify-center rounded-full bg-blue-600">
151
+ <span className="text-xs font-bold text-white">{config.icon}</span>
152
+ </div>
153
+ {config.label}
154
+ </div>
155
+ <ChevronRight className="h-4 w-4" />
156
+ </>
157
+ );
158
+ }
159
+ return (
160
+ <>
161
+ Select payment method
162
+ <ChevronRight className="h-4 w-4" />
163
+ </>
164
+ );
165
+ })()}
168
166
  </button>
169
167
  </div>
170
168
 
@@ -279,15 +277,13 @@ export function PanelOnramp({
279
277
  <Info className="h-4 w-4" />
280
278
  </button>
281
279
  )}
282
- {featureFlags.showPoints &&
283
- anyspendQuote?.data?.pointsAmount &&
284
- anyspendQuote?.data?.pointsAmount > 0 && (
285
- <PointsBadge
286
- pointsAmount={anyspendQuote.data.pointsAmount}
287
- pointsMultiplier={anyspendQuote.data.pointsMultiplier}
288
- onClick={() => onShowPointsDetail?.()}
289
- />
290
- )}
280
+ {anyspendQuote?.data?.pointsAmount && anyspendQuote?.data?.pointsAmount > 0 && (
281
+ <PointsBadge
282
+ pointsAmount={anyspendQuote.data.pointsAmount}
283
+ pointsMultiplier={anyspendQuote.data.pointsMultiplier}
284
+ onClick={() => onShowPointsDetail?.()}
285
+ />
286
+ )}
291
287
  </div>
292
288
  <div className="flex flex-col items-end gap-0.5">
293
289
  <span className="text-as-primary font-semibold">
@@ -65,6 +65,7 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
65
65
  geoData,
66
66
  coinbaseOnrampOptions,
67
67
  coinbaseAvailablePaymentMethods,
68
+ stripeOnrampSupport,
68
69
  stripeWeb2Support,
69
70
  isLoading: isLoadingGeoOnramp,
70
71
  } = useGeoOnrampOptions(srcAmountOnRamp);
@@ -201,16 +202,18 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
201
202
  >
202
203
  ${parseFloat(srcAmountOnRamp).toFixed(2)}
203
204
  </p>
204
- {anyspendQuote?.data?.fee?.type === "standard_fee" && anyspendQuote.data.currencyIn?.amountUsd && (
205
- <p className="text-b3-react-foreground/60 text-xs">
206
- incl. $
207
- {(
208
- (Number(anyspendQuote.data.currencyIn.amountUsd) * anyspendQuote.data.fee.finalFeeBps) /
209
- 10000
210
- ).toFixed(2)}{" "}
211
- fee
212
- </p>
213
- )}
205
+ {anyspendQuote?.data?.fee?.type === "standard_fee" &&
206
+ anyspendQuote.data.currencyIn?.amountUsd &&
207
+ anyspendQuote.data.fee.finalFeeBps > 0 && (
208
+ <p className="text-b3-react-foreground/60 text-xs">
209
+ incl. $
210
+ {(
211
+ (Number(anyspendQuote.data.currencyIn.amountUsd) * anyspendQuote.data.fee.finalFeeBps) /
212
+ 10000
213
+ ).toFixed(2)}{" "}
214
+ fee
215
+ </p>
216
+ )}
214
217
  </div>
215
218
  </div>
216
219
  </div>
@@ -311,7 +314,37 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
311
314
  );
312
315
  })()}
313
316
 
314
- {/* Stripe Option - Show if supported */}
317
+ {/* Stripe Redirect Option - Primary option */}
318
+ {stripeOnrampSupport && (
319
+ <button
320
+ onClick={() => handlePaymentMethodClick("stripe")}
321
+ className="bg-b3-react-background border-b3-react-border hover:border-as-brand group flex w-full items-center justify-between gap-4 rounded-xl border p-5 transition-all duration-200 hover:shadow-md"
322
+ >
323
+ <div className="flex items-center gap-4">
324
+ <div className="flex h-12 w-12 items-center justify-center rounded-full bg-purple-50">
325
+ <img
326
+ src="https://raw.githubusercontent.com/stripe/stripe.github.io/455f506a628dc3f6c505e3001db45a64e29e9fc3/images/stripe-logo.svg"
327
+ alt="Stripe"
328
+ className="h-5"
329
+ />
330
+ </div>
331
+ <div className="flex flex-col items-start text-left">
332
+ <h4 className="text-b3-react-foreground text-lg font-semibold">Credit/Debit Card</h4>
333
+ <p className="text-b3-react-foreground/60 text-sm">Pay via Stripe checkout</p>
334
+ {stripeWeb2Support?.isSupport && stripeWeb2Support.formattedFeeUsd && (
335
+ <div className="mt-1">
336
+ <span className="text-xs text-gray-500">
337
+ ${Number(stripeWeb2Support.formattedFeeUsd).toFixed(2)} fee
338
+ </span>
339
+ </div>
340
+ )}
341
+ </div>
342
+ </div>
343
+ <ChevronRight className="text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" />
344
+ </button>
345
+ )}
346
+
347
+ {/* Stripe Web2 Option - Embedded payment (secondary) */}
315
348
  {stripeWeb2Support.isSupport && (
316
349
  <button
317
350
  onClick={() => handlePaymentMethodClick("stripe-web2")}
@@ -326,11 +359,15 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
326
359
  />
327
360
  </div>
328
361
  <div className="flex flex-col items-start text-left">
329
- <h4 className="text-b3-react-foreground text-lg font-semibold">Stripe</h4>
330
- <p className="text-b3-react-foreground/60 text-sm">Credit or debit card payment</p>
331
- <div className="mt-1 flex items-center gap-1">
332
- <span className="text-xs font-medium text-orange-600">Fee Applied</span>
333
- </div>
362
+ <h4 className="text-b3-react-foreground text-lg font-semibold">Quick Pay</h4>
363
+ <p className="text-b3-react-foreground/60 text-sm">Credit or debit card</p>
364
+ {stripeWeb2Support.formattedFeeUsd && (
365
+ <div className="mt-1">
366
+ <span className="text-xs text-gray-500">
367
+ ${Number(stripeWeb2Support.formattedFeeUsd).toFixed(2)} fee
368
+ </span>
369
+ </div>
370
+ )}
334
371
  </div>
335
372
  </div>
336
373
  <ChevronRight className="text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" />
@@ -20,7 +20,7 @@ export function useGeoOnrampOptions(srcFiatAmount: string) {
20
20
  const { geoData, loading: isLoadingGeo, error: geoError } = useGetGeo();
21
21
  const { coinbaseOnrampOptions, isLoadingCoinbaseOnrampOptions, coinbaseOnrampOptionsError } =
22
22
  useCoinbaseOnrampOptions(geoData?.country, visitorData);
23
- const { stripeWeb2Support, isLoadingStripeSupport, stripeSupportError } = useStripeSupport(
23
+ const { stripeOnrampSupport, stripeWeb2Support, isLoadingStripeSupport, stripeSupportError } = useStripeSupport(
24
24
  srcFiatAmount,
25
25
  visitorData,
26
26
  );
@@ -43,8 +43,10 @@ export function useGeoOnrampOptions(srcFiatAmount: string) {
43
43
  geoData,
44
44
  coinbaseOnrampOptions,
45
45
  coinbaseAvailablePaymentMethods,
46
+ stripeOnrampSupport,
46
47
  stripeWeb2Support,
47
- isOnrampSupported: coinbaseAvailablePaymentMethods.length > 0 || stripeWeb2Support,
48
+ isOnrampSupported:
49
+ coinbaseAvailablePaymentMethods.length > 0 || stripeOnrampSupport || stripeWeb2Support.isSupport,
48
50
  isLoading: isLoadingGeo || isLoadingCoinbaseOnrampOptions || isLoadingStripeSupport || isLoadingVisitorData,
49
51
  isLoadingGeo,
50
52
  isLoadingCoinbaseOnrampOptions,
@@ -57,6 +59,7 @@ export function useGeoOnrampOptions(srcFiatAmount: string) {
57
59
  geoData,
58
60
  coinbaseOnrampOptions,
59
61
  coinbaseAvailablePaymentMethods,
62
+ stripeOnrampSupport,
60
63
  stripeWeb2Support,
61
64
  isLoadingGeo,
62
65
  isLoadingCoinbaseOnrampOptions,
@@ -12,6 +12,7 @@ export function useStripeSupport(usdAmount?: string, visitorData?: VisitorData,
12
12
 
13
13
  return useMemo(
14
14
  () => ({
15
+ stripeOnrampSupport: data?.stripeOnramp || false,
15
16
  stripeWeb2Support: data?.stripeWeb2 || { isSupport: false },
16
17
  isLoadingStripeSupport: isLoading,
17
18
  stripeSupportError: error,
@@ -1,13 +1,10 @@
1
1
  "use client";
2
2
 
3
+ import { getIpfsUrl } from "@b3dotfun/sdk/shared/utils/ipfs";
3
4
  import { client as defaultClient } from "@b3dotfun/sdk/shared/utils/thirdweb";
4
5
  import type { ThirdwebClient } from "thirdweb";
5
6
  import { MediaRenderer } from "thirdweb/react";
6
7
 
7
- // Primary IPFS gateway URL - matches our allowed list in profileDisplay.ts
8
- // Note: MediaRenderer expects the base gateway URL without /ipfs path
9
- // const IPFS_GATEWAY_URL = "https://cloudflare-ipfs.com";
10
-
11
8
  interface IPFSMediaRendererProps {
12
9
  /** The source URL - can be IPFS URL (ipfs://...) or HTTP URL */
13
10
  src: string | null | undefined;
@@ -66,12 +63,13 @@ export function IPFSMediaRenderer({
66
63
  );
67
64
  }
68
65
 
69
- // Convert IPFS URLs to HTTP gateway URLs if needed
70
- // This handles both ipfs:// URLs and existing HTTP gateway URLs
66
+ // Convert IPFS URLs to HTTP gateway URLs using our preferred gateway
67
+ // This avoids Thirdweb's default cloudflare-ipfs.com which can be unreliable
68
+ const resolvedSrc = src.startsWith("ipfs://") ? getIpfsUrl(src) : src;
71
69
 
72
70
  return (
73
71
  <MediaRenderer
74
- src={src}
72
+ src={resolvedSrc}
75
73
  client={client}
76
74
  alt={alt}
77
75
  className={className}
@@ -3,23 +3,23 @@
3
3
  * These gateways must match the allowed list in profileDisplay.ts validateImageUrl()
4
4
  */
5
5
  const IPFS_GATEWAYS = [
6
- "https://cloudflare-ipfs.com/ipfs", // Primary gateway - fast and reliable
6
+ "https://dweb.link/ipfs", // Primary gateway - Protocol Labs maintained
7
+ "https://w3s.link/ipfs", // web3.storage gateway - reliable
8
+ "https://nftstorage.link/ipfs", // NFT.storage gateway
9
+ "https://gateway.pinata.cloud/ipfs", // Pinata gateway
7
10
  "https://ipfs.io/ipfs", // Fallback gateway
8
- "https://gateway.pinata.cloud/ipfs", // Additional option
9
- "https://dweb.link/ipfs", // Additional option
10
- "https://nftstorage.link/ipfs", // Additional option
11
- "https://w3s.link/ipfs", // Additional option
11
+ "https://cloudflare-ipfs.com/ipfs", // Cloudflare gateway (can be slow/unreliable)
12
12
  ] as const;
13
13
 
14
14
  /**
15
15
  * Converts an IPFS URL to a gateway URL
16
16
  * @param ipfsUrl - URL in format ipfs://CID/path or just the CID
17
- * @param gatewayIndex - Optional index to specify which gateway to use (0: Cloudflare, 1: ipfs.io)
17
+ * @param gatewayIndex - Optional index to specify which gateway to use (0: dweb.link, 1: w3s.link, etc.)
18
18
  * @returns HTTP URL using the specified IPFS gateway
19
19
  * @example
20
- * // returns 'https://cloudflare-ipfs.com/ipfs/QmUbJ4p.../2.png'
20
+ * // returns 'https://dweb.link/ipfs/QmUbJ4p.../2.png'
21
21
  * getIpfsUrl('ipfs://QmUbJ4pnHMNXGeWWhBFFSEqCGuc6cEtDyz35wQfv7k2TXy/2.png')
22
- * // returns 'https://ipfs.io/ipfs/QmUbJ4p.../2.png'
22
+ * // returns 'https://w3s.link/ipfs/QmUbJ4p.../2.png'
23
23
  * getIpfsUrl('ipfs://QmUbJ4pnHMNXGeWWhBFFSEqCGuc6cEtDyz35wQfv7k2TXy/2.png', 1)
24
24
  */
25
25
  export function getIpfsUrl(ipfsUrl: string, gatewayIndex = 0): string {