@0xsequence/marketplace-sdk 0.4.8 → 0.5.0

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 (116) hide show
  1. package/dist/{chunk-2OJB35FS.js → chunk-5NORRVPM.js} +5 -5
  2. package/dist/{chunk-2OJB35FS.js.map → chunk-5NORRVPM.js.map} +1 -1
  3. package/dist/{chunk-ATDCYXXV.js → chunk-6YHHCGGY.js} +2 -2
  4. package/dist/{chunk-WRMJ5FZM.js → chunk-HV2X2VZN.js} +874 -194
  5. package/dist/chunk-HV2X2VZN.js.map +1 -0
  6. package/dist/{chunk-AQT3BQ67.js → chunk-J2XJZ6SJ.js} +12 -5
  7. package/dist/chunk-J2XJZ6SJ.js.map +1 -0
  8. package/dist/{chunk-JEOUQFT3.js → chunk-LJAB3S6U.js} +4 -3
  9. package/dist/chunk-LJAB3S6U.js.map +1 -0
  10. package/dist/{chunk-7WCZP6FN.js → chunk-OUVFTA63.js} +649 -386
  11. package/dist/chunk-OUVFTA63.js.map +1 -0
  12. package/dist/{chunk-XXML5K3X.js → chunk-QTJF5GDQ.js} +2 -2
  13. package/dist/{chunk-LF44FCG5.js → chunk-TQWM4ER6.js} +2 -2
  14. package/dist/{chunk-LF44FCG5.js.map → chunk-TQWM4ER6.js.map} +1 -1
  15. package/dist/{chunk-6R4G7J6Q.js → chunk-WSCUPAGR.js} +33 -5
  16. package/dist/chunk-WSCUPAGR.js.map +1 -0
  17. package/dist/{create-config-D5WqfUft.d.ts → create-config-BXvwUh55.d.ts} +2 -2
  18. package/dist/index.css +31 -17
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +1 -1
  21. package/dist/{marketplace-config-C_fDWzz0.d.ts → marketplace-config-znEu4L0K.d.ts} +1 -1
  22. package/dist/{marketplace.gen-B8S8fflj.d.ts → marketplace.gen-CCJ-URn2.d.ts} +16 -4
  23. package/dist/react/_internal/api/index.d.ts +3 -2
  24. package/dist/react/_internal/api/index.js +3 -1
  25. package/dist/react/_internal/index.d.ts +5 -5
  26. package/dist/react/_internal/index.js +3 -1
  27. package/dist/react/_internal/wagmi/index.d.ts +3 -3
  28. package/dist/react/hooks/index.d.ts +249 -74
  29. package/dist/react/hooks/index.js +18 -5
  30. package/dist/react/index.css +37 -17
  31. package/dist/react/index.css.map +1 -1
  32. package/dist/react/index.d.ts +6 -6
  33. package/dist/react/index.js +22 -9
  34. package/dist/react/ssr/index.js +4 -0
  35. package/dist/react/ssr/index.js.map +1 -1
  36. package/dist/react/ui/components/collectible-card/index.css +37 -17
  37. package/dist/react/ui/components/collectible-card/index.css.map +1 -1
  38. package/dist/react/ui/components/collectible-card/index.d.ts +27 -4
  39. package/dist/react/ui/components/collectible-card/index.js +8 -9
  40. package/dist/react/ui/icons/index.js +3 -3
  41. package/dist/react/ui/index.css +37 -17
  42. package/dist/react/ui/index.css.map +1 -1
  43. package/dist/react/ui/index.d.ts +3 -3
  44. package/dist/react/ui/index.js +8 -9
  45. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +3 -3
  46. package/dist/react/ui/modals/_internal/components/actionModal/index.js +5 -5
  47. package/dist/{sdk-config-BXVH8PS2.d.ts → sdk-config-B32_2bG3.d.ts} +29 -7
  48. package/dist/{services-CdXAIjt1.d.ts → services-BRBVE0mm.d.ts} +1 -1
  49. package/dist/styles/index.css +31 -17
  50. package/dist/styles/index.css.map +1 -1
  51. package/dist/styles/index.d.ts +2 -2
  52. package/dist/styles/index.js +2 -2
  53. package/dist/types/index.d.ts +3 -3
  54. package/dist/types/index.js +1 -1
  55. package/dist/{types-eX4P9xju.d.ts → types-Yto6KrTN.d.ts} +2 -2
  56. package/dist/utils/index.d.ts +3 -3
  57. package/dist/utils/index.js +1 -1
  58. package/package.json +16 -16
  59. package/src/react/_internal/api/__mocks__/marketplace.msw.ts +4 -0
  60. package/src/react/_internal/api/marketplace.gen.ts +45 -7
  61. package/src/react/_internal/api/query-keys.ts +4 -0
  62. package/src/react/_internal/wallet/useWallet.ts +30 -46
  63. package/src/react/_internal/wallet/wallet.ts +52 -6
  64. package/src/react/hooks/index.ts +4 -0
  65. package/src/react/hooks/options/__mocks__/marketplaceConfig.msw.ts +10 -1
  66. package/src/react/hooks/useAutoSelectFeeOption.tsx +104 -0
  67. package/src/react/hooks/useCancelOrder.tsx +57 -1
  68. package/src/react/hooks/useCollectionBalanceDetails.tsx +87 -0
  69. package/src/react/hooks/useCollectionDetails.tsx +35 -0
  70. package/src/react/hooks/useCollectionDetailsPolling.tsx +60 -0
  71. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +36 -118
  72. package/src/react/ui/components/_internals/action-button/components/ActionButtonBody.tsx +52 -0
  73. package/src/react/ui/components/_internals/action-button/components/NonOwnerActions.tsx +72 -0
  74. package/src/react/ui/components/_internals/action-button/components/OwnerActions.tsx +81 -0
  75. package/src/react/ui/components/_internals/action-button/hooks/useActionButtonLogic.ts +93 -0
  76. package/src/react/ui/components/_internals/action-button/store.ts +47 -0
  77. package/src/react/ui/components/_internals/action-button/styles.css.ts +8 -0
  78. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +35 -18
  79. package/src/react/ui/components/collectible-card/Footer.tsx +5 -8
  80. package/src/react/ui/components/collectible-card/styles.css.ts +44 -31
  81. package/src/react/ui/icons/CartIcon.tsx +46 -0
  82. package/src/react/ui/modals/BuyModal/Modal.tsx +0 -2
  83. package/src/react/ui/modals/BuyModal/__tests__/Modal.test.tsx +253 -0
  84. package/src/react/ui/modals/BuyModal/__tests__/store.test.ts +100 -0
  85. package/src/react/ui/modals/BuyModal/hooks/__tests__/useBuyCollectable.test.tsx +402 -0
  86. package/src/react/ui/modals/BuyModal/hooks/__tests__/useCheckoutOptions.test.tsx +267 -0
  87. package/src/react/ui/modals/BuyModal/hooks/__tests__/useFees.test.tsx +166 -0
  88. package/src/react/ui/modals/BuyModal/hooks/__tests__/useLoadData.test.tsx +209 -0
  89. package/src/react/ui/modals/BuyModal/hooks/useBuyCollectable.ts +7 -4
  90. package/src/react/ui/modals/BuyModal/hooks/useCheckoutOptions.ts +19 -17
  91. package/src/react/ui/modals/BuyModal/hooks/useLoadData.ts +9 -7
  92. package/src/react/ui/modals/BuyModal/modals/Modal1155.tsx +36 -18
  93. package/src/react/ui/modals/BuyModal/modals/__tests__/CheckoutModal.test.tsx +162 -0
  94. package/src/react/ui/modals/BuyModal/modals/__tests__/Modal1155.test.tsx +243 -0
  95. package/src/react/ui/modals/BuyModal/store.ts +11 -10
  96. package/src/react/ui/modals/CreateListingModal/Modal.tsx +26 -3
  97. package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +141 -29
  98. package/src/react/ui/modals/TransferModal/_views/enterWalletAddress/useHandleTransfer.tsx +5 -1
  99. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +20 -11
  100. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx +13 -58
  101. package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +2 -0
  102. package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/TransactionStatusModal.test.tsx +18 -19
  103. package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/utils.test.ts +2 -0
  104. package/src/react/ui/modals/_internal/components/transactionStatusModal/hooks/useTransactionStatus.ts +62 -0
  105. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +53 -100
  106. package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +2 -10
  107. package/tsconfig.tsbuildinfo +1 -1
  108. package/dist/chunk-6R4G7J6Q.js.map +0 -1
  109. package/dist/chunk-7WCZP6FN.js.map +0 -1
  110. package/dist/chunk-AQT3BQ67.js.map +0 -1
  111. package/dist/chunk-FWN2MCLI.js +0 -425
  112. package/dist/chunk-FWN2MCLI.js.map +0 -1
  113. package/dist/chunk-JEOUQFT3.js.map +0 -1
  114. package/dist/chunk-WRMJ5FZM.js.map +0 -1
  115. /package/dist/{chunk-ATDCYXXV.js.map → chunk-6YHHCGGY.js.map} +0 -0
  116. /package/dist/{chunk-XXML5K3X.js.map → chunk-QTJF5GDQ.js.map} +0 -0
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable */
2
- // marketplace-api fde5cccee73a952c6b2c70e0fb424d7d3eb23f1c
2
+ // marketplace-api 8d0cafb3a02c5db5bdd5d6b139a752349e301f0c
3
3
  // --
