@0xsequence/marketplace-sdk 2.0.0 → 2.0.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 (89) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/BellIcon.js +1 -1
  3. package/dist/Card.js +1 -1
  4. package/dist/ShopCard.d.ts +4 -4
  5. package/dist/collectible.js +2 -2
  6. package/dist/collectible.js.map +1 -1
  7. package/dist/collection.js +1 -1
  8. package/dist/create-config.d.ts +8 -8
  9. package/dist/currency.js +3 -3
  10. package/dist/currency.js.map +1 -1
  11. package/dist/expirationDateSelect.js +1 -1
  12. package/dist/filters.d.ts +3 -3
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +2 -2
  15. package/dist/index10.d.ts +2 -2
  16. package/dist/index11.d.ts +14 -14
  17. package/dist/index12.d.ts +25 -25
  18. package/dist/index14.d.ts +2 -2
  19. package/dist/index16.d.ts +2 -2
  20. package/dist/index17.d.ts +85 -85
  21. package/dist/index18.d.ts +36 -36
  22. package/dist/index21.d.ts +14 -14
  23. package/dist/index22.d.ts +4 -4
  24. package/dist/index23.d.ts +11 -11
  25. package/dist/index26.d.ts +3 -3
  26. package/dist/index27.d.ts +3 -3
  27. package/dist/index28.d.ts +9 -9
  28. package/dist/index31.d.ts +4 -4
  29. package/dist/index33.d.ts +2 -2
  30. package/dist/index34.d.ts +3 -3
  31. package/dist/index36.d.ts +2 -2
  32. package/dist/index37.d.ts +2 -0
  33. package/dist/index38.d.ts +3 -3
  34. package/dist/index4.d.ts +458 -458
  35. package/dist/index40.d.ts +2 -2
  36. package/dist/index8.d.ts +10 -2
  37. package/dist/inventory.d.ts +3 -3
  38. package/dist/inventory.js +2 -2
  39. package/dist/inventory.js.map +1 -1
  40. package/dist/marketplace2.js +2 -2
  41. package/dist/marketplace2.js.map +1 -1
  42. package/dist/metadata.d.ts +72 -72
  43. package/dist/primary-sale-checkout-options.d.ts +3 -3
  44. package/dist/quantityInput.js +1 -1
  45. package/dist/ranges.d.ts +16 -16
  46. package/dist/react/ssr/index.d.ts +2 -2
  47. package/dist/react/ssr/index.js +2 -2
  48. package/dist/react/ui/components/marketplace-logos/index.d.ts +21 -21
  49. package/dist/react/ui/modals/_internal/components/baseModal/index.d.ts +6 -6
  50. package/dist/react/ui/modals/_internal/components/calendarDropdown/index.d.ts +2 -2
  51. package/dist/react/ui/modals/_internal/components/currencyImage/index.d.ts +2 -2
  52. package/dist/react/ui/modals/_internal/components/currencyOptionsSelect/index.d.ts +2 -2
  53. package/dist/react/ui/modals/_internal/components/floorPriceText/index.d.ts +2 -2
  54. package/dist/react/ui/modals/_internal/components/priceInput/index.d.ts +2 -4
  55. package/dist/react/ui/modals/_internal/components/selectWaasFeeOptions/index.d.ts +2 -2
  56. package/dist/react/ui/modals/_internal/components/switchChainErrorModal/index.d.ts +2 -2
  57. package/dist/react/ui/modals/_internal/components/timeAgo/index.d.ts +2 -2
  58. package/dist/react/ui/modals/_internal/components/tokenPreview/index.d.ts +2 -2
  59. package/dist/react/ui/modals/_internal/components/transaction-footer/index.d.ts +3 -3
  60. package/dist/react/ui/modals/_internal/components/transactionStatusModal/index.d.ts +2 -2
  61. package/dist/react.js +114 -43
  62. package/dist/react.js.map +1 -1
  63. package/dist/styles/index.css +15 -0
  64. package/dist/token-balances.d.ts +27 -27
  65. package/dist/transaction-footer.js +1 -1
  66. package/dist/utils/index.d.ts +2 -2
  67. package/dist/utils/index.js +2 -2
  68. package/dist/utils.js +12 -1
  69. package/dist/utils.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/react/hooks/currency/list.test.tsx +23 -2
  72. package/src/react/hooks/transactions/useMarketTransactionSteps.tsx +55 -15
  73. package/src/react/queries/collectible/market-list.ts +5 -3
  74. package/src/react/queries/currency/list.ts +8 -5
  75. package/src/react/queries/inventory/inventory.ts +5 -3
  76. package/src/react/queries/marketplace/filters.ts +5 -3
  77. package/src/react/ui/modals/BuyModal/components/BuyModalContent.tsx +40 -6
  78. package/src/react/ui/modals/BuyModal/hooks/useMarketPlatformFee.ts +5 -5
  79. package/src/react/ui/modals/CreateListingModal/internal/store.ts +5 -2
  80. package/src/react/ui/modals/MakeOfferModal/internal/context.ts +20 -1
  81. package/src/react/ui/modals/MakeOfferModal/internal/helpers/validation.ts +16 -1
  82. package/src/react/ui/modals/MakeOfferModal/internal/store.ts +5 -2
  83. package/src/react/ui/modals/SellModal/internal/store.ts +5 -2
  84. package/src/react/ui/modals/_internal/components/baseModal/errors/ModalInitializationError.tsx +8 -6
  85. package/src/react/ui/modals/_internal/components/priceInput/index.tsx +2 -9
  86. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +5 -2
  87. package/src/styles/styles.ts +18 -0
  88. package/src/utils/collection.ts +19 -0
  89. package/src/utils/index.ts +1 -0
