@0xsequence/marketplace-sdk 0.8.2 → 0.8.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 (101) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/{builder-types-Wrqq6YoW.d.ts → builder-types-BY6eD6vD.d.ts} +1 -1
  3. package/dist/{chunk-IZ44XPBH.js → chunk-25CAMYCG.js} +2 -2
  4. package/dist/{chunk-LDHGFXPJ.js → chunk-44YGZVBS.js} +2 -2
  5. package/dist/{chunk-BNAUZXPV.js → chunk-ALICO7NG.js} +3 -3
  6. package/dist/chunk-ALICO7NG.js.map +1 -0
  7. package/dist/{chunk-3II5GLHE.js → chunk-HGKWWZWY.js} +2 -2
  8. package/dist/{chunk-S2UFNIYX.js → chunk-HRL2TMXU.js} +12 -9
  9. package/dist/chunk-HRL2TMXU.js.map +1 -0
  10. package/dist/{chunk-XOHAZXBZ.js → chunk-VBRJ2OPM.js} +14 -6
  11. package/dist/chunk-VBRJ2OPM.js.map +1 -0
  12. package/dist/{chunk-F6CUGMI4.js → chunk-VF3LWBQB.js} +378 -263
  13. package/dist/chunk-VF3LWBQB.js.map +1 -0
  14. package/dist/{chunk-GBQVYNCD.js → chunk-XUNDLCEH.js} +3 -3
  15. package/dist/{chunk-4DFOSZTE.js → chunk-ZSCZLHKX.js} +195 -3
  16. package/dist/chunk-ZSCZLHKX.js.map +1 -0
  17. package/dist/{create-config-B58hoCDv.d.ts → create-config-qDML4ZNn.d.ts} +1 -1
  18. package/dist/{index-PhhCRKUH.d.ts → index-DtWR0b_l.d.ts} +1 -1
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.js +7 -9
  21. package/dist/{lowestListing-CuLxIWxy.d.ts → lowestListing-W7P4EkC3.d.ts} +34 -11
  22. package/dist/{marketplace.gen-De2-sxiG.d.ts → marketplace.gen-DS-MmGEB.d.ts} +35 -4
  23. package/dist/react/_internal/api/index.d.ts +2 -2
  24. package/dist/react/_internal/api/index.js +1 -1
  25. package/dist/react/_internal/databeat/index.d.ts +1 -1
  26. package/dist/react/_internal/databeat/index.js +8 -8
  27. package/dist/react/_internal/index.d.ts +5 -5
  28. package/dist/react/_internal/index.js +2 -2
  29. package/dist/react/_internal/wagmi/index.d.ts +3 -3
  30. package/dist/react/_internal/wagmi/index.js +2 -2
  31. package/dist/react/hooks/index.d.ts +72 -49
  32. package/dist/react/hooks/index.js +9 -7
  33. package/dist/react/hooks/options/index.d.ts +3 -3
  34. package/dist/react/hooks/options/index.js +4 -4
  35. package/dist/react/index.d.ts +8 -8
  36. package/dist/react/index.js +12 -10
  37. package/dist/react/queries/index.d.ts +2 -2
  38. package/dist/react/queries/index.js +7 -3
  39. package/dist/react/ssr/index.d.ts +2 -2
  40. package/dist/react/ssr/index.js +3 -3
  41. package/dist/react/ui/components/collectible-card/index.d.ts +1 -1
  42. package/dist/react/ui/components/collectible-card/index.js +10 -10
  43. package/dist/react/ui/icons/index.js +3 -3
  44. package/dist/react/ui/index.d.ts +1 -1
  45. package/dist/react/ui/index.js +10 -10
  46. package/dist/react/ui/modals/_internal/components/actionModal/index.js +8 -8
  47. package/dist/{services-BdzIAR9w.d.ts → services-BOX67E7W.d.ts} +1 -1
  48. package/dist/types/index.d.ts +3 -3
  49. package/dist/types/index.js +2 -4
  50. package/dist/{types-CmHOStH3.d.ts → types-CJLhc2VZ.d.ts} +2 -2
  51. package/dist/utils/abi/index.js +5 -5
  52. package/dist/utils/index.d.ts +1 -1
  53. package/dist/utils/index.js +7 -7
  54. package/package.json +1 -1
  55. package/src/react/_internal/api/__mocks__/marketplace.msw.ts +35 -18
  56. package/src/react/_internal/api/marketplace.gen.ts +39 -6
  57. package/src/react/_internal/api/zod-schema.ts +7 -18
  58. package/src/react/hooks/__tests__/useCancelTransactionSteps.test.tsx +4 -9
  59. package/src/react/hooks/__tests__/useGenerateCancelTransaction.test.tsx +5 -4
  60. package/src/react/hooks/__tests__/useGenerateListingTransaction.test.tsx +14 -2
  61. package/src/react/hooks/__tests__/useGenerateOfferTransaction.test.tsx +115 -57
  62. package/src/react/hooks/__tests__/useGenerateSellTransaction.test.tsx +10 -3
  63. package/src/react/hooks/__tests__/useInventory.test.tsx +294 -0
  64. package/src/react/hooks/index.ts +1 -0
  65. package/src/react/hooks/useAutoSelectFeeOption.tsx +10 -3
  66. package/src/react/hooks/useCancelOrder.tsx +1 -0
  67. package/src/react/hooks/useCancelTransactionSteps.tsx +20 -6
  68. package/src/react/hooks/useGenerateOfferTransaction.tsx +11 -1
  69. package/src/react/hooks/useInventory.tsx +15 -0
  70. package/src/react/hooks/util/optimisticCancelUpdates.ts +115 -0
  71. package/src/react/queries/index.ts +1 -0
  72. package/src/react/queries/inventory.ts +303 -0
  73. package/src/react/queries/listBalances.ts +1 -8
  74. package/src/react/queries/listCollectibles.ts +12 -3
  75. package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +74 -104
  76. package/src/react/ui/modals/CreateListingModal/hooks/useTransactionSteps.tsx +2 -2
  77. package/src/react/ui/modals/MakeOfferModal/__tests__/Modal.test.tsx +108 -78
  78. package/src/react/ui/modals/MakeOfferModal/hooks/useTransactionSteps.tsx +2 -2
  79. package/src/react/ui/modals/SellModal/hooks/useTransactionSteps.tsx +2 -2
  80. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.test.tsx +286 -0
  81. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +16 -4
  82. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx +35 -132
  83. package/src/react/ui/modals/_internal/components/floorPriceText/__tests__/FloorPriceText.test.tsx +199 -0
  84. package/src/react/ui/modals/_internal/components/priceInput/__tests__/PriceInput.test.tsx +55 -0
  85. package/src/react/ui/modals/_internal/components/priceInput/index.tsx +1 -1
  86. package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +2 -2
  87. package/src/types/api-types.ts +0 -1
  88. package/test/const.ts +24 -0
  89. package/test/test-utils.tsx +74 -26
  90. package/.changeset/flat-parks-clean.md +0 -8
  91. package/.changeset/seven-doors-taste.md +0 -5
  92. package/dist/chunk-4DFOSZTE.js.map +0 -1
  93. package/dist/chunk-BNAUZXPV.js.map +0 -1
  94. package/dist/chunk-F6CUGMI4.js.map +0 -1
  95. package/dist/chunk-S2UFNIYX.js.map +0 -1
  96. package/dist/chunk-XOHAZXBZ.js.map +0 -1
  97. package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +0 -164
  98. /package/dist/{chunk-IZ44XPBH.js.map → chunk-25CAMYCG.js.map} +0 -0
  99. /package/dist/{chunk-LDHGFXPJ.js.map → chunk-44YGZVBS.js.map} +0 -0
  100. /package/dist/{chunk-3II5GLHE.js.map → chunk-HGKWWZWY.js.map} +0 -0
  101. /package/dist/{chunk-GBQVYNCD.js.map → chunk-XUNDLCEH.js.map} +0 -0