4
4
  // Code generated by webrpc-gen@v0.22.1 with typescript generator. DO NOT EDIT.
5
5
  //
@@ -7,7 +7,7 @@
7
7
 
8
8
  export const WebrpcHeader = "Webrpc"
9
9
 
10
- export const WebrpcHeaderValue = "webrpc@v0.22.1;gen-typescript@v0.16.2;marketplace-api@v0.0.0-fde5cccee73a952c6b2c70e0fb424d7d3eb23f1c"
10
+ export const WebrpcHeaderValue = "webrpc@v0.22.1;gen-typescript@v0.16.2;marketplace-api@v0.0.0-8d0cafb3a02c5db5bdd5d6b139a752349e301f0c"
11
11
 
12
12
  // WebRPC description and code-gen version
13
13
  export const WebRPCVersion = "v1"
@@ -16,7 +16,7 @@ export const WebRPCVersion = "v1"
16
16
  export const WebRPCSchemaVersion = ""
17
17
 
18
18
  // Schema hash generated from your RIDL schema
19
- export const WebRPCSchemaHash = "fde5cccee73a952c6b2c70e0fb424d7d3eb23f1c"
19
+ export const WebRPCSchemaHash = "8d0cafb3a02c5db5bdd5d6b139a752349e301f0c"
20
20
 