@@ -10,12 +10,14 @@ import {
10
10
  Text,
11
11
  } from '@0xsequence/design-system';
12
12
  import { TrailsWidget } from '0xtrails/widget';
13
+ import { useRef, useState } from 'react';
13
14
  import {
14
15
  getSequenceApiUrl,
15
16
  getSequenceIndexerUrl,
16
17
  getSequenceNodeGatewayUrl,
17
18
  getTrailsApiUrl,
18
19
  } from '../../../../_internal/api/services';
20
+ import { cn } from '../../../../ssr';
19
21
  import { ModalInitializationError } from '../../_internal/components/baseModal/errors/ModalInitializationError';
20
22
  import { MODAL_OVERLAY_PROPS } from '../../_internal/components/consts';
21
23
  import { useBuyModalContext } from '../internal/buyModalContext';
@@ -56,12 +58,12 @@ export const BuyModalContent = () => {
56
58
  if (error) {
57
59
  return (
58
60
  <Modal
59
- isDismissible={false}
61
+ isDismissible={true}
60
62
  onClose={close}
61
63
  overlayProps={MODAL_OVERLAY_PROPS}
62
64
  contentProps={{
63
65
  style: {
64
- width: '450px',
66
+ width: '400px',
65
67
  height: 'auto',
66
68
  },
67
69
  className: 'overflow-y-auto',
@@ -101,10 +103,7 @@ export const BuyModalContent = () => {
101
103
 
102
104
  {isLoading && (
103
105
  <div className="flex w-full items-center justify-center py-8">
104
- <div className="flex flex-col items-center gap-4">
105
- <Spinner size="lg" />
106
- <Text className="text-text-80">Loading payment options...</Text>
107
- </div>
106
+ <ProgressiveLoadingMessage />
108
107
  </div>
109
108
  )}
110
109
 
@@ -159,3 +158,38 @@ export const BuyModalContent = () => {
159
158
  </Dialog>
160
159
  );
161
160
  };
161
+
162
+ const ProgressiveLoadingMessage = () => {
163
+ const [showSecondaryMessage, setShowSecondaryMessage] = useState(false);
164
+ const timerRef = useRef<NodeJS.Timeout | null>(null);
165
+
166
+ if (!timerRef.current) {
167
+ timerRef.current = setTimeout(() => {
168
+ setShowSecondaryMessage(true);
169
+ }, 3000);
170
+ }
171
+
172
+ return (
173
+ <div className="flex items-center gap-4">
174
+ <div
175
+ className={cn(
176
+ 'transition-all duration-300',
177
+ showSecondaryMessage ? 'h-10 w-10' : 'h-5 w-5',
178
+ )}
179
+ >
180
+ <Spinner className="h-full w-full transition-all duration-150" />
181
+ </div>
182
+
183
+ <div className="flex flex-col gap-2">
184
+ <p className="animate-pulse text-text-100">
185
+ Loading payment options...
186
+ </p>
187
+ {showSecondaryMessage && (
188
+ <p className="text-small text-text-50">
189
+ This is taking longer than expected.
190
+ </p>
191
+ )}
192
+ </div>
193
+ </div>
194
+ );
195
+ };
@@ -1,6 +1,6 @@
1
1
  import { skipToken } from '@tanstack/react-query';
2
2
  import { avalanche, optimism } from 'viem/chains';
3
- import { compareAddress } from '../../../../../utils';
3
+ import { findMarketCollection } from '../../../../../utils';
4
4
  import type { AdditionalFee } from '../../../../_internal';
5
5
  import { useMarketplaceConfig } from '../../../../hooks';
6
6
 
@@ -28,10 +28,10 @@ export const useMarketPlatformFee = (params: FeesParams | typeof skipToken) => {
28
28
  const { chainId, collectionAddress } = params;
29
29
 
30
30
  // Find collection in market collections only (not shop collections)
31
- const marketCollection = marketplaceConfig?.market?.collections?.find(
32
- (col) =>
33
- compareAddress(col.itemsAddress, collectionAddress) &&
34
- String(col.chainId) === String(chainId),
31
+ const marketCollection = findMarketCollection(
32
+ marketplaceConfig?.market?.collections ?? [],
33
+ collectionAddress,
34
+ chainId,
35
35
  );
36
36
 
37
37
  const avalancheOrOptimism =
@@ -1,6 +1,7 @@
1
1
  import type { Address } from '@0xsequence/api-client';
2
2
  import { createStore } from '@xstate/store';
3
3
  import { useSelector } from '@xstate/store/react';
4
+ import { findMarketCollection } from '../../../../../utils';
4
5
  import { useMarketplaceConfig } from '../../../hooks';
5
6
 
6
7
  export type OpenCreateListingModalArgs = {
@@ -109,8 +110,10 @@ export const useCreateListingModalState = () => {
109
110
  } = useSelector(createListingModalStore, (state) => state.context);
110
111
 
111
112
  const { data: marketplaceConfig } = useMarketplaceConfig();
112
- const orderbookKind = marketplaceConfig?.market.collections.find(
113
- (collection) => collection.itemsAddress === collectionAddress,
113
+ const orderbookKind = findMarketCollection(
114
+ marketplaceConfig?.market.collections ?? [],
115
+ collectionAddress,
116
+ chainId,
114
117
  )?.destinationMarketplace;
115
118
 
116
119
  const closeModal = () => createListingModalStore.send({ type: 'close' });
@@ -11,6 +11,7 @@ import {
11
11
  useCollectionMetadata,
12
12
  useConfig,
13
13
  useConnectorMetadata,
14
+ useCurrencyConvertToUSD,
14
15
  useCurrencyList,
15
16
  useTokenCurrencyBalance,
16
17
  } from '../../../../hooks';
@@ -106,6 +107,18 @@ export function useMakeOfferModalContext() {
106
107
  },
107
108
  });
108
109
 
110
+ const { data: usdConversion } = useCurrencyConvertToUSD({
111
+ chainId: state.chainId,
112
+ currencyAddress: selectedCurrency?.contractAddress ?? zeroAddress,
113
+ amountRaw: state.priceInput?.toString(),
114
+ query: {
115
+ enabled:
116
+ state.orderbookKind === OrderbookKind.opensea &&
117
+ !!selectedCurrency?.contractAddress &&
118
+ !!state.priceInput,
119
+ },
120
+ });
121
+
109
122
  const expiryDate = useMemo(
110
123
  () => new Date(Date.now() + state.expiryDays * 24 * 60 * 60 * 1000),
111
124
  [state.expiryDays],
@@ -139,6 +152,7 @@ export function useMakeOfferModalContext() {
139
152
  balance: balanceDnum,
140
153
  lowestListing: lowestListingDnum,
141
154
  orderbookKind: state.orderbookKind,
155
+ usdAmount: usdConversion?.usdAmount,
142
156
  });
143
157
 
144
158
  const formIsValid = isFormValid(validation);
@@ -359,6 +373,7 @@ export function useMakeOfferModalContext() {
359
373
  quantity: validation.quantity,
360
374
  balance: validation.balance,
361
375
  openseaCriteria: validation.openseaCriteria,
376
+ openseaMinPrice: validation.openseaMinPrice,
362
377
  },
363
378
  errors: {
364
379
  price: state.isPriceTouched ? validation.price.error : undefined,
@@ -369,6 +384,9 @@ export function useMakeOfferModalContext() {
369
384
  openseaCriteria: state.isPriceTouched
370
385
  ? validation.openseaCriteria?.error
371
386
  : undefined,
387
+ openseaMinPrice: state.isPriceTouched
388
+ ? validation.openseaMinPrice?.error
389
+ : undefined,
372
390
  },
373
391
  },
374
392
 
@@ -415,7 +433,8 @@ export function useMakeOfferModalContext() {
415
433
  this.form.errors.price ||
416
434
  this.form.errors.quantity ||
417
435
  this.form.errors.balance ||
418
- this.form.errors.openseaCriteria
436
+ this.form.errors.openseaCriteria ||
437
+ this.form.errors.openseaMinPrice
419
438
  );
420
439
  },
421
440
 
@@ -11,6 +11,7 @@ export type OfferValidation = {
11
11
  quantity: FieldValidation;
12
12
  balance: FieldValidation;
13
13
  openseaCriteria?: FieldValidation;
14
+ openseaMinPrice?: FieldValidation;
14
15
  };
15
16
 
16
17
  export function validateOfferForm({
@@ -19,12 +20,14 @@ export function validateOfferForm({
19
20
  balance,
20
21
  lowestListing,
21
22
  orderbookKind,
23
+ usdAmount,
22
24
  }: {
23
25
  price: Dnum;
24
26
  quantity: Dnum;
25
27
  balance?: Dnum;
26
28
  lowestListing?: Dnum;
27
29
  orderbookKind?: string;
30
+ usdAmount?: number;
28
31
  }): OfferValidation {
29
32
  const validation: OfferValidation = {
30
33
  price: { isValid: true, error: null },
@@ -67,6 +70,15 @@ export function validateOfferForm({
67
70
  };
68
71
  }
69
72
 
73
+ // OpenSea minimum price validation ($0.01 USD)
74
+ if (orderbookKind === 'opensea' && usdAmount !== undefined) {
75
+ const meetsMinPrice = usdAmount >= 0.01;
76
+ validation.openseaMinPrice = {
77
+ isValid: meetsMinPrice,
78
+ error: meetsMinPrice ? null : 'Lowest price must be at least $0.01',
79
+ };
80
+ }
81
+
70
82
  return validation;
71
83
  }
72
84
 
@@ -75,7 +87,8 @@ export function isFormValid(validation: OfferValidation): boolean {
75
87
  validation.price.isValid &&
76
88
  validation.quantity.isValid &&
77
89
  validation.balance.isValid &&
78
- (validation.openseaCriteria?.isValid ?? true)
90
+ (validation.openseaCriteria?.isValid ?? true) &&
91
+ (validation.openseaMinPrice?.isValid ?? true)
79
92
  );
80
93
  }
81
94
 
@@ -87,6 +100,8 @@ export function getValidationErrors(validation: OfferValidation): string[] {
87
100
  if (validation.balance.error) errors.push(validation.balance.error);
88
101
  if (validation.openseaCriteria?.error)
89
102
  errors.push(validation.openseaCriteria.error);
103
+ if (validation.openseaMinPrice?.error)
104
+ errors.push(validation.openseaMinPrice.error);
90
105
 
91
106
  return errors;
92
107
  }
@@ -1,6 +1,7 @@
1
1
  import type { Address } from '@0xsequence/api-client';
2
2
  import { createStore } from '@xstate/store';
3
3
  import { useSelector } from '@xstate/store/react';
4
+ import { findMarketCollection } from '../../../../../utils';
4
5
  import { useMarketplaceConfig } from '../../../hooks';
5
6
 
6
7
  export type OpenMakeOfferModalArgs = {
@@ -109,8 +110,10 @@ export const useMakeOfferModalState = () => {
109
110
  } = useSelector(makeOfferModalStore, (state) => state.context);
110
111
 
111
112
  const { data: marketplaceConfig } = useMarketplaceConfig();
112
- const orderbookKind = marketplaceConfig?.market.collections.find(
113
- (collection) => collection.itemsAddress === collectionAddress,
113
+ const orderbookKind = findMarketCollection(
114
+ marketplaceConfig?.market.collections ?? [],
115
+ collectionAddress,
116
+ chainId,
114
117
  )?.destinationMarketplace;
115
118
 
116
119
  const closeModal = () => makeOfferModalStore.send({ type: 'close' });
@@ -2,6 +2,7 @@ import type { Address } from '@0xsequence/api-client';
2
2
  import { createStore } from '@xstate/store';
3
3
  import { useSelector } from '@xstate/store/react';
4
4
  import { zeroAddress } from 'viem';
5
+ import { findMarketCollection } from '../../../../../utils';
5
6
  import type { Order } from '../../../../_internal';
6
7
  import { useMarketplaceConfig } from '../../../hooks';
7
8
 
@@ -56,8 +57,10 @@ export const useSellModalState = () => {
56
57
  );
57
58
 
58
59
  const { data: marketplaceConfig } = useMarketplaceConfig();
59
- const orderbookKind = marketplaceConfig?.market.collections.find(
60
- (collection) => collection.itemsAddress === collectionAddress,
60
+ const orderbookKind = findMarketCollection(
61
+ marketplaceConfig?.market.collections ?? [],
62
+ collectionAddress,
63
+ chainId,
61
64
  )?.destinationMarketplace;
62
65
 
63
66
  const closeModal = () => sellModalStore.send({ type: 'close' });
@@ -17,7 +17,7 @@ export const ModalInitializationError = ({
17
17
  const [showTechnicalDetails, setShowTechnicalDetails] = useState(false);
18
18
 
19
19
  return (
20
- <div className="flex min-h-[400px] flex-col items-center justify-center p-4 text-white">
20
+ <div className="flex min-h-[400px] flex-col items-center justify-center p-6 text-white">
21
21
  <div className="mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-red-500">
22
22
  <WarningIcon className="h-8 w-8 text-white" />
23
23
  </div>
@@ -29,7 +29,7 @@ export const ModalInitializationError = ({
29
29
  refresh the page.
30
30
  </p>
31
31
 
32
- {error.stack && (
32
+ {(error.cause as string) && (
33
33
  <button
34
34
  type="button"
35
35
  onClick={() => setShowTechnicalDetails(!showTechnicalDetails)}
@@ -50,19 +50,21 @@ export const ModalInitializationError = ({
50
50
  d="M19 9l-7 7-7-7"
51
51
  />
52
52
  </svg>
53
- Show technical details
53
+ {showTechnicalDetails
54
+ ? 'Hide technical details'
55
+ : 'Show technical details'}
54
56
  </button>
55
57
  )}
56
58
 
57
- {showTechnicalDetails && error.stack && (
59
+ {showTechnicalDetails && (error.cause as string) && (
58
60
  <div className="mb-8 max-h-64 w-full max-w-md overflow-y-auto rounded-lg border border-red-900 bg-[#2b0000] p-4">
59
61
  <pre className="whitespace-pre-wrap break-words text-red-100 text-xs">
60
- {error.stack}
62
+ {error.cause as string}
61
63
  </pre>
62
64
  </div>
63
65
  )}
64
66
 
65
- <div className="flex w-full max-w-xs flex-col space-y-3">
67
+ <div className="flex w-full flex-col space-y-3">
66
68
  {onTryAgain && (
67
69
  <Button
68
70
  onClick={onTryAgain}
@@ -36,7 +36,6 @@ type PriceInputProps = {
36
36
  };
37
37
  disabled?: boolean;
38
38
  orderbookKind?: OrderbookKind;
39
- setOpenseaLowestPriceCriteriaMet?: (state: boolean) => void;
40
39
  modalType?: 'listing' | 'offer';
41
40
  // Fee data for enhanced balance checking in offers
42
41
  feeData?: {
@@ -55,7 +54,6 @@ export default function PriceInput({
55
54
  includeNativeCurrency,
56
55
  disabled,
57
56
  orderbookKind,
58
- setOpenseaLowestPriceCriteriaMet,
59
57
  modalType,
60
58
  feeData,
61
59
  }: PriceInputProps) {
@@ -81,8 +79,7 @@ export default function PriceInput({
81
79
  enabled:
82
80
  orderbookKind === OrderbookKind.opensea &&
83
81
  !!currencyAddress &&
84
- !!priceAmountRaw &&
85
- !!setOpenseaLowestPriceCriteriaMet,
82
+ !!priceAmountRaw,
86
83
  },
87
84
  });
88
85
 
@@ -164,17 +161,13 @@ export default function PriceInput({
164
161
 
165
162
  const openseaLowestPriceCriteriaMet =
166
163
  orderbookKind === OrderbookKind.opensea &&
167
- !!conversion?.usdAmount &&
164
+ conversion?.usdAmount !== undefined &&
168
165
  conversion.usdAmount >= 0.01;
169
166
 
170
167
  if (checkBalance?.enabled) {
171
168
  checkBalance.callback(balanceError);
172
169
  }
173
170
 
174
- if (setOpenseaLowestPriceCriteriaMet) {
175
- setOpenseaLowestPriceCriteriaMet(openseaLowestPriceCriteriaMet);
176
- }
177
-
178
171
  const [value, setValue] = useState('0');
179
172
  const prevCurrencyDecimals = useRef(currencyDecimals);
180
173
  const [openseaDecimalError, setOpenseaDecimalError] = useState<string | null>(
@@ -11,6 +11,7 @@ import {
11
11
  import { useEffect, useState } from 'react';
12
12
  import { DEFAULT_MARKETPLACE_FEE_PERCENTAGE } from '../../../../../../consts';
13
13
  import type { Price } from '../../../../../../types';
14
+ import { findMarketCollection } from '../../../../../../utils';
14
15
  import { calculateEarningsAfterFees } from '../../../../../../utils/price';
15
16
  import { useMarketplaceConfig, useRoyalty } from '../../../../../hooks';
16
17
 
@@ -37,8 +38,10 @@ export default function TransactionDetails({
37
38
  const { data, isLoading: marketplaceConfigLoading } = useMarketplaceConfig();
38
39
 
39
40
  const marketplaceFeePercentage = includeMarketplaceFee
40
- ? data?.market.collections.find(
41
- (collection) => collection.itemsAddress === collectionAddress,
41
+ ? findMarketCollection(
42
+ data?.market.collections ?? [],
43
+ collectionAddress,
44
+ chainId,
42
45
  )?.feePercentage || DEFAULT_MARKETPLACE_FEE_PERCENTAGE
43
46
  : 0;
44
47
  // royaltyPercentage is an array of [recipient, percentage]
@@ -1855,6 +1855,17 @@ export const styles = String.raw`/* Modified Tailwind CSS, to avoid issues with
1855
1855
  --tw-tracking: var(--tracking-normal);
1856
1856
  letter-spacing: var(--tracking-normal);
1857
1857
  }
1858
+ .text-small {
1859
+ font-family: "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1860
+ font-size: var(--text-xs);
1861
+ line-height: var(--tw-leading, var(--text-xs--line-height));
1862
+ --tw-leading: calc(var(--spacing) * 4);
1863
+ line-height: calc(var(--spacing) * 4);
1864
+ --tw-font-weight: var(--font-weight-medium);
1865
+ font-weight: var(--font-weight-medium);
1866
+ --tw-tracking: var(--tracking-wide);
1867
+ letter-spacing: var(--tracking-wide);
1868
+ }
1858
1869
  .font-body {
1859
1870
  font-family: "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1860
1871
  }
@@ -1985,6 +1996,9 @@ export const styles = String.raw`/* Modified Tailwind CSS, to avoid issues with
1985
1996
  .break-words {
1986
1997
  overflow-wrap: break-word;
1987
1998
  }
1999
+ .wrap-anywhere {
2000
+ overflow-wrap: anywhere;
2001
+ }
1988
2002
  .text-ellipsis {
1989
2003
  text-overflow: ellipsis;
1990
2004
  }
@@ -2313,6 +2327,10 @@ export const styles = String.raw`/* Modified Tailwind CSS, to avoid issues with
2313
2327
  --tw-duration: 100ms;
2314
2328
  transition-duration: 100ms;
2315
2329
  }
2330
+ .duration-150 {
2331
+ --tw-duration: 150ms;
2332
+ transition-duration: 150ms;
2333
+ }
2316
2334
  .duration-200 {
2317
2335
  --tw-duration: 200ms;
2318
2336
  transition-duration: 200ms;
@@ -0,0 +1,19 @@
1
+ import type { MarketCollection } from '@0xsequence/api-client';
2
+ import { compareAddress } from './address';
3
+
4
+ /**
5
+ * Finds a market collection matching both the collection address and chain ID.
6
+ * Always use this instead of manually searching market.collections to avoid
7
+ * multi-chain bugs where the same collection address exists on different chains.
8
+ */
9
+ export const findMarketCollection = (
10
+ collections: MarketCollection[],
11
+ collectionAddress: string,
12
+ chainId: string | number,
13
+ ): MarketCollection | undefined => {
14
+ return collections.find(
15
+ (collection) =>
16
+ compareAddress(collection.itemsAddress, collectionAddress) &&
17
+ Number(collection.chainId) === Number(chainId),
18
+ );
19
+ };
@@ -1,6 +1,7 @@
1
1
  export * from './abi';
2
2
  export * from './address';
3
3
  export * from './cn';
4
+ export * from './collection';
4
5
  export * from './getMarketplaceDetails';
5
6
  export * from './network';
6
7
  export * from './networkconfigToWagmiChain';