@@ -50,12 +50,18 @@ describe('useGenerateListingTransaction', () => {
50
50
  [
51
51
  {
52
52
  "data": "0x...",
53
- "executeType": "order",
54
53
  "id": "tokenApproval",
55
54
  "price": "0",
56
55
  "to": "0x1234567890123456789012345678901234567890",
57
56
  "value": "0",
58
57
  },
58
+ {
59
+ "data": "0x...",
60
+ "id": "createListing",
61
+ "price": "0",
62
+ "to": "0x1234567890123456789012345678901234567890",
63
+ "value": "0",
64
+ },
59
65
  ],
60
66
  {
61
67
  "collectionAddress": "0x0000000000000000000000000000000000000000",
@@ -91,12 +97,18 @@ describe('useGenerateListingTransaction', () => {
91
97
  [
92
98
  {
93
99
  "data": "0x...",
94
- "executeType": "order",
95
100
  "id": "tokenApproval",
96
101
  "price": "0",
97
102
  "to": "0x1234567890123456789012345678901234567890",
98
103
  "value": "0",
99
104
  },
105
+ {
106
+ "data": "0x...",
107
+ "id": "createListing",
108
+ "price": "0",
109
+ "to": "0x1234567890123456789012345678901234567890",
110
+ "value": "0",
111
+ },
100
112
  ],
101
113
  {
102
114
  "collectionAddress": "0x0000000000000000000000000000000000000000",
@@ -1,12 +1,19 @@
1
1
  import { renderHook, server, waitFor } from '@test';
2
+ import { createMockWallet } from '@test/mocks/wallet';
2
3
  import { http, HttpResponse } from 'msw';
3
4
  import { zeroAddress } from 'viem';
4
5
  import { beforeEach, describe, expect, it, vi } from 'vitest';
5
- import { mockMarketplaceEndpoint } from '../../_internal/api/__mocks__/marketplace.msw';
6
+ import {
7
+ createMockSteps,
8
+ mockMarketplaceEndpoint,
9
+ } from '../../_internal/api/__mocks__/marketplace.msw';
6
10
  import {
7
11
  ContractType,
8
12
  OrderbookKind,
13
+ StepType,
14
+ WalletKind,
9
15
  } from '../../_internal/api/marketplace.gen';
16
+ import * as walletModule from '../../_internal/wallet/useWallet';
10
17
  import { useGenerateOfferTransaction } from '../useGenerateOfferTransaction';
11
18
 
12
19
  describe('useGenerateOfferTransaction', () => {
@@ -44,34 +51,9 @@ describe('useGenerateOfferTransaction', () => {
44
51
 
45
52
  await result.current.generateOfferTransactionAsync(mockTransactionProps);
46
53
 
47
- expect(mockOnSuccess.mock.lastCall).toMatchInlineSnapshot(`
48
- [
49
- [
50
- {
51
- "data": "0x...",
52
- "executeType": "order",
53
- "id": "tokenApproval",
54
- "price": "0",
55
- "to": "0x1234567890123456789012345678901234567890",
56
- "value": "0",
57
- },
58
- ],
59
- {
60
- "collectionAddress": "0x0000000000000000000000000000000000000000",
61
- "contractType": "ERC721",
62
- "maker": "0x0000000000000000000000000000000000000000",
63
- "offer": {
64
- "currencyAddress": "0x0000000000000000000000000000000000000000",
65
- "expiry": 2024-12-31T00:00:00.000Z,
66
- "pricePerToken": "1000000000000000000",
67
- "quantity": "1",
68
- "tokenId": "1",
69
- },
70
- "orderbook": "sequence_marketplace_v2",
71
- },
72
- undefined,
73
- ]
74
- `);
54
+ expect(mockOnSuccess).toHaveBeenCalled();
55
+ const steps = mockOnSuccess.mock.calls[0]?.[0];
56
+ expect(steps.length).toBeGreaterThan(0);
75
57
  });
76
58
 
77
59
  it('should handle non-async generation with callback', async () => {
@@ -84,34 +66,9 @@ describe('useGenerateOfferTransaction', () => {
84
66
  await waitFor(() => {
85
67
  expect(mockOnSuccess).toHaveBeenCalled();
86
68
  });
87
- expect(mockOnSuccess.mock.lastCall).toMatchInlineSnapshot(`
88
- [
89
- [
90
- {
91
- "data": "0x...",
92
- "executeType": "order",
93
- "id": "tokenApproval",
94
- "price": "0",
95
- "to": "0x1234567890123456789012345678901234567890",
96
- "value": "0",
97
- },
98
- ],
99
- {
100
- "collectionAddress": "0x0000000000000000000000000000000000000000",
101
- "contractType": "ERC721",
102
- "maker": "0x0000000000000000000000000000000000000000",
103
- "offer": {
104
- "currencyAddress": "0x0000000000000000000000000000000000000000",
105
- "expiry": 2024-12-31T00:00:00.000Z,
106
- "pricePerToken": "1000000000000000000",
107
- "quantity": "1",
108
- "tokenId": "1",
109
- },
110
- "orderbook": "sequence_marketplace_v2",
111
- },
112
- undefined,
113
- ]
114
- `);
69
+
70
+ const steps = mockOnSuccess.mock.calls[0]?.[0];
71
+ expect(steps.length).toBeGreaterThan(0);
115
72
  });
116
73
 
117
74
  it('should handle API errors', async () => {
@@ -158,4 +115,105 @@ describe('useGenerateOfferTransaction', () => {
158
115
 
159
116
  expect(mockOnSuccess).not.toHaveBeenCalled();
160
117
  });
118
+
119
+ describe('wallet-specific behavior', () => {
120
+ // Create mock wallets for different types
121
+ const mockSequenceWallet = createMockWallet({
122
+ walletKind: WalletKind.sequence,
123
+ });
124
+
125
+ const mockNonSequenceWallet = createMockWallet({
126
+ walletKind: WalletKind.unknown,
127
+ });
128
+
129
+ it('should not include tokenApproval step for Sequence wallet', async () => {
130
+ // Mock useWallet to return a Sequence wallet
131
+ const useWalletSpy = vi.spyOn(walletModule, 'useWallet');
132
+ useWalletSpy.mockReturnValue({
133
+ wallet: mockSequenceWallet,
134
+ isLoading: false,
135
+ isError: false,
136
+ });
137
+
138
+ // Override the default handler to include walletKind in the response
139
+ server.use(
140
+ http.post(
141
+ mockMarketplaceEndpoint('GenerateOfferTransaction'),
142
+ async ({ request }) => {
143
+ // Add wallet type to the request payload
144
+ const reqBody = (await request.json()) as Record<string, unknown>;
145
+ reqBody.walletType = WalletKind.sequence;
146
+
147
+ // For Sequence wallet - only return createOffer step
148
+ return HttpResponse.json({
149
+ steps: createMockSteps([StepType.createOffer]),
150
+ });
151
+ },
152
+ ),
153
+ );
154
+
155
+ const { result } = renderHook(() =>
156
+ useGenerateOfferTransaction(defaultArgs),
157
+ );
158
+
159
+ await result.current.generateOfferTransactionAsync(mockTransactionProps);
160
+
161
+ expect(mockOnSuccess).toHaveBeenCalled();
162
+ const steps = mockOnSuccess.mock.calls[0]?.[0];
163
+
164
+ // Verify there is only one step: createOffer (no tokenApproval)
165
+ expect(steps).toHaveLength(1);
166
+ expect(steps[0].id).toBe('createOffer');
167
+
168
+ // Restore the original useWallet implementation
169
+ useWalletSpy.mockRestore();
170
+ });
171
+
172
+ it('should include tokenApproval step for non-Sequence wallet', async () => {
173
+ // Mock useWallet to return a non-Sequence wallet
174
+ const useWalletSpy = vi.spyOn(walletModule, 'useWallet');
175
+ useWalletSpy.mockReturnValue({
176
+ wallet: mockNonSequenceWallet,
177
+ isLoading: false,
178
+ isError: false,
179
+ });
180
+
181
+ // Override the default handler to include walletKind in the response
182
+ server.use(
183
+ http.post(
184
+ mockMarketplaceEndpoint('GenerateOfferTransaction'),
185
+ async ({ request }) => {
186
+ // Add wallet type to the request payload
187
+ const reqBody = (await request.json()) as Record<string, unknown>;
188
+ reqBody.walletType = WalletKind.unknown;
189
+
190
+ // For non-Sequence wallet - return tokenApproval and createOffer steps
191
+ return HttpResponse.json({
192
+ steps: createMockSteps([
193
+ StepType.tokenApproval,
194
+ StepType.createOffer,
195
+ ]),
196
+ });
197
+ },
198
+ ),
199
+ );
200
+
201
+ const { result } = renderHook(() =>
202
+ useGenerateOfferTransaction(defaultArgs),
203
+ );
204
+
205
+ await result.current.generateOfferTransactionAsync(mockTransactionProps);
206
+
207
+ expect(mockOnSuccess).toHaveBeenCalled();
208
+ const steps = mockOnSuccess.mock.calls[0]?.[0];
209
+
210
+ // Verify there are two steps: tokenApproval and createOffer
211
+ expect(steps).toHaveLength(2);
212
+ expect(steps[0].id).toBe('tokenApproval');
213
+ expect(steps[1].id).toBe('createOffer');
214
+
215
+ // Restore the original useWallet implementation
216
+ useWalletSpy.mockRestore();
217
+ });
218
+ });
161
219
  });
@@ -3,13 +3,14 @@ import { http, HttpResponse } from 'msw';
3
3
  import { zeroAddress } from 'viem';
4
4
  import { beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import {
6
+ createMockSteps,
6
7
  mockMarketplaceEndpoint,
7
- mockSteps,
8
8
  } from '../../_internal/api/__mocks__/marketplace.msw';
9
9
  import {
10
10
  ContractType,
11
11
  MarketplaceKind,
12
12
  OrderbookKind,
13
+ StepType,
13
14
  } from '../../_internal/api/marketplace.gen';
14
15
  import { useGenerateSellTransaction } from '../useGenerateSellTransaction';
15
16
 
@@ -52,12 +53,18 @@ describe('useGenerateSellTransaction', () => {
52
53
  [
53
54
  {
54
55
  "data": "0x...",
55
- "executeType": "order",
56
56
  "id": "tokenApproval",
57
57
  "price": "0",
58
58
  "to": "0x1234567890123456789012345678901234567890",
59
59
  "value": "0",
60
60
  },
61
+ {
62
+ "data": "0x...",
63
+ "id": "sell",
64
+ "price": "0",
65
+ "to": "0x1234567890123456789012345678901234567890",
66
+ "value": "0",
67
+ },
61
68
  ],
62
69
  {
63
70
  "additionalFees": [],
@@ -87,7 +94,7 @@ describe('useGenerateSellTransaction', () => {
87
94
 
88
95
  await waitFor(() => {
89
96
  expect(mockOnSuccess).toHaveBeenCalledWith(
90
- mockSteps,
97
+ createMockSteps([StepType.tokenApproval, StepType.sell]),
91
98
  mockTransactionProps,
92
99
  undefined,
93
100
  );
@@ -0,0 +1,294 @@
1
+ import { renderHook, server, waitFor } from '@test';
2
+ import { http, HttpResponse } from 'msw';
3
+ import { zeroAddress } from 'viem';
4
+ import { beforeEach, describe, expect, it } from 'vitest';
5
+ import {
6
+ mockIndexerEndpoint,
7
+ mockTokenBalance,
8
+ } from '../../_internal/api/__mocks__/indexer.msw';
9
+ import {
10
+ mockCollectibleOrder,
11
+ mockMarketplaceEndpoint,
12
+ } from '../../_internal/api/__mocks__/marketplace.msw';
13
+ import type { UseInventoryArgs } from '../../queries/inventory';
14
+ import { mockConfig } from '../options/__mocks__/marketplaceConfig.msw';
15
+ import { useInventory } from '../useInventory';
16
+
17
+ // Make sure mockCollectibleOrder has a tokenId of "1" for tests
18
+ mockCollectibleOrder.metadata.tokenId = '1';
19
+
20
+ describe('useInventory', () => {
21
+ const defaultArgs: UseInventoryArgs = {
22
+ accountAddress:
23
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
24
+ chainId: 1,
25
+ collectionAddress: zeroAddress,
26
+ };
27
+
28
+ beforeEach(() => {
29
+ // Reset MSW handlers before each test to ensure a clean state
30
+ server.resetHandlers();
31
+ });
32
+
33
+ it('should fetch inventory data successfully', async () => {
34
+ const { result } = renderHook(() => useInventory(defaultArgs));
35
+
36
+ // Initially loading
37
+ expect(result.current.isLoading).toBe(true);
38
+ expect(result.current.data).toBeUndefined();
39
+
40
+ // Wait for data to be loaded
41
+ await waitFor(() => {
42
+ expect(result.current.isLoading).toBe(false);
43
+ });
44
+
45
+ // Verify the data is defined and has pages
46
+ expect(result.current.data).toBeDefined();
47
+ expect(result.current.data?.pages).toBeDefined();
48
+ expect(result.current.error).toBeNull();
49
+ });
50
+
51
+ it('should handle error gracefully', async () => {
52
+ // Mock both API endpoints to return errors
53
+ server.use(
54
+ http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
55
+ return new HttpResponse(null, { status: 500 });
56
+ }),
57
+ http.post(mockIndexerEndpoint('GetTokenBalances'), () => {
58
+ return new HttpResponse(null, { status: 500 });
59
+ }),
60
+ );
61
+
62
+ const { result } = renderHook(() =>
63
+ useInventory({
64
+ ...defaultArgs,
65
+ // Add a unique key to avoid caching
66
+ collectionAddress:
67
+ '0xdeadbeef0000000000000000000000000000dead' as `0x${string}`,
68
+ }),
69
+ );
70
+
71
+ // Wait for the query to finish loading and show error
72
+ await waitFor(
73
+ () => {
74
+ expect(result.current.isLoading).toBe(false);
75
+ },
76
+ { timeout: 3000 },
77
+ );
78
+
79
+ // Verify error state
80
+ expect(result.current.status).toBe('error');
81
+ expect(result.current.isError).toBe(true);
82
+ expect(result.current.error).not.toBeNull();
83
+ });
84
+
85
+ it('should refetch when args change', async () => {
86
+ const { result, rerender } = renderHook(() => useInventory(defaultArgs));
87
+
88
+ // Wait for initial data
89
+ await waitFor(() => {
90
+ expect(result.current.isLoading).toBe(false);
91
+ });
92
+
93
+ // Change args and rerender with a new hook
94
+ const newArgs = {
95
+ ...defaultArgs,
96
+ collectionAddress:
97
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
98
+ };
99
+
100
+ rerender(() => useInventory(newArgs));
101
+
102
+ // Wait for new data
103
+ await waitFor(() => {
104
+ expect(result.current.data).toBeDefined();
105
+ });
106
+
107
+ // Verify that the query was refetched with new args
108
+ expect(result.current.data).toBeDefined();
109
+ expect(result.current.isSuccess).toBe(true);
110
+ });
111
+
112
+ it('should handle disabled queries', async () => {
113
+ const disabledArgs: UseInventoryArgs = {
114
+ ...defaultArgs,
115
+ query: {
116
+ enabled: false,
117
+ },
118
+ };
119
+
120
+ const { result } = renderHook(() => useInventory(disabledArgs));
121
+
122
+ // Should not be loading or have data
123
+ expect(result.current.isLoading).toBe(false);
124
+ expect(result.current.data).toBeUndefined();
125
+ expect(result.current.isFetched).toBe(false);
126
+ });
127
+
128
+ it('should use isLaos721 flag from marketplaceConfig', async () => {
129
+ // Setup config with LAOS collection
130
+ const laosCollectionAddress = '0x1234567890123456789012345678901234567890';
131
+ server.use(
132
+ http.get('*/marketplace/*/settings.json', () => {
133
+ const configWithLaos = {
134
+ ...mockConfig,
135
+ collections: [
136
+ {
137
+ ...mockConfig.collections[0],
138
+ address: laosCollectionAddress,
139
+ isLAOSERC721: true,
140
+ },
141
+ ],
142
+ };
143
+ return HttpResponse.json(configWithLaos);
144
+ }),
145
+ );
146
+
147
+ const laosArgs: UseInventoryArgs = {
148
+ ...defaultArgs,
149
+ collectionAddress: laosCollectionAddress as `0x${string}`,
150
+ };
151
+
152
+ const { result } = renderHook(() => useInventory(laosArgs));
153
+
154
+ await waitFor(() => {
155
+ expect(result.current.isLoading).toBe(false);
156
+ });
157
+
158
+ expect(result.current.data).toBeDefined();
159
+ expect(result.current.isSuccess).toBe(true);
160
+ });
161
+
162
+ it('should fetch data from indexer when marketplace API has no more results', async () => {
163
+ // Mock marketplace API with empty results
164
+ server.use(
165
+ http.post(mockMarketplaceEndpoint('ListCollectibles'), () => {
166
+ return HttpResponse.json({
167
+ collectibles: [],
168
+ page: {
169
+ page: 1,
170
+ pageSize: 50,
171
+ more: false,
172
+ },
173
+ });
174
+ }),
175
+ // Mock indexer with data
176
+ http.post(mockIndexerEndpoint('GetTokenBalances'), () => {
177
+ return HttpResponse.json({
178
+ balances: [mockTokenBalance],
179
+ page: {
180
+ page: 1,
181
+ pageSize: 50,
182
+ more: false,
183
+ },
184
+ });
185
+ }),
186
+ );
187
+
188
+ const { result } = renderHook(() => useInventory(defaultArgs));
189
+
190
+ await waitFor(() => {
191
+ expect(result.current.isLoading).toBe(false);
192
+ });
193
+
194
+ expect(result.current.data).toBeDefined();
195
+ expect(result.current.data?.pages[0].collectibles).toBeDefined();
196
+ expect(result.current.isSuccess).toBe(true);
197
+ });
198
+
199
+ it('should support pagination with fetchNextPage', async () => {
200
+ // Simplified test for pagination
201
+ // Set up mock data for first and second pages
202
+ const page1Data = {
203
+ collectibles: [
204
+ {
205
+ ...mockCollectibleOrder,
206
+ metadata: {
207
+ ...mockCollectibleOrder.metadata,
208
+ tokenId: '1',
209
+ name: 'Token 1',
210
+ },
211
+ },
212
+ ],
213
+ page: {
214
+ page: 1,
215
+ pageSize: 10,
216
+ more: true, // indicate there's a next page
217
+ },
218
+ };
219
+
220
+ // Mock API to return data for the first page
221
+ server.use(
222
+ http.post(
223
+ mockMarketplaceEndpoint('ListCollectibles'),
224
+ async ({ request }) => {
225
+ const body = (await request.json()) as { page?: number };
226
+ const pageNumber = body?.page || 1;
227
+
228
+ // Return first page data
229
+ if (pageNumber === 1) {
230
+ return HttpResponse.json(page1Data);
231
+ }
232
+
233
+ // For second page, return empty collectibles but with more=false
234
+ return HttpResponse.json({
235
+ collectibles: [],
236
+ page: {
237
+ page: 2,
238
+ pageSize: 10,
239
+ more: false,
240
+ },
241
+ });
242
+ },
243
+ ),
244
+ );
245
+
246
+ // Use unique test args to avoid caching issues
247
+ const testArgs = {
248
+ ...defaultArgs,
249
+ accountAddress:
250
+ '0xabcdef1234567890abcdef1234567890abcdef12' as `0x${string}`,
251
+ collectionAddress:
252
+ '0xabcdef1234567890abcdef1234567890abcdef12' as `0x${string}`,
253
+ };
254
+
255
+ const { result } = renderHook(() => useInventory(testArgs));
256
+
257
+ // Wait for the first page to load
258
+ await waitFor(
259
+ () => {
260
+ expect(result.current.isLoading).toBe(false);
261
+ expect(result.current.data?.pages).toBeDefined();
262
+ },
263
+ { timeout: 5000 },
264
+ );
265
+
266
+ // Verify first page data loaded correctly
267
+ expect(result.current.data?.pages[0].collectibles).toBeDefined();
268
+ expect(result.current.data?.pages[0].collectibles.length).toBeGreaterThan(
269
+ 0,
270
+ );
271
+ // Check that at least one item has the expected tokenId
272
+ expect(
273
+ result.current.data?.pages[0].collectibles.some(
274
+ (c) => c.metadata.tokenId === '1',
275
+ ),
276
+ ).toBe(true);
277
+ expect(result.current.hasNextPage).toBe(true);
278
+
279
+ // Fetch the next page
280
+ await result.current.fetchNextPage();
281
+
282
+ // Wait for second page to load
283
+ await waitFor(
284
+ () => {
285
+ expect(result.current.data?.pages.length).toBe(2);
286
+ },
287
+ { timeout: 5000 },
288
+ );
289
+
290
+ // For an empty second page we just verify it exists
291
+ expect(result.current.data?.pages[1]).toBeDefined();
292
+ expect(result.current.hasNextPage).toBe(false); // No more pages
293
+ });
294
+ });
@@ -12,6 +12,7 @@ export * from './useCurrency';
12
12
  export * from './useFilters';
13
13
  export * from './useFloorOrder';
14
14
  export * from './useHighestOffer';
15
+ export * from './useInventory';
15
16
  export * from './useListBalances';
16
17
  export * from './useListCollectibleActivities';
17
18
  export * from './useListCollectibles';
@@ -1,7 +1,7 @@
1
1
  import { type Address, zeroAddress } from 'viem';
2
2
 
3
3
  import { useChain } from '@0xsequence/connect';
4
- import { useCallback } from 'react';
4
+ import { useCallback, useEffect } from 'react';
5
5
  import { useAccount } from 'wagmi';
6
6
  import type { FeeOption } from '../../types/waas-types';
7
7
  import { useCollectionBalanceDetails } from './useCollectionBalanceDetails';
@@ -19,6 +19,7 @@ type UseAutoSelectFeeOptionArgs = {
19
19
  options: FeeOption[] | undefined;
20
20
  chainId: number;
21
21
  };
22
+ enabled?: boolean;
22
23
  };
23
24
 
24
25
  /**
@@ -87,6 +88,7 @@ type UseAutoSelectFeeOptionArgs = {
87
88
  */
88
89
  export function useAutoSelectFeeOption({
89
90
  pendingFeeOptionConfirmation,
91
+ enabled,
90
92
  }: UseAutoSelectFeeOptionArgs) {
91
93
  const { address: userAddress } = useAccount();
92
94
 
@@ -110,7 +112,8 @@ export function useAutoSelectFeeOption({
110
112
  omitNativeBalances: false,
111
113
  },
112
114
  query: {
113
- enabled: !!pendingFeeOptionConfirmation.options && !!userAddress,
115
+ enabled:
116
+ !!pendingFeeOptionConfirmation.options && !!userAddress && enabled,
114
117
  },
115
118
  });
116
119
  const chain = useChain(pendingFeeOptionConfirmation.chainId);
@@ -131,7 +134,11 @@ export function useAutoSelectFeeOption({
131
134
  })),
132
135
  ];
133
136
 
134
- console.debug('currency balances', combinedBalances);
137
+ useEffect(() => {
138
+ if (combinedBalances) {
139
+ console.debug('currency balances', combinedBalances);
140
+ }
141
+ }, [combinedBalances]);
135
142
 
136
143
  const autoSelectedOption = useCallback(async () => {
137
144
  if (!userAddress) {
@@ -54,6 +54,7 @@ export const useCancelOrder = ({
54
54
  options: undefined,
55
55
  chainId,
56
56
  },
57
+ enabled: !!pendingFeeOptionConfirmation,
57
58
  });
58
59
 
59
60
  useEffect(() => {