21
21
  type WebrpcGenVersions = {
22
22
  webrpcGenVersion: string;
@@ -126,9 +126,7 @@ export enum MarketplaceKind {
126
126
  x2y2 = 'x2y2',
127
127
  alienswap = 'alienswap',
128
128
  payment_processor = 'payment_processor',
129
- mintify = 'mintify',
130
- element_erc721 = 'element_erc721',
131
- element_erc1155 = 'element_erc1155'
129
+ mintify = 'mintify'
132
130
  }
133
131
 
134
132
  export enum OrderbookKind {
@@ -181,6 +179,8 @@ export enum CollectionPriority {
181
179
  export enum CollectionStatus {
182
180
  unknown = 'unknown',
183
181
  created = 'created',
182
+ syncing_contract_metadata = 'syncing_contract_metadata',
183
+ synced_contract_metadata = 'synced_contract_metadata',
184
184
  syncing_metadata = 'syncing_metadata',
185
185
  synced_metadata = 'synced_metadata',
186
186
  syncing_tokens = 'syncing_tokens',
@@ -188,7 +188,8 @@ export enum CollectionStatus {
188
188
  syncing_orders = 'syncing_orders',
189
189
  active = 'active',
190
190
  failed = 'failed',
191
- inactive = 'inactive'
191
+ inactive = 'inactive',
192
+ incompatible_type = 'incompatible_type'
192
193
  }
193
194
 
194
195
  export enum ProjectStatus {
@@ -203,6 +204,14 @@ export enum CollectibleStatus {
203
204
  inactive = 'inactive'
204
205
  }
205
206
 
207
+ export enum CurrencyStatus {
208
+ unknown = 'unknown',
209
+ created = 'created',
210
+ syncing_metadata = 'syncing_metadata',
211
+ active = 'active',
212
+ failed = 'failed'
213
+ }
214
+
206
215
  export enum WalletKind {
207
216
  unknown = 'unknown',
208
217
  sequence = 'sequence'
@@ -312,6 +321,7 @@ export interface Order {
312
321
  priceCurrencyAddress: string
313
322
  priceDecimals: number
314
323
  priceUSD: number
324
+ priceUSDFormatted: string
315
325
  quantityInitial: string
316
326
  quantityInitialFormatted: string
317
327
  quantityRemaining: string
@@ -340,6 +350,8 @@ export interface FeeBreakdown {
340
350
  export interface CollectibleOrder {
341
351
  metadata: TokenMetadata
342
352
  order?: Order
353
+ listing?: Order
354
+ offer?: Order
343
355
  }
344
356
 
345
357
  export interface OrderFilter {
@@ -397,6 +409,7 @@ export interface Collectible {
397
409
  export interface Currency {
398
410
  chainId: number
399
411
  contractAddress: string
412
+ status: CurrencyStatus
400
413
  name: string
401
414
  symbol: string
402
415
  decimals: number
@@ -594,6 +607,7 @@ export interface DeleteCurrencyReturn {
594
607
 
595
608
  export interface Marketplace {
596
609
  listCurrencies(headers?: object, signal?: AbortSignal): Promise<ListCurrenciesReturn>
610
+ getCollectionDetail(args: GetCollectionDetailArgs, headers?: object, signal?: AbortSignal): Promise<GetCollectionDetailReturn>
597
611
  getCollectible(args: GetCollectibleArgs, headers?: object, signal?: AbortSignal): Promise<GetCollectibleReturn>
598
612
  getLowestPriceOfferForCollectible(args: GetLowestPriceOfferForCollectibleArgs, headers?: object, signal?: AbortSignal): Promise<GetLowestPriceOfferForCollectibleReturn>
599
613
  getHighestPriceOfferForCollectible(args: GetHighestPriceOfferForCollectibleArgs, headers?: object, signal?: AbortSignal): Promise<GetHighestPriceOfferForCollectibleReturn>
@@ -636,6 +650,13 @@ export interface ListCurrenciesArgs {
636
650
  export interface ListCurrenciesReturn {
637
651
  currencies: Array<Currency>
638
652
  }
653
+ export interface GetCollectionDetailArgs {
654
+ contractAddress: string
655
+ }
656
+
657
+ export interface GetCollectionDetailReturn {
658
+ collection: Collection
659
+ }
639
660
  export interface GetCollectibleArgs {
640
661
  contractAddress: string
641
662
  tokenId: string
@@ -684,6 +705,7 @@ export interface ListListingsForCollectibleArgs {
684
705
  contractAddress: string
685
706
  tokenId: string
686
707
  filter?: OrderFilter
708
+ skipRealtimeSync?: boolean
687
709
  page?: Page
688
710
  }
689
711
 
@@ -695,6 +717,7 @@ export interface ListOffersForCollectibleArgs {
695
717
  contractAddress: string
696
718
  tokenId: string
697
719
  filter?: OrderFilter
720
+ skipRealtimeSync?: boolean
698
721
  page?: Page
699
722
  }
700
723
 
@@ -1151,6 +1174,20 @@ export class Marketplace implements Marketplace {
1151
1174
  })
1152
1175
  }
1153
1176
 
1177
+ getCollectionDetail = (args: GetCollectionDetailArgs, headers?: object, signal?: AbortSignal): Promise<GetCollectionDetailReturn> => {
1178
+ return this.fetch(
1179
+ this.url('GetCollectionDetail'),
1180
+ createHTTPRequest(args, headers, signal)).then((res) => {
1181
+ return buildResponse(res).then(_data => {
1182
+ return {
1183
+ collection: <Collection>(_data.collection),
1184
+ }
1185
+ })
1186
+ }, (error) => {
1187
+ throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` })
1188
+ })
1189
+ }
1190
+
1154
1191
  getCollectible = (args: GetCollectibleArgs, headers?: object, signal?: AbortSignal): Promise<GetCollectibleReturn> => {
1155
1192
  return this.fetch(
1156
1193
  this.url('GetCollectible'),
@@ -2084,3 +2121,4 @@ export const webrpcErrorByCode: { [code: number]: any } = {
2084
2121
  }
2085
2122
 
2086
2123
  export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise<Response>
2124
+
@@ -50,6 +50,10 @@ class CollectionKeys {
50
50
  class BalanceQueries {
51
51
  static all = ['balances'] as const;
52
52
  static lists = [...BalanceQueries.all, 'tokenBalances'] as const;
53
+ static collectionBalanceDetails = [
54
+ ...BalanceQueries.all,
55
+ 'collectionBalanceDetails',
56
+ ] as const;
53
57
  }
54
58
 
55
59
  // biome-ignore lint/complexity/noStaticOnlyClass:
@@ -1,58 +1,42 @@
1
- import { useAccount, useSwitchChain, useWalletClient } from 'wagmi';
1
+ import { useAccount, useChainId, useSwitchChain, useWalletClient } from 'wagmi';
2
+ import { useQuery } from '@tanstack/react-query';
2
3
  import { wallet, type WalletInstance } from './wallet';
4
+ import { useConfig } from '../../hooks';
3
5
 
4
- type UseWalletReturn =
5
- | {
6
- status: 'loading';
7
- wallet: null;
8
- isLoading: true;
9
- isError: false;
10
- }
11
- | {
12
- status: 'error';
13
- wallet: null;
14
- isLoading: false;
15
- isError: true;
16
- }
17
- | {
18
- status: 'success';
19
- wallet: WalletInstance;
20
- isLoading: false;
21
- isError: false;
22
- };
6
+ type UseWalletReturn = {
7
+ wallet: WalletInstance | null | undefined;
8
+ isLoading: boolean;
9
+ isError: boolean;
10
+ };
23
11
 
24
12
  export const useWallet = (): UseWalletReturn => {
25
13
  const { chains } = useSwitchChain();
26
- const { data: walletClient, isLoading: walletClientIsLoading } =
14
+ const { data: walletClient, isLoading: wagmiWalletIsLoading } =
27
15
  useWalletClient();
28
16
  const { connector, isConnected, isConnecting } = useAccount();
17
+ const sdkConfig = useConfig();
18
+ const chainId = useChainId();
29
19
 
30
- if (walletClientIsLoading || isConnecting) {
31
- return {
32
- status: 'loading',
33
- wallet: null,
34
- isLoading: true,
35
- isError: false,
36
- };
37
- }
38
-
39
- if (!walletClient || !connector || !isConnected) {
40
- return {
41
- status: 'error',
42
- wallet: null,
43
- isLoading: false,
44
- isError: true,
45
- };
46
- }
20
+ const { data, isLoading, isError } = useQuery({
21
+ queryKey: ['wallet', chainId, connector?.uid],
22
+ queryFn: () => {
23
+ if (!walletClient || !connector || !isConnected) {
24
+ return null;
25
+ }
26
+ return wallet({
27
+ wallet: walletClient,
28
+ chains,
29
+ connector,
30
+ sdkConfig,
31
+ });
32
+ },
33
+ staleTime: Number.POSITIVE_INFINITY,
34
+ enabled: Boolean(walletClient && connector && isConnected),
35
+ });
47
36
 
48
37
  return {
49
- status: 'success',
50
- wallet: wallet({
51
- wallet: walletClient,
52
- chains,
53
- connector,
54
- }),
55
- isLoading: false,
56
- isError: false,
38
+ wallet: data,
39
+ isLoading: isLoading || isConnecting || wagmiWalletIsLoading,
40
+ isError,
57
41
  };
58
42
  };
@@ -3,9 +3,10 @@ import {
3
3
  type Address,
4
4
  type Chain,
5
5
  type Hex,
6
- type TransactionReceipt,
6
+ TransactionReceiptNotFoundError,
7
7
  type TypedDataDomain,
8
8
  type WalletClient as ViemWalletClient,
9
+ WaitForTransactionReceiptTimeoutError,
9
10
  custom,
10
11
  erc20Abi,
11
12
  erc721Abi,
@@ -22,13 +23,15 @@ import {
22
23
  TransactionSignatureError,
23
24
  UserRejectedRequestError,
24
25
  } from '../../../utils/_internal/error/transaction';
25
- import { StepType, WalletKind } from '../api';
26
+ import { getIndexerClient, StepType, WalletKind } from '../api';
26
27
  import { createLogger } from '../logger';
27
28
  import type { SignatureStep, TransactionStep } from '../utils';
28
29
  import {
29
30
  SEQUENCE_MARKET_V1_ADDRESS,
30
31
  SEQUENCE_MARKET_V2_ADDRESS,
31
32
  } from '../../../consts';
33
+ import type { SdkConfig } from '../../../types';
34
+ import type { TransactionReceipt } from '@0xsequence/indexer';
32
35
 
33
36
  interface WalletClient extends Omit<ViemWalletClient, 'account'> {
34
37
  account: Account;
@@ -61,10 +64,12 @@ export const wallet = ({
61
64
  wallet,
62
65
  chains,
63
66
  connector,
67
+ sdkConfig,
64
68
  }: {
65
69
  wallet: WalletClient;
66
70
  chains: readonly [Chain, ...Chain[]];
67
71
  connector: Connector;
72
+ sdkConfig: SdkConfig;
68
73
  }): WalletInstance => {
69
74
  const logger = createLogger('Wallet');
70
75
 
@@ -174,11 +179,11 @@ export const wallet = ({
174
179
  },
175
180
  handleConfirmTransactionStep: async (txHash: Hex, chainId: number) => {
176
181
  logger.debug('Confirming transaction', { txHash, chainId });
177
-
178
182
  try {
179
- const publicClient = getPublicRpcClient(chainId);
180
- const receipt = await publicClient.waitForTransactionReceipt({
181
- hash: txHash as Address,
183
+ const receipt = await awaitTransactionReceipt({
184
+ txHash,
185
+ chainId,
186
+ sdkConfig,
182
187
  });
183
188
  logger.info('Transaction confirmed', { txHash, receipt });
184
189
  return receipt;
@@ -235,3 +240,44 @@ export const wallet = ({
235
240
 
236
241
  return walletInstance;
237
242
  };
243
+
244
+ const ONE_MIN = 60 * 1000;
245
+ const THREE_MIN = 3 * ONE_MIN;
246
+
247
+ const awaitTransactionReceipt = async ({
248
+ txHash,
249
+ chainId,
250
+ sdkConfig,
251
+ timeout = THREE_MIN,
252
+ }: {
253
+ txHash: Hex;
254
+ chainId: number;
255
+ sdkConfig: SdkConfig;
256
+ timeout?: number;
257
+ }) => {
258
+ const indexer = getIndexerClient(chainId, sdkConfig);
259
+ return Promise.race([
260
+ new Promise<TransactionReceipt>((resolve, reject) => {
261
+ indexer.subscribeReceipts(
262
+ {
263
+ filter: {
264
+ txnHash: txHash,
265
+ },
266
+ },
267
+ {
268
+ onMessage: ({ receipt }) => {
269
+ resolve(receipt);
270
+ },
271
+ onError: () => {
272
+ reject(TransactionReceiptNotFoundError);
273
+ },
274
+ },
275
+ );
276
+ }),
277
+ new Promise<TransactionReceipt>((_, reject) => {
278
+ setTimeout(() => {
279
+ reject(WaitForTransactionReceiptTimeoutError);
280
+ }, timeout);
281
+ }),
282
+ ]);
283
+ };
@@ -1,7 +1,9 @@
1
+ export * from './useAutoSelectFeeOption';
1
2
  export * from './useBalanceOfCollectible';
2
3
  export * from './useCountOfCollectables';
3
4
  export * from './useCollectible';
4
5
  export * from './useCollection';
6
+ export * from './useCollectionBalanceDetails';
5
7
  export * from './useConfig';
6
8
  export * from './useCurrencies';
7
9
  export * from './useCurrencyOptions';
@@ -29,3 +31,5 @@ export * from './useCheckoutOptions';
29
31
  export * from './useListCollections';
30
32
  export * from './useGenerateBuyTransaction';
31
33
  export * from './useCancelOrder';
34
+ export * from './useCollectionDetails';
35
+ export * from './useCollectionDetailsPolling';
@@ -1,5 +1,6 @@
1
1
  import { http, HttpResponse } from 'msw';
2
2
  import { WalletOptions, type MarketplaceConfig } from '../../../../types';
3
+ import { mockCurrencies } from '../../../_internal/api/__mocks__/marketplace.msw';
3
4
 
4
5
  // Mock data
5
6
  export const mockConfig: MarketplaceConfig = {
@@ -12,7 +13,15 @@ export const mockConfig: MarketplaceConfig = {
12
13
  logoUrl: 'https://example.com/logo.png',
13
14
  titleTemplate: '%s | Test Marketplace',
14
15
  walletOptions: [WalletOptions.Sequence],
15
- collections: [],
16
+ collections: [
17
+ {
18
+ collectionAddress: '0x1234567890123456789012345678901234567890',
19
+ chainId: 1,
20
+ marketplaceFeePercentage: 2.5,
21
+ marketplaceType: 'orderbook',
22
+ currencyOptions: mockCurrencies.map((c) => c.contractAddress),
23
+ },
24
+ ],
16
25
  landingPageLayout: 'default',
17
26
  cssString: '',
18
27
  manifestUrl: '',
@@ -0,0 +1,104 @@
1
+ import type { Address } from 'viem';
2
+ import { useAccount, useConfig } from 'wagmi';
3
+ import type { FeeOption } from '../ui/modals/_internal/components/waasFeeOptionsSelect/WaasFeeOptionsSelect';
4
+ import { getBalance } from 'wagmi/actions';
5
+ import { useCallback } from 'react';
6
+
7
+ type Error =
8
+ | 'User not connected'
9
+ | 'No options provided'
10
+ | 'Insufficient balance for any fee option'
11
+ | 'Failed to check balances';
12
+
13
+ type UseAutoSelectFeeOptionArgs = {
14
+ pendingFeeOptionConfirmation: {
15
+ id: string;
16
+ options: FeeOption[] | undefined;
17
+ chainId: number;
18
+ };
19
+ };
20
+
21
+ type AutoSelectFeeOptionResult = Promise<{
22
+ selectedOption: FeeOption | null;
23
+ error: Error | null;
24
+ isLoading: boolean;
25
+ }>;
26
+
27
+ /**
28
+ * Automatically selects the first fee option that user has enough balance for
29
+ * @param pendingFeeOptionConfirmation
30
+ * @returns The selected fee option, an error message if any, and a loading flag
31
+ */
32
+ export function useAutoSelectFeeOption({
33
+ pendingFeeOptionConfirmation,
34
+ }: UseAutoSelectFeeOptionArgs): AutoSelectFeeOptionResult {
35
+ const { address: userAddress } = useAccount();
36
+ const config = useConfig();
37
+
38
+ const findAffordableOption = useCallback(async () => {
39
+ if (!userAddress) {
40
+ return {
41
+ selectedOption: null,
42
+ error: 'User not connected' as Error,
43
+ isLoading: false,
44
+ };
45
+ }
46
+
47
+ if (!pendingFeeOptionConfirmation.options) {
48
+ return {
49
+ selectedOption: null,
50
+ error: 'No options provided' as Error,
51
+ isLoading: false,
52
+ };
53
+ }
54
+
55
+ try {
56
+ // Try each option until we find an affordable one
57
+ for (const option of pendingFeeOptionConfirmation.options) {
58
+ if (!option.token.contractAddress) continue;
59
+
60
+ try {
61
+ const balance = await getBalance(config, {
62
+ address: userAddress as Address,
63
+ token: option.token.contractAddress as Address,
64
+ chainId: pendingFeeOptionConfirmation.chainId,
65
+ });
66
+
67
+ const optionValue = BigInt(option.value);
68
+ if (balance.value >= optionValue) {
69
+ return {
70
+ selectedOption: option,
71
+ error: null,
72
+ isLoading: false,
73
+ };
74
+ }
75
+ } catch (error) {
76
+ console.error(
77
+ 'Error fetching balance for currency:',
78
+ option.token.contractAddress,
79
+ error,
80
+ );
81
+ }
82
+ }
83
+
84
+ return {
85
+ selectedOption: null,
86
+ error: 'Insufficient balance for any fee option' as Error,
87
+ isLoading: false,
88
+ };
89
+ } catch (error) {
90
+ return {
91
+ selectedOption: null,
92
+ error: 'Failed to check balances' as Error,
93
+ isLoading: false,
94
+ };
95
+ }
96
+ }, [
97
+ pendingFeeOptionConfirmation.options,
98
+ pendingFeeOptionConfirmation.chainId,
99
+ userAddress,
100
+ config,
101
+ ]);
102
+
103
+ return findAffordableOption();
104
+ }
@@ -1,6 +1,8 @@
1
- import { useState } from 'react';
1
+ import { useState, useEffect } from 'react';
2
2
  import { useCancelTransactionSteps } from './useCancelTransactionSteps';
3
3
  import type { MarketplaceKind } from '../../types';
4
+ import { useWaasFeeOptions } from '@0xsequence/kit';
5
+ import { useAutoSelectFeeOption } from './useAutoSelectFeeOption';
4
6
 
5
7
  interface UseCancelOrderArgs {
6
8
  collectionAddress: string;
@@ -29,6 +31,60 @@ export const useCancelOrder = ({
29
31
  const [cancellingOrderId, setCancellingOrderId] = useState<string | null>(
30
32
  null,
31
33
  );
34
+ const [pendingFeeOptionConfirmation, confirmPendingFeeOption] =
35
+ useWaasFeeOptions();
36
+ const autoSelectFeeOptionResult = useAutoSelectFeeOption({
37
+ pendingFeeOptionConfirmation: pendingFeeOptionConfirmation
38
+ ? {
39
+ id: pendingFeeOptionConfirmation.id,
40
+ options: pendingFeeOptionConfirmation.options?.map((opt) => ({
41
+ ...opt,
42
+ token: {
43
+ ...opt.token,
44
+ contractAddress: opt.token.contractAddress || null,
45
+ decimals: opt.token.decimals || 0,
46
+ tokenID: opt.token.tokenID || null,
47
+ },
48
+ })),
49
+ chainId: Number(chainId),
50
+ }
51
+ : {
52
+ id: '',
53
+ options: undefined,
54
+ chainId: Number(chainId),
55
+ },
56
+ });
57
+
58
+ useEffect(() => {
59
+ const handleFeeOptionSelection = async () => {
60
+ if (!pendingFeeOptionConfirmation) return;
61
+
62
+ const { selectedOption, error } = await autoSelectFeeOptionResult;
63
+
64
+ if (error) {
65
+ console.error('Error selecting fee option:', error);
66
+ onError?.(new Error(`Failed to select fee option: ${error}`));
67
+ return;
68
+ }
69
+
70
+ if (
71
+ selectedOption?.token.contractAddress &&
72
+ pendingFeeOptionConfirmation.id
73
+ ) {
74
+ confirmPendingFeeOption(
75
+ pendingFeeOptionConfirmation.id,
76
+ selectedOption.token.contractAddress,
77
+ );
78
+ }
79
+ };
80
+
81
+ handleFeeOptionSelection();
82
+ }, [
83
+ pendingFeeOptionConfirmation,
84
+ autoSelectFeeOptionResult,
85
+ confirmPendingFeeOption,
86
+ onError,
87
+ ]);
32
88
 
33
89
  const { cancelOrder: cancelOrderBase } = useCancelTransactionSteps({
34
90
  collectionAddress,
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ AddressSchema,
4
+ ChainIdSchema,
5
+ QueryArgSchema,
6
+ balanceQueries,
7
+ getIndexerClient,
8
+ } from '../_internal';
9
+ import { queryOptions, useQuery } from '@tanstack/react-query';
10
+ import { useConfig } from './useConfig';
11
+ import type { GetTokenBalancesDetailsReturn } from '@0xsequence/indexer';
12
+ import type { SdkConfig } from '../../types';
13
+
14
+ const filterSchema = z.object({
15
+ accountAddresses: z.array(AddressSchema),
16
+ contractWhitelist: z.array(AddressSchema).optional(),
17
+ omitNativeBalances: z.boolean(),
18
+ });
19
+
20
+ const useCollectionBalanceDetailsArgsSchema = z.object({
21
+ chainId: ChainIdSchema.pipe(z.coerce.number()),
22
+ filter: filterSchema,
23
+ query: QueryArgSchema.optional(),
24
+ });
25
+
26
+ export type CollectionBalanceFilter = z.infer<typeof filterSchema>;
27
+ export type UseCollectionBalanceDetailsArgs = z.input<
28
+ typeof useCollectionBalanceDetailsArgsSchema
29
+ >;
30
+
31
+ const fetchCollectionBalanceDetails = async (
32
+ args: UseCollectionBalanceDetailsArgs,
33
+ indexerClient: Awaited<ReturnType<typeof getIndexerClient>>,
34
+ ) => {
35
+ const promises = args.filter.accountAddresses.map((accountAddress) =>
36
+ indexerClient.getTokenBalancesDetails({
37
+ filter: {
38
+ accountAddresses: [accountAddress],
39
+ contractWhitelist: args.filter.contractWhitelist,
40
+ omitNativeBalances: args.filter.omitNativeBalances,
41
+ },
42
+ }),
43
+ );
44
+
45
+ const responses = await Promise.all(promises);
46
+ const mergedResponse = responses.reduce<GetTokenBalancesDetailsReturn>(
47
+ (acc, curr) => {
48
+ if (!curr) return acc;
49
+ return {
50
+ page: curr.page,
51
+ nativeBalances: [
52
+ ...(acc.nativeBalances || []),
53
+ ...(curr.nativeBalances || []),
54
+ ],
55
+ balances: [...(acc.balances || []), ...(curr.balances || [])],
56
+ };
57
+ },
58
+ { page: {}, nativeBalances: [], balances: [] },
59
+ );
60
+
61
+ if (!mergedResponse) {
62
+ throw new Error('Failed to fetch collection balance details');
63
+ }
64
+
65
+ return mergedResponse;
66
+ };
67
+
68
+ export const collectionBalanceDetailsOptions = (
69
+ args: UseCollectionBalanceDetailsArgs,
70
+ config: SdkConfig,
71
+ ) => {
72
+ const parsedArgs = useCollectionBalanceDetailsArgsSchema.parse(args);
73
+ const indexerClient = getIndexerClient(parsedArgs.chainId, config);
74
+
75
+ return queryOptions({
76
+ queryKey: [...balanceQueries.collectionBalanceDetails, args, config],
77
+ queryFn: () => fetchCollectionBalanceDetails(parsedArgs, indexerClient),
78
+ ...args.query,
79
+ });
80
+ };
81
+
82
+ export const useCollectionBalanceDetails = (
83
+ args: UseCollectionBalanceDetailsArgs,
84
+ ) => {
85
+ const config = useConfig();
86
+ return useQuery(collectionBalanceDetailsOptions(args, config));
87
+ };
@@ -0,0 +1,35 @@
1
+ import { queryOptions, useQuery } from '@tanstack/react-query';
2
+ import type { SdkConfig } from '../../types';
3
+ import { getMarketplaceClient } from '../_internal';
4
+ import { useConfig } from './useConfig';
5
+
6
+ type UseCollectionDetails = {
7
+ collectionAddress: string;
8
+ chainId: number;
9
+ };
10
+
11
+ const fetchCollectionDetails = async (
12
+ args: { collectionAddress: string },
13
+ marketplaceClient: Awaited<ReturnType<typeof getMarketplaceClient>>,
14
+ ) => {
15
+ const { collection } = await marketplaceClient.getCollectionDetail({
16
+ contractAddress: args.collectionAddress,
17
+ });
18
+ return collection;
19
+ };
20
+
21
+ export const collectionDetailsOptions = (
22
+ args: UseCollectionDetails,
23
+ config: SdkConfig,
24
+ ) => {
25
+ const marketplaceClient = getMarketplaceClient(args.chainId, config);
26
+ return queryOptions({
27
+ queryKey: ['collectionDetails', args],
28
+ queryFn: () => fetchCollectionDetails(args, marketplaceClient),
29
+ });
30
+ };
31
+
32
+ export const useCollectionDetails = (args: UseCollectionDetails) => {
33
+ const config = useConfig();
34
+ return useQuery(collectionDetailsOptions(args, config));
35
+ };