@net-protocol/bazaar 0.1.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.
package/dist/react.mjs ADDED
@@ -0,0 +1,1393 @@
1
+ import { useState, useMemo, useEffect } from 'react';
2
+ import { useNetMessageCount, useNetMessages } from '@net-protocol/core/react';
3
+ import { createPublicClient, http, defineChain, decodeAbiParameters, formatEther } from 'viem';
4
+ import { NetClient } from '@net-protocol/core';
5
+ import { Seaport } from '@opensea/seaport-js';
6
+ import { ethers } from 'ethers';
7
+ import { readContract } from 'viem/actions';
8
+
9
+ // src/hooks/useBazaarListings.ts
10
+
11
+ // src/abis/helpers.ts
12
+ var BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI = [
13
+ { type: "constructor", inputs: [], stateMutability: "nonpayable" },
14
+ {
15
+ type: "function",
16
+ name: "getOrderStatuses",
17
+ inputs: [
18
+ { name: "seaport", type: "address", internalType: "address" },
19
+ { name: "orderHashes", type: "bytes32[]", internalType: "bytes32[]" }
20
+ ],
21
+ outputs: [
22
+ {
23
+ name: "results",
24
+ type: "tuple[]",
25
+ internalType: "struct BulkSeaportOrderStatusFetcher.OrderStatusInfo[]",
26
+ components: [
27
+ { name: "isValidated", type: "bool", internalType: "bool" },
28
+ { name: "isCancelled", type: "bool", internalType: "bool" },
29
+ { name: "totalFilled", type: "uint256", internalType: "uint256" },
30
+ { name: "totalSize", type: "uint256", internalType: "uint256" }
31
+ ]
32
+ }
33
+ ],
34
+ stateMutability: "view"
35
+ }
36
+ ];
37
+ var ERC721_OWNER_OF_HELPER_ABI = [
38
+ {
39
+ type: "function",
40
+ name: "getTokenOwners",
41
+ inputs: [
42
+ { name: "nftContract", type: "address", internalType: "address" },
43
+ { name: "tokenIds", type: "uint256[]", internalType: "uint256[]" }
44
+ ],
45
+ outputs: [
46
+ { name: "owners", type: "address[]", internalType: "address[]" }
47
+ ],
48
+ stateMutability: "view"
49
+ },
50
+ { type: "error", name: "InvalidAddress", inputs: [] },
51
+ { type: "error", name: "TokenQueryFailed", inputs: [] }
52
+ ];
53
+ var ERC20_BULK_BALANCE_CHECKER_ABI = [
54
+ {
55
+ type: "function",
56
+ name: "getBalances",
57
+ inputs: [
58
+ { name: "token", type: "address", internalType: "address" },
59
+ { name: "addresses", type: "address[]", internalType: "address[]" }
60
+ ],
61
+ outputs: [
62
+ { name: "balances", type: "uint256[]", internalType: "uint256[]" }
63
+ ],
64
+ stateMutability: "view"
65
+ }
66
+ ];
67
+
68
+ // src/abis/seaport.ts
69
+ var SEAPORT_CANCEL_ABI = [
70
+ {
71
+ inputs: [
72
+ {
73
+ components: [
74
+ { internalType: "address", name: "offerer", type: "address" },
75
+ { internalType: "address", name: "zone", type: "address" },
76
+ {
77
+ components: [
78
+ { internalType: "enum ItemType", name: "itemType", type: "uint8" },
79
+ { internalType: "address", name: "token", type: "address" },
80
+ { internalType: "uint256", name: "identifierOrCriteria", type: "uint256" },
81
+ { internalType: "uint256", name: "startAmount", type: "uint256" },
82
+ { internalType: "uint256", name: "endAmount", type: "uint256" }
83
+ ],
84
+ internalType: "struct OfferItem[]",
85
+ name: "offer",
86
+ type: "tuple[]"
87
+ },
88
+ {
89
+ components: [
90
+ { internalType: "enum ItemType", name: "itemType", type: "uint8" },
91
+ { internalType: "address", name: "token", type: "address" },
92
+ { internalType: "uint256", name: "identifierOrCriteria", type: "uint256" },
93
+ { internalType: "uint256", name: "startAmount", type: "uint256" },
94
+ { internalType: "uint256", name: "endAmount", type: "uint256" },
95
+ { internalType: "address payable", name: "recipient", type: "address" }
96
+ ],
97
+ internalType: "struct ConsiderationItem[]",
98
+ name: "consideration",
99
+ type: "tuple[]"
100
+ },
101
+ { internalType: "enum OrderType", name: "orderType", type: "uint8" },
102
+ { internalType: "uint256", name: "startTime", type: "uint256" },
103
+ { internalType: "uint256", name: "endTime", type: "uint256" },
104
+ { internalType: "bytes32", name: "zoneHash", type: "bytes32" },
105
+ { internalType: "uint256", name: "salt", type: "uint256" },
106
+ { internalType: "bytes32", name: "conduitKey", type: "bytes32" },
107
+ { internalType: "uint256", name: "counter", type: "uint256" }
108
+ ],
109
+ internalType: "struct OrderComponents[]",
110
+ name: "orders",
111
+ type: "tuple[]"
112
+ }
113
+ ],
114
+ name: "cancel",
115
+ outputs: [{ internalType: "bool", name: "cancelled", type: "bool" }],
116
+ stateMutability: "nonpayable",
117
+ type: "function"
118
+ }
119
+ ];
120
+
121
+ // src/abis/index.ts
122
+ var BAZAAR_SUBMISSION_ABI = [
123
+ {
124
+ name: "submission",
125
+ type: "tuple",
126
+ internalType: "struct BazaarV2.Submission",
127
+ components: [
128
+ {
129
+ name: "parameters",
130
+ type: "tuple",
131
+ internalType: "struct OrderParameters",
132
+ components: [
133
+ { name: "offerer", type: "address", internalType: "address" },
134
+ { name: "zone", type: "address", internalType: "address" },
135
+ {
136
+ name: "offer",
137
+ type: "tuple[]",
138
+ internalType: "struct OfferItem[]",
139
+ components: [
140
+ { name: "itemType", type: "uint8", internalType: "enum ItemType" },
141
+ { name: "token", type: "address", internalType: "address" },
142
+ { name: "identifierOrCriteria", type: "uint256", internalType: "uint256" },
143
+ { name: "startAmount", type: "uint256", internalType: "uint256" },
144
+ { name: "endAmount", type: "uint256", internalType: "uint256" }
145
+ ]
146
+ },
147
+ {
148
+ name: "consideration",
149
+ type: "tuple[]",
150
+ internalType: "struct ConsiderationItem[]",
151
+ components: [
152
+ { name: "itemType", type: "uint8", internalType: "enum ItemType" },
153
+ { name: "token", type: "address", internalType: "address" },
154
+ { name: "identifierOrCriteria", type: "uint256", internalType: "uint256" },
155
+ { name: "startAmount", type: "uint256", internalType: "uint256" },
156
+ { name: "endAmount", type: "uint256", internalType: "uint256" },
157
+ { name: "recipient", type: "address", internalType: "address payable" }
158
+ ]
159
+ },
160
+ { name: "orderType", type: "uint8", internalType: "enum OrderType" },
161
+ { name: "startTime", type: "uint256", internalType: "uint256" },
162
+ { name: "endTime", type: "uint256", internalType: "uint256" },
163
+ { name: "zoneHash", type: "bytes32", internalType: "bytes32" },
164
+ { name: "salt", type: "uint256", internalType: "uint256" },
165
+ { name: "conduitKey", type: "bytes32", internalType: "bytes32" },
166
+ { name: "totalOriginalConsiderationItems", type: "uint256", internalType: "uint256" }
167
+ ]
168
+ },
169
+ { name: "counter", type: "uint256", internalType: "uint256" },
170
+ { name: "signature", type: "bytes", internalType: "bytes" }
171
+ ]
172
+ }
173
+ ];
174
+
175
+ // src/chainConfig.ts
176
+ var DEFAULT_SEAPORT_ADDRESS = "0x0000000000000068F116a894984e2DB1123eB395";
177
+ var DEFAULT_BAZAAR_ADDRESS = "0x00000000E3dA5fC031282A39759bDDA78ae7fAE5";
178
+ var DEFAULT_COLLECTION_OFFERS_ADDRESS = "0x0000000D43423E0A12CecB307a74591999b32B32";
179
+ var DEFAULT_FEE_COLLECTOR_ADDRESS = "0x32D16C15410248bef498D7aF50D10Db1a546b9E5";
180
+ var DEFAULT_NFT_FEE_BPS = 500;
181
+ var BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS = "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
182
+ var ERC721_OWNER_OF_HELPER_ADDRESS = "0x000000aa4eFa2e5A4a6002C7F08B6e8Ec8cf1dDa";
183
+ var ERC20_BULK_BALANCE_CHECKER_ADDRESS = "0x000000b50a9f2923f2db931391824f6d1278f712";
184
+ var NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS = "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
185
+ var BAZAAR_CHAIN_CONFIGS = {
186
+ // Base Mainnet
187
+ 8453: {
188
+ bazaarAddress: "0x000000058f3ade587388daf827174d0e6fc97595",
189
+ collectionOffersAddress: "0x0000000f9c45efcff0f78d8b54aa6a40092d66dc",
190
+ erc20OffersAddress: "0x0000000e23a89aa06f317306aa1ae231d3503082",
191
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
192
+ feeCollectorAddress: "0x66547ff4f7206e291F7BC157b54C026Fc6660961",
193
+ nftFeeBps: 0,
194
+ // 0% on Base
195
+ wrappedNativeCurrency: {
196
+ address: "0x4200000000000000000000000000000000000006",
197
+ name: "Wrapped Ether",
198
+ symbol: "WETH"
199
+ },
200
+ currencySymbol: "eth"
201
+ },
202
+ // Base Sepolia (Testnet)
203
+ 84532: {
204
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
205
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
206
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
207
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
208
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
209
+ wrappedNativeCurrency: {
210
+ address: "0x4200000000000000000000000000000000000006",
211
+ name: "Wrapped Ether",
212
+ symbol: "WETH"
213
+ },
214
+ currencySymbol: "eth"
215
+ },
216
+ // Degen
217
+ 666666666: {
218
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
219
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
220
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
221
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
222
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
223
+ wrappedNativeCurrency: {
224
+ address: "0xEb54dACB4C2ccb64F8074eceEa33b5eBb38E5387",
225
+ name: "Wrapped Degen",
226
+ symbol: "WDEGEN"
227
+ },
228
+ currencySymbol: "degen"
229
+ },
230
+ // Ham Chain
231
+ 5112: {
232
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
233
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
234
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
235
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
236
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
237
+ wrappedNativeCurrency: {
238
+ address: "0x4200000000000000000000000000000000000006",
239
+ name: "Wrapped Ether",
240
+ symbol: "WETH"
241
+ },
242
+ highEthAddress: "0x4200000000000000000000000000000000000006",
243
+ currencySymbol: "eth"
244
+ },
245
+ // Ink Chain
246
+ 57073: {
247
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
248
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
249
+ // Custom Seaport address for Ink (no create2 factory)
250
+ seaportAddress: "0xD00C96804e9fF35f10C7D2a92239C351Ff3F94e5",
251
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
252
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
253
+ wrappedNativeCurrency: {
254
+ address: "0x4200000000000000000000000000000000000006",
255
+ name: "Wrapped ETH",
256
+ symbol: "WETH"
257
+ },
258
+ highEthAddress: "0x4200000000000000000000000000000000000006",
259
+ currencySymbol: "eth"
260
+ },
261
+ // Unichain
262
+ 130: {
263
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
264
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
265
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
266
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
267
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
268
+ wrappedNativeCurrency: {
269
+ address: "0x4200000000000000000000000000000000000006",
270
+ name: "Wrapped Ether",
271
+ symbol: "WETH"
272
+ },
273
+ currencySymbol: "eth"
274
+ },
275
+ // HyperEVM (Hyperliquid)
276
+ 999: {
277
+ bazaarAddress: "0x000000058f3ade587388daf827174d0e6fc97595",
278
+ collectionOffersAddress: "0x0000000f9c45efcff0f78d8b54aa6a40092d66dc",
279
+ erc20OffersAddress: "0x0000000e23a89aa06f317306aa1ae231d3503082",
280
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
281
+ feeCollectorAddress: "0x66547ff4f7206e291F7BC157b54C026Fc6660961",
282
+ nftFeeBps: 0,
283
+ // 0% on HyperEVM
284
+ wrappedNativeCurrency: {
285
+ address: "0x5555555555555555555555555555555555555555",
286
+ name: "Wrapped Hype",
287
+ symbol: "WHYPE"
288
+ },
289
+ currencySymbol: "hype"
290
+ },
291
+ // Plasma
292
+ 9745: {
293
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
294
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
295
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
296
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
297
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
298
+ wrappedNativeCurrency: {
299
+ address: "0x6100e367285b01f48d07953803a2d8dca5d19873",
300
+ name: "Wrapped XPL",
301
+ symbol: "WXPL"
302
+ },
303
+ currencySymbol: "xpl"
304
+ },
305
+ // Monad
306
+ 143: {
307
+ bazaarAddress: DEFAULT_BAZAAR_ADDRESS,
308
+ collectionOffersAddress: DEFAULT_COLLECTION_OFFERS_ADDRESS,
309
+ seaportAddress: DEFAULT_SEAPORT_ADDRESS,
310
+ feeCollectorAddress: DEFAULT_FEE_COLLECTOR_ADDRESS,
311
+ nftFeeBps: DEFAULT_NFT_FEE_BPS,
312
+ wrappedNativeCurrency: {
313
+ address: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A",
314
+ name: "Wrapped Monad",
315
+ symbol: "WMONAD"
316
+ },
317
+ currencySymbol: "monad"
318
+ }
319
+ };
320
+ function getBazaarChainConfig(chainId) {
321
+ return BAZAAR_CHAIN_CONFIGS[chainId];
322
+ }
323
+ function isBazaarSupportedOnChain(chainId) {
324
+ return chainId in BAZAAR_CHAIN_CONFIGS;
325
+ }
326
+ function getBazaarAddress(chainId) {
327
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.bazaarAddress ?? DEFAULT_BAZAAR_ADDRESS;
328
+ }
329
+ function getCollectionOffersAddress(chainId) {
330
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.collectionOffersAddress ?? DEFAULT_COLLECTION_OFFERS_ADDRESS;
331
+ }
332
+ function getSeaportAddress(chainId) {
333
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.seaportAddress ?? DEFAULT_SEAPORT_ADDRESS;
334
+ }
335
+ function getWrappedNativeCurrency(chainId) {
336
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.wrappedNativeCurrency;
337
+ }
338
+ function getCurrencySymbol(chainId) {
339
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.currencySymbol ?? "eth";
340
+ }
341
+ function getHighEthAddress(chainId) {
342
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.highEthAddress;
343
+ }
344
+ function getErc20OffersAddress(chainId) {
345
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.erc20OffersAddress;
346
+ }
347
+ function decodeSeaportSubmission(messageData) {
348
+ const [decoded] = decodeAbiParameters(BAZAAR_SUBMISSION_ABI, messageData);
349
+ return {
350
+ parameters: {
351
+ offerer: decoded.parameters.offerer,
352
+ zone: decoded.parameters.zone,
353
+ offer: decoded.parameters.offer.map((item) => ({
354
+ itemType: item.itemType,
355
+ token: item.token,
356
+ identifierOrCriteria: BigInt(item.identifierOrCriteria),
357
+ startAmount: BigInt(item.startAmount),
358
+ endAmount: BigInt(item.endAmount)
359
+ })),
360
+ consideration: decoded.parameters.consideration.map((item) => ({
361
+ itemType: item.itemType,
362
+ token: item.token,
363
+ identifierOrCriteria: BigInt(item.identifierOrCriteria),
364
+ startAmount: BigInt(item.startAmount),
365
+ endAmount: BigInt(item.endAmount),
366
+ recipient: item.recipient
367
+ })),
368
+ orderType: decoded.parameters.orderType,
369
+ startTime: BigInt(decoded.parameters.startTime),
370
+ endTime: BigInt(decoded.parameters.endTime),
371
+ zoneHash: decoded.parameters.zoneHash,
372
+ salt: BigInt(decoded.parameters.salt),
373
+ conduitKey: decoded.parameters.conduitKey,
374
+ totalOriginalConsiderationItems: BigInt(decoded.parameters.totalOriginalConsiderationItems)
375
+ },
376
+ counter: BigInt(decoded.counter),
377
+ signature: decoded.signature
378
+ };
379
+ }
380
+ function getSeaportOrderFromMessageData(messageData) {
381
+ const submission = decodeSeaportSubmission(messageData);
382
+ return {
383
+ parameters: {
384
+ ...submission.parameters,
385
+ // Convert BigInts to strings for Seaport SDK compatibility
386
+ offer: submission.parameters.offer.map((item) => ({
387
+ ...item,
388
+ identifierOrCriteria: item.identifierOrCriteria.toString(),
389
+ startAmount: item.startAmount.toString(),
390
+ endAmount: item.endAmount.toString()
391
+ })),
392
+ consideration: submission.parameters.consideration.map((item) => ({
393
+ ...item,
394
+ identifierOrCriteria: item.identifierOrCriteria.toString(),
395
+ startAmount: item.startAmount.toString(),
396
+ endAmount: item.endAmount.toString()
397
+ })),
398
+ startTime: submission.parameters.startTime.toString(),
399
+ endTime: submission.parameters.endTime.toString(),
400
+ salt: submission.parameters.salt.toString(),
401
+ totalOriginalConsiderationItems: submission.parameters.totalOriginalConsiderationItems.toString(),
402
+ counter: submission.counter.toString()
403
+ },
404
+ signature: submission.signature,
405
+ counter: submission.counter
406
+ };
407
+ }
408
+ function createSeaportInstance(chainId, rpcUrl) {
409
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
410
+ const signer = new ethers.Wallet(
411
+ // Random private key for read-only operations
412
+ "dc63e9af2088e2afd61499411cb6dd718d00a3d9e46e2cb5e33912c781bd77fe",
413
+ provider
414
+ );
415
+ const highEthAddress = getHighEthAddress(chainId);
416
+ const finalSigner = highEthAddress ? {
417
+ ...signer,
418
+ getAddress: () => highEthAddress,
419
+ address: highEthAddress
420
+ } : signer;
421
+ return new Seaport(finalSigner, {
422
+ overrides: { contractAddress: getSeaportAddress(chainId) }
423
+ });
424
+ }
425
+ function computeOrderHash(seaport, orderParameters, counter) {
426
+ return seaport.getOrderHash({
427
+ ...orderParameters,
428
+ counter: counter.toString()
429
+ });
430
+ }
431
+ function getOrderStatusFromInfo(orderParameters, statusInfo) {
432
+ if (statusInfo.isCancelled) {
433
+ return 0 /* CANCELLED */;
434
+ }
435
+ if (statusInfo.totalFilled === statusInfo.totalSize && statusInfo.totalFilled > BigInt(0)) {
436
+ return 3 /* FILLED */;
437
+ }
438
+ const now = BigInt(Math.floor(Date.now() / 1e3));
439
+ if (orderParameters.endTime < now) {
440
+ return 1 /* EXPIRED */;
441
+ }
442
+ return 2 /* OPEN */;
443
+ }
444
+ function getTotalConsiderationAmount(parameters) {
445
+ return parameters.consideration.reduce(
446
+ (acc, item) => acc + item.startAmount,
447
+ BigInt(0)
448
+ );
449
+ }
450
+ function formatPrice(priceWei) {
451
+ return parseFloat(formatEther(priceWei));
452
+ }
453
+ async function bulkFetchOrderStatuses(client, chainId, orderHashes) {
454
+ if (orderHashes.length === 0) {
455
+ return [];
456
+ }
457
+ const seaportAddress = getSeaportAddress(chainId);
458
+ const results = await readContract(client, {
459
+ address: BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS,
460
+ abi: BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI,
461
+ functionName: "getOrderStatuses",
462
+ args: [seaportAddress, orderHashes]
463
+ });
464
+ return results.map((r) => ({
465
+ isValidated: r.isValidated,
466
+ isCancelled: r.isCancelled,
467
+ totalFilled: BigInt(r.totalFilled),
468
+ totalSize: BigInt(r.totalSize)
469
+ }));
470
+ }
471
+ async function bulkFetchNftOwners(client, nftAddress, tokenIds) {
472
+ if (tokenIds.length === 0) {
473
+ return [];
474
+ }
475
+ try {
476
+ const owners = await readContract(client, {
477
+ address: ERC721_OWNER_OF_HELPER_ADDRESS,
478
+ abi: ERC721_OWNER_OF_HELPER_ABI,
479
+ functionName: "getTokenOwners",
480
+ args: [nftAddress, tokenIds.map((id) => BigInt(id))]
481
+ });
482
+ return owners.map(
483
+ (owner) => owner === "0x0000000000000000000000000000000000000000" ? null : owner
484
+ );
485
+ } catch {
486
+ return tokenIds.map(() => null);
487
+ }
488
+ }
489
+ async function bulkFetchErc20Balances(client, tokenAddress, addresses) {
490
+ if (addresses.length === 0) {
491
+ return [];
492
+ }
493
+ try {
494
+ const balances = await readContract(client, {
495
+ address: ERC20_BULK_BALANCE_CHECKER_ADDRESS,
496
+ abi: ERC20_BULK_BALANCE_CHECKER_ABI,
497
+ functionName: "getBalances",
498
+ args: [tokenAddress, addresses]
499
+ });
500
+ return balances.map((b) => BigInt(b));
501
+ } catch {
502
+ return addresses.map(() => BigInt(0));
503
+ }
504
+ }
505
+ function isListingValid(orderStatus, expirationDate, sellerAddress, currentOwner) {
506
+ if (orderStatus !== 2 /* OPEN */) {
507
+ return false;
508
+ }
509
+ const now = Math.floor(Date.now() / 1e3);
510
+ if (expirationDate <= now) {
511
+ return false;
512
+ }
513
+ if (!currentOwner || currentOwner.toLowerCase() !== sellerAddress.toLowerCase()) {
514
+ return false;
515
+ }
516
+ return true;
517
+ }
518
+ function isCollectionOfferValid(orderStatus, expirationDate, priceWei, buyerBalance) {
519
+ if (orderStatus !== 2 /* OPEN */) {
520
+ return false;
521
+ }
522
+ const now = Math.floor(Date.now() / 1e3);
523
+ if (expirationDate <= now) {
524
+ return false;
525
+ }
526
+ if (buyerBalance < priceWei) {
527
+ return false;
528
+ }
529
+ return true;
530
+ }
531
+ function isErc20OfferValid(orderStatus, expirationDate, priceWei, buyerWethBalance) {
532
+ if (orderStatus !== 2 /* OPEN */) {
533
+ return false;
534
+ }
535
+ const now = Math.floor(Date.now() / 1e3);
536
+ if (expirationDate <= now) {
537
+ return false;
538
+ }
539
+ if (buyerWethBalance < priceWei) {
540
+ return false;
541
+ }
542
+ return true;
543
+ }
544
+
545
+ // src/utils/parsing.ts
546
+ function parseListingFromMessage(message, chainId) {
547
+ try {
548
+ const submission = decodeSeaportSubmission(message.data);
549
+ const { parameters } = submission;
550
+ const offerItem = parameters.offer[0];
551
+ if (!offerItem) {
552
+ return null;
553
+ }
554
+ if (offerItem.itemType !== 2 /* ERC721 */ && offerItem.itemType !== 3 /* ERC1155 */) {
555
+ return null;
556
+ }
557
+ const priceWei = getTotalConsiderationAmount(parameters);
558
+ const tokenId = offerItem.identifierOrCriteria.toString();
559
+ return {
560
+ maker: parameters.offerer,
561
+ nftAddress: offerItem.token,
562
+ tokenId,
563
+ priceWei,
564
+ price: formatPrice(priceWei),
565
+ currency: getCurrencySymbol(chainId),
566
+ expirationDate: Number(parameters.endTime),
567
+ orderHash: "",
568
+ // Will be computed later
569
+ orderStatus: 2 /* OPEN */,
570
+ // Will be validated later
571
+ messageData: message.data,
572
+ orderComponents: {
573
+ ...parameters,
574
+ counter: submission.counter
575
+ }
576
+ };
577
+ } catch {
578
+ return null;
579
+ }
580
+ }
581
+ function parseCollectionOfferFromMessage(message, chainId) {
582
+ try {
583
+ const submission = decodeSeaportSubmission(message.data);
584
+ const { parameters } = submission;
585
+ if (parameters.zone.toLowerCase() !== NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS.toLowerCase()) {
586
+ return null;
587
+ }
588
+ const offerItem = parameters.offer[0];
589
+ if (!offerItem || offerItem.itemType !== 1 /* ERC20 */) {
590
+ return null;
591
+ }
592
+ const nftConsideration = parameters.consideration.find(
593
+ (item) => item.itemType === 4 /* ERC721_WITH_CRITERIA */ || item.itemType === 5 /* ERC1155_WITH_CRITERIA */
594
+ );
595
+ if (!nftConsideration) {
596
+ return null;
597
+ }
598
+ if (nftConsideration.startAmount !== BigInt(1)) {
599
+ return null;
600
+ }
601
+ const priceWei = offerItem.startAmount;
602
+ return {
603
+ maker: parameters.offerer,
604
+ nftAddress: nftConsideration.token,
605
+ priceWei,
606
+ price: formatPrice(priceWei),
607
+ currency: getCurrencySymbol(chainId),
608
+ expirationDate: Number(parameters.endTime),
609
+ orderHash: "",
610
+ // Will be computed later
611
+ orderStatus: 2 /* OPEN */,
612
+ // Will be validated later
613
+ messageData: message.data,
614
+ orderComponents: {
615
+ ...parameters,
616
+ counter: submission.counter
617
+ }
618
+ };
619
+ } catch {
620
+ return null;
621
+ }
622
+ }
623
+ function getBestListingPerToken(listings) {
624
+ const tokenMap = /* @__PURE__ */ new Map();
625
+ for (const listing of listings) {
626
+ const key = `${listing.nftAddress.toLowerCase()}-${listing.tokenId}`;
627
+ const existing = tokenMap.get(key);
628
+ if (!existing || listing.priceWei < existing.priceWei) {
629
+ tokenMap.set(key, listing);
630
+ }
631
+ }
632
+ return Array.from(tokenMap.values());
633
+ }
634
+ function sortListingsByPrice(listings) {
635
+ return [...listings].sort((a, b) => {
636
+ const diff = a.priceWei - b.priceWei;
637
+ if (diff < BigInt(0)) return -1;
638
+ if (diff > BigInt(0)) return 1;
639
+ return 0;
640
+ });
641
+ }
642
+ function sortOffersByPrice(offers) {
643
+ return [...offers].sort((a, b) => {
644
+ const diff = b.priceWei - a.priceWei;
645
+ if (diff < BigInt(0)) return -1;
646
+ if (diff > BigInt(0)) return 1;
647
+ return 0;
648
+ });
649
+ }
650
+ function parseErc20OfferFromMessage(message, chainId) {
651
+ try {
652
+ const submission = decodeSeaportSubmission(message.data);
653
+ const { parameters } = submission;
654
+ if (parameters.zone.toLowerCase() !== NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS.toLowerCase()) {
655
+ return null;
656
+ }
657
+ const offerItem = parameters.offer[0];
658
+ if (!offerItem || offerItem.itemType !== 1 /* ERC20 */) {
659
+ return null;
660
+ }
661
+ const erc20Consideration = parameters.consideration.find(
662
+ (item) => item.itemType === 1 /* ERC20 */
663
+ );
664
+ if (!erc20Consideration) {
665
+ return null;
666
+ }
667
+ const tokenAmount = erc20Consideration.startAmount;
668
+ if (tokenAmount === BigInt(0)) {
669
+ return null;
670
+ }
671
+ const priceWei = offerItem.startAmount;
672
+ const pricePerTokenWei = priceWei / tokenAmount;
673
+ return {
674
+ maker: parameters.offerer,
675
+ tokenAddress: erc20Consideration.token,
676
+ tokenAmount,
677
+ priceWei,
678
+ pricePerTokenWei,
679
+ price: formatPrice(priceWei),
680
+ pricePerToken: formatPrice(pricePerTokenWei),
681
+ currency: getCurrencySymbol(chainId),
682
+ expirationDate: Number(parameters.endTime),
683
+ orderHash: "0x",
684
+ // Will be computed later
685
+ orderStatus: 2 /* OPEN */,
686
+ // Will be validated later
687
+ messageData: message.data,
688
+ orderComponents: {
689
+ ...parameters,
690
+ counter: submission.counter
691
+ }
692
+ };
693
+ } catch {
694
+ return null;
695
+ }
696
+ }
697
+ function sortErc20OffersByPricePerToken(offers) {
698
+ return [...offers].sort((a, b) => {
699
+ const diff = b.pricePerTokenWei - a.pricePerTokenWei;
700
+ if (diff < BigInt(0)) return -1;
701
+ if (diff > BigInt(0)) return 1;
702
+ return 0;
703
+ });
704
+ }
705
+
706
+ // src/client/BazaarClient.ts
707
+ var CHAIN_RPC_URLS = {
708
+ 8453: ["https://base-mainnet.public.blastapi.io", "https://mainnet.base.org"],
709
+ 84532: ["https://sepolia.base.org"],
710
+ 666666666: ["https://rpc.degen.tips"],
711
+ 5112: ["https://rpc.ham.fun"],
712
+ 57073: ["https://rpc-qnd.inkonchain.com"],
713
+ 130: ["https://mainnet.unichain.org"],
714
+ 999: ["https://rpc.hyperliquid.xyz/evm"],
715
+ 9745: ["https://rpc.plasma.to"],
716
+ 143: ["https://rpc3.monad.xyz"]
717
+ };
718
+ var BazaarClient = class {
719
+ constructor(params) {
720
+ if (!isBazaarSupportedOnChain(params.chainId)) {
721
+ throw new Error(`Bazaar is not supported on chain ${params.chainId}`);
722
+ }
723
+ this.chainId = params.chainId;
724
+ this.rpcUrl = params.rpcUrl || CHAIN_RPC_URLS[params.chainId]?.[0] || "";
725
+ if (!this.rpcUrl) {
726
+ throw new Error(`No RPC URL available for chain ${params.chainId}`);
727
+ }
728
+ const config = getBazaarChainConfig(params.chainId);
729
+ this.client = createPublicClient({
730
+ chain: defineChain({
731
+ id: params.chainId,
732
+ name: `Chain ${params.chainId}`,
733
+ nativeCurrency: {
734
+ name: config.wrappedNativeCurrency.name.replace("Wrapped ", ""),
735
+ symbol: config.currencySymbol.toUpperCase(),
736
+ decimals: 18
737
+ },
738
+ rpcUrls: {
739
+ default: { http: [this.rpcUrl] }
740
+ }
741
+ }),
742
+ transport: http(this.rpcUrl),
743
+ batch: { multicall: true }
744
+ });
745
+ this.netClient = new NetClient({
746
+ chainId: params.chainId,
747
+ overrides: params.rpcUrl ? { rpcUrls: [params.rpcUrl] } : void 0
748
+ });
749
+ }
750
+ /**
751
+ * Get valid NFT listings for a collection
752
+ *
753
+ * Returns listings that are:
754
+ * - OPEN status (not filled, cancelled, or expired)
755
+ * - Not expired
756
+ * - Seller still owns the NFT
757
+ *
758
+ * Results are deduplicated (one per token) and sorted by price (lowest first)
759
+ */
760
+ async getListings(options) {
761
+ const { nftAddress, excludeMaker, maxMessages = 200 } = options;
762
+ const bazaarAddress = getBazaarAddress(this.chainId);
763
+ const count = await this.netClient.getMessageCount({
764
+ filter: {
765
+ appAddress: bazaarAddress,
766
+ topic: nftAddress.toLowerCase()
767
+ }
768
+ });
769
+ if (count === 0) {
770
+ return [];
771
+ }
772
+ const startIndex = Math.max(0, count - maxMessages);
773
+ const messages = await this.netClient.getMessages({
774
+ filter: {
775
+ appAddress: bazaarAddress,
776
+ topic: nftAddress.toLowerCase()
777
+ },
778
+ startIndex,
779
+ endIndex: count
780
+ });
781
+ let listings = [];
782
+ for (const message of messages) {
783
+ const listing = parseListingFromMessage(message, this.chainId);
784
+ if (!listing) continue;
785
+ if (excludeMaker && listing.maker.toLowerCase() === excludeMaker.toLowerCase()) {
786
+ continue;
787
+ }
788
+ listings.push(listing);
789
+ }
790
+ if (listings.length === 0) {
791
+ return [];
792
+ }
793
+ const seaport = createSeaportInstance(this.chainId, this.rpcUrl);
794
+ for (const listing of listings) {
795
+ const order = getSeaportOrderFromMessageData(listing.messageData);
796
+ listing.orderHash = computeOrderHash(seaport, order.parameters, order.counter);
797
+ }
798
+ const orderHashes = listings.map((l) => l.orderHash);
799
+ const statusInfos = await bulkFetchOrderStatuses(this.client, this.chainId, orderHashes);
800
+ listings.forEach((listing, index) => {
801
+ const statusInfo = statusInfos[index];
802
+ listing.orderStatus = getOrderStatusFromInfo(listing.orderComponents, statusInfo);
803
+ });
804
+ listings = listings.filter(
805
+ (l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > Math.floor(Date.now() / 1e3)
806
+ );
807
+ if (listings.length === 0) {
808
+ return [];
809
+ }
810
+ const tokenIds = listings.map((l) => l.tokenId);
811
+ const owners = await bulkFetchNftOwners(this.client, nftAddress, tokenIds);
812
+ listings = listings.filter((listing, index) => {
813
+ const owner = owners[index];
814
+ return isListingValid(
815
+ listing.orderStatus,
816
+ listing.expirationDate,
817
+ listing.maker,
818
+ owner
819
+ );
820
+ });
821
+ return sortListingsByPrice(getBestListingPerToken(listings));
822
+ }
823
+ /**
824
+ * Get valid collection offers for a collection
825
+ *
826
+ * Returns offers that are:
827
+ * - OPEN status (not filled, cancelled, or expired)
828
+ * - Not expired
829
+ * - Buyer has sufficient WETH balance
830
+ *
831
+ * Results are sorted by price (highest first)
832
+ */
833
+ async getCollectionOffers(options) {
834
+ const { nftAddress, excludeMaker, maxMessages = 100 } = options;
835
+ const collectionOffersAddress = getCollectionOffersAddress(this.chainId);
836
+ const weth = getWrappedNativeCurrency(this.chainId);
837
+ if (!weth) {
838
+ return [];
839
+ }
840
+ const count = await this.netClient.getMessageCount({
841
+ filter: {
842
+ appAddress: collectionOffersAddress,
843
+ topic: nftAddress.toLowerCase()
844
+ }
845
+ });
846
+ if (count === 0) {
847
+ return [];
848
+ }
849
+ const startIndex = Math.max(0, count - maxMessages);
850
+ const messages = await this.netClient.getMessages({
851
+ filter: {
852
+ appAddress: collectionOffersAddress,
853
+ topic: nftAddress.toLowerCase()
854
+ },
855
+ startIndex,
856
+ endIndex: count
857
+ });
858
+ let offers = [];
859
+ for (const message of messages) {
860
+ const offer = parseCollectionOfferFromMessage(message, this.chainId);
861
+ if (!offer) continue;
862
+ const order = getSeaportOrderFromMessageData(offer.messageData);
863
+ const offerToken = order.parameters.offer?.[0]?.token?.toLowerCase();
864
+ if (offerToken !== weth.address.toLowerCase()) {
865
+ continue;
866
+ }
867
+ if (excludeMaker && offer.maker.toLowerCase() === excludeMaker.toLowerCase()) {
868
+ continue;
869
+ }
870
+ offers.push(offer);
871
+ }
872
+ if (offers.length === 0) {
873
+ return [];
874
+ }
875
+ const seaport = createSeaportInstance(this.chainId, this.rpcUrl);
876
+ for (const offer of offers) {
877
+ const order = getSeaportOrderFromMessageData(offer.messageData);
878
+ offer.orderHash = computeOrderHash(seaport, order.parameters, order.counter);
879
+ }
880
+ const orderHashes = offers.map((o) => o.orderHash);
881
+ const statusInfos = await bulkFetchOrderStatuses(this.client, this.chainId, orderHashes);
882
+ offers.forEach((offer, index) => {
883
+ const statusInfo = statusInfos[index];
884
+ offer.orderStatus = getOrderStatusFromInfo(offer.orderComponents, statusInfo);
885
+ });
886
+ offers = offers.filter(
887
+ (o) => o.orderStatus === 2 /* OPEN */ && o.expirationDate > Math.floor(Date.now() / 1e3)
888
+ );
889
+ if (offers.length === 0) {
890
+ return [];
891
+ }
892
+ const uniqueMakers = [...new Set(offers.map((o) => o.maker))];
893
+ const balances = await bulkFetchErc20Balances(this.client, weth.address, uniqueMakers);
894
+ const balanceMap = /* @__PURE__ */ new Map();
895
+ uniqueMakers.forEach((maker, index) => {
896
+ balanceMap.set(maker.toLowerCase(), balances[index]);
897
+ });
898
+ offers = offers.filter((offer) => {
899
+ const balance = balanceMap.get(offer.maker.toLowerCase()) || BigInt(0);
900
+ return isCollectionOfferValid(
901
+ offer.orderStatus,
902
+ offer.expirationDate,
903
+ offer.priceWei,
904
+ balance
905
+ );
906
+ });
907
+ return sortOffersByPrice(offers);
908
+ }
909
+ /**
910
+ * Get valid ERC20 offers for a token
911
+ *
912
+ * ERC20 offers are only available on Base (8453) and HyperEVM (999).
913
+ *
914
+ * Returns offers that are:
915
+ * - OPEN status (not filled, cancelled, or expired)
916
+ * - Not expired
917
+ * - Buyer has sufficient WETH balance
918
+ *
919
+ * Results are sorted by price per token (highest first)
920
+ */
921
+ async getErc20Offers(options) {
922
+ const { tokenAddress, excludeMaker, maxMessages = 200 } = options;
923
+ const erc20OffersAddress = getErc20OffersAddress(this.chainId);
924
+ const weth = getWrappedNativeCurrency(this.chainId);
925
+ if (!erc20OffersAddress || !weth) {
926
+ return [];
927
+ }
928
+ const count = await this.netClient.getMessageCount({
929
+ filter: {
930
+ appAddress: erc20OffersAddress,
931
+ topic: tokenAddress.toLowerCase()
932
+ }
933
+ });
934
+ if (count === 0) {
935
+ return [];
936
+ }
937
+ const startIndex = Math.max(0, count - maxMessages);
938
+ const messages = await this.netClient.getMessages({
939
+ filter: {
940
+ appAddress: erc20OffersAddress,
941
+ topic: tokenAddress.toLowerCase()
942
+ },
943
+ startIndex,
944
+ endIndex: count
945
+ });
946
+ let offers = [];
947
+ for (const message of messages) {
948
+ const offer = parseErc20OfferFromMessage(message, this.chainId);
949
+ if (!offer) continue;
950
+ const order = getSeaportOrderFromMessageData(offer.messageData);
951
+ const offerToken = order.parameters.offer?.[0]?.token?.toLowerCase();
952
+ if (offerToken !== weth.address.toLowerCase()) {
953
+ continue;
954
+ }
955
+ if (offer.tokenAddress.toLowerCase() !== tokenAddress.toLowerCase()) {
956
+ continue;
957
+ }
958
+ if (excludeMaker && offer.maker.toLowerCase() === excludeMaker.toLowerCase()) {
959
+ continue;
960
+ }
961
+ offers.push(offer);
962
+ }
963
+ if (offers.length === 0) {
964
+ return [];
965
+ }
966
+ const seaport = createSeaportInstance(this.chainId, this.rpcUrl);
967
+ for (const offer of offers) {
968
+ const order = getSeaportOrderFromMessageData(offer.messageData);
969
+ offer.orderHash = computeOrderHash(seaport, order.parameters, order.counter);
970
+ }
971
+ const orderHashes = offers.map((o) => o.orderHash);
972
+ const statusInfos = await bulkFetchOrderStatuses(this.client, this.chainId, orderHashes);
973
+ offers.forEach((offer, index) => {
974
+ const statusInfo = statusInfos[index];
975
+ offer.orderStatus = getOrderStatusFromInfo(offer.orderComponents, statusInfo);
976
+ });
977
+ offers = offers.filter(
978
+ (o) => o.orderStatus === 2 /* OPEN */ && o.expirationDate > Math.floor(Date.now() / 1e3)
979
+ );
980
+ if (offers.length === 0) {
981
+ return [];
982
+ }
983
+ const uniqueMakers = [...new Set(offers.map((o) => o.maker))];
984
+ const balances = await bulkFetchErc20Balances(this.client, weth.address, uniqueMakers);
985
+ const balanceMap = /* @__PURE__ */ new Map();
986
+ uniqueMakers.forEach((maker, index) => {
987
+ balanceMap.set(maker.toLowerCase(), balances[index]);
988
+ });
989
+ offers = offers.filter((offer) => {
990
+ const balance = balanceMap.get(offer.maker.toLowerCase()) || BigInt(0);
991
+ return isErc20OfferValid(
992
+ offer.orderStatus,
993
+ offer.expirationDate,
994
+ offer.priceWei,
995
+ balance
996
+ );
997
+ });
998
+ return sortErc20OffersByPricePerToken(offers);
999
+ }
1000
+ /**
1001
+ * Get the chain ID this client is configured for
1002
+ */
1003
+ getChainId() {
1004
+ return this.chainId;
1005
+ }
1006
+ /**
1007
+ * Get the bazaar contract address for this chain
1008
+ */
1009
+ getBazaarAddress() {
1010
+ return getBazaarAddress(this.chainId);
1011
+ }
1012
+ /**
1013
+ * Get the collection offers contract address for this chain
1014
+ */
1015
+ getCollectionOffersAddress() {
1016
+ return getCollectionOffersAddress(this.chainId);
1017
+ }
1018
+ /**
1019
+ * Get the ERC20 offers contract address for this chain
1020
+ * Only available on Base (8453) and HyperEVM (999)
1021
+ */
1022
+ getErc20OffersAddress() {
1023
+ return getErc20OffersAddress(this.chainId);
1024
+ }
1025
+ /**
1026
+ * Get the Seaport contract address for this chain
1027
+ */
1028
+ getSeaportAddress() {
1029
+ return getSeaportAddress(this.chainId);
1030
+ }
1031
+ /**
1032
+ * Prepare a transaction to cancel a listing
1033
+ *
1034
+ * The listing must have been created by the caller.
1035
+ * Use the orderComponents from the Listing object returned by getListings().
1036
+ */
1037
+ prepareCancelListing(listing) {
1038
+ if (!listing.orderComponents) {
1039
+ throw new Error("Listing does not have order components");
1040
+ }
1041
+ return this.prepareCancelOrder(listing.orderComponents);
1042
+ }
1043
+ /**
1044
+ * Prepare a transaction to cancel a collection offer
1045
+ *
1046
+ * The offer must have been created by the caller.
1047
+ * Use the orderComponents from the CollectionOffer object returned by getCollectionOffers().
1048
+ */
1049
+ prepareCancelCollectionOffer(offer) {
1050
+ if (!offer.orderComponents) {
1051
+ throw new Error("Offer does not have order components");
1052
+ }
1053
+ return this.prepareCancelOrder(offer.orderComponents);
1054
+ }
1055
+ /**
1056
+ * Prepare a transaction to cancel a Seaport order
1057
+ *
1058
+ * This is a low-level method. Prefer prepareCancelListing or prepareCancelCollectionOffer.
1059
+ */
1060
+ prepareCancelOrder(orderComponents) {
1061
+ const seaportAddress = getSeaportAddress(this.chainId);
1062
+ const orderForCancel = {
1063
+ offerer: orderComponents.offerer,
1064
+ zone: orderComponents.zone,
1065
+ offer: orderComponents.offer.map((item) => ({
1066
+ itemType: item.itemType,
1067
+ token: item.token,
1068
+ identifierOrCriteria: item.identifierOrCriteria,
1069
+ startAmount: item.startAmount,
1070
+ endAmount: item.endAmount
1071
+ })),
1072
+ consideration: orderComponents.consideration.map((item) => ({
1073
+ itemType: item.itemType,
1074
+ token: item.token,
1075
+ identifierOrCriteria: item.identifierOrCriteria,
1076
+ startAmount: item.startAmount,
1077
+ endAmount: item.endAmount,
1078
+ recipient: item.recipient
1079
+ })),
1080
+ orderType: orderComponents.orderType,
1081
+ startTime: orderComponents.startTime,
1082
+ endTime: orderComponents.endTime,
1083
+ zoneHash: orderComponents.zoneHash,
1084
+ salt: orderComponents.salt,
1085
+ conduitKey: orderComponents.conduitKey,
1086
+ counter: orderComponents.counter
1087
+ };
1088
+ return {
1089
+ to: seaportAddress,
1090
+ functionName: "cancel",
1091
+ args: [[orderForCancel]],
1092
+ abi: SEAPORT_CANCEL_ABI
1093
+ };
1094
+ }
1095
+ };
1096
+
1097
+ // src/hooks/useBazaarListings.ts
1098
+ function useBazaarListings({
1099
+ chainId,
1100
+ nftAddress,
1101
+ excludeMaker,
1102
+ maxMessages = 200,
1103
+ enabled = true
1104
+ }) {
1105
+ const [listings, setListings] = useState([]);
1106
+ const [isProcessing, setIsProcessing] = useState(false);
1107
+ const [processingError, setProcessingError] = useState();
1108
+ const [refetchTrigger, setRefetchTrigger] = useState(0);
1109
+ const isSupported = useMemo(
1110
+ () => isBazaarSupportedOnChain(chainId),
1111
+ [chainId]
1112
+ );
1113
+ const bazaarAddress = useMemo(
1114
+ () => isSupported ? getBazaarAddress(chainId) : void 0,
1115
+ [chainId, isSupported]
1116
+ );
1117
+ const filter = useMemo(
1118
+ () => ({
1119
+ appAddress: bazaarAddress,
1120
+ topic: nftAddress.toLowerCase()
1121
+ }),
1122
+ [bazaarAddress, nftAddress]
1123
+ );
1124
+ const { count: totalCount, isLoading: isLoadingCount } = useNetMessageCount({
1125
+ chainId,
1126
+ filter,
1127
+ enabled: enabled && isSupported
1128
+ });
1129
+ const startIndex = useMemo(
1130
+ () => Math.max(0, totalCount - maxMessages),
1131
+ [totalCount, maxMessages]
1132
+ );
1133
+ const {
1134
+ messages,
1135
+ isLoading: isLoadingMessages,
1136
+ error: messagesError,
1137
+ refetch: refetchMessages
1138
+ } = useNetMessages({
1139
+ chainId,
1140
+ filter,
1141
+ startIndex,
1142
+ endIndex: totalCount,
1143
+ enabled: enabled && isSupported && totalCount > 0
1144
+ });
1145
+ useEffect(() => {
1146
+ if (!isSupported || !enabled) {
1147
+ setListings([]);
1148
+ return;
1149
+ }
1150
+ if (!messages || messages.length === 0) {
1151
+ setListings([]);
1152
+ return;
1153
+ }
1154
+ let cancelled = false;
1155
+ async function processListings() {
1156
+ setIsProcessing(true);
1157
+ setProcessingError(void 0);
1158
+ try {
1159
+ const client = new BazaarClient({ chainId });
1160
+ const validListings = await client.getListings({
1161
+ nftAddress,
1162
+ excludeMaker,
1163
+ maxMessages
1164
+ });
1165
+ if (!cancelled) {
1166
+ setListings(validListings);
1167
+ }
1168
+ } catch (err) {
1169
+ if (!cancelled) {
1170
+ setProcessingError(err instanceof Error ? err : new Error(String(err)));
1171
+ setListings([]);
1172
+ }
1173
+ } finally {
1174
+ if (!cancelled) {
1175
+ setIsProcessing(false);
1176
+ }
1177
+ }
1178
+ }
1179
+ processListings();
1180
+ return () => {
1181
+ cancelled = true;
1182
+ };
1183
+ }, [chainId, nftAddress, excludeMaker, maxMessages, messages, isSupported, enabled, refetchTrigger]);
1184
+ const refetch = () => {
1185
+ refetchMessages();
1186
+ setRefetchTrigger((t) => t + 1);
1187
+ };
1188
+ return {
1189
+ listings,
1190
+ isLoading: isLoadingCount || isLoadingMessages || isProcessing,
1191
+ error: messagesError || processingError,
1192
+ refetch
1193
+ };
1194
+ }
1195
+ function useBazaarCollectionOffers({
1196
+ chainId,
1197
+ nftAddress,
1198
+ excludeMaker,
1199
+ maxMessages = 100,
1200
+ enabled = true
1201
+ }) {
1202
+ const [offers, setOffers] = useState([]);
1203
+ const [isProcessing, setIsProcessing] = useState(false);
1204
+ const [processingError, setProcessingError] = useState();
1205
+ const [refetchTrigger, setRefetchTrigger] = useState(0);
1206
+ const isSupported = useMemo(
1207
+ () => isBazaarSupportedOnChain(chainId),
1208
+ [chainId]
1209
+ );
1210
+ const collectionOffersAddress = useMemo(
1211
+ () => isSupported ? getCollectionOffersAddress(chainId) : void 0,
1212
+ [chainId, isSupported]
1213
+ );
1214
+ const filter = useMemo(
1215
+ () => ({
1216
+ appAddress: collectionOffersAddress,
1217
+ topic: nftAddress.toLowerCase()
1218
+ }),
1219
+ [collectionOffersAddress, nftAddress]
1220
+ );
1221
+ const { count: totalCount, isLoading: isLoadingCount } = useNetMessageCount({
1222
+ chainId,
1223
+ filter,
1224
+ enabled: enabled && isSupported
1225
+ });
1226
+ const startIndex = useMemo(
1227
+ () => Math.max(0, totalCount - maxMessages),
1228
+ [totalCount, maxMessages]
1229
+ );
1230
+ const {
1231
+ messages,
1232
+ isLoading: isLoadingMessages,
1233
+ error: messagesError,
1234
+ refetch: refetchMessages
1235
+ } = useNetMessages({
1236
+ chainId,
1237
+ filter,
1238
+ startIndex,
1239
+ endIndex: totalCount,
1240
+ enabled: enabled && isSupported && totalCount > 0
1241
+ });
1242
+ useEffect(() => {
1243
+ if (!isSupported || !enabled) {
1244
+ setOffers([]);
1245
+ return;
1246
+ }
1247
+ if (!messages || messages.length === 0) {
1248
+ setOffers([]);
1249
+ return;
1250
+ }
1251
+ let cancelled = false;
1252
+ async function processOffers() {
1253
+ setIsProcessing(true);
1254
+ setProcessingError(void 0);
1255
+ try {
1256
+ const client = new BazaarClient({ chainId });
1257
+ const validOffers = await client.getCollectionOffers({
1258
+ nftAddress,
1259
+ excludeMaker,
1260
+ maxMessages
1261
+ });
1262
+ if (!cancelled) {
1263
+ setOffers(validOffers);
1264
+ }
1265
+ } catch (err) {
1266
+ if (!cancelled) {
1267
+ setProcessingError(err instanceof Error ? err : new Error(String(err)));
1268
+ setOffers([]);
1269
+ }
1270
+ } finally {
1271
+ if (!cancelled) {
1272
+ setIsProcessing(false);
1273
+ }
1274
+ }
1275
+ }
1276
+ processOffers();
1277
+ return () => {
1278
+ cancelled = true;
1279
+ };
1280
+ }, [chainId, nftAddress, excludeMaker, maxMessages, messages, isSupported, enabled, refetchTrigger]);
1281
+ const refetch = () => {
1282
+ refetchMessages();
1283
+ setRefetchTrigger((t) => t + 1);
1284
+ };
1285
+ return {
1286
+ offers,
1287
+ isLoading: isLoadingCount || isLoadingMessages || isProcessing,
1288
+ error: messagesError || processingError,
1289
+ refetch
1290
+ };
1291
+ }
1292
+ function useBazaarErc20Offers({
1293
+ chainId,
1294
+ tokenAddress,
1295
+ excludeMaker,
1296
+ maxMessages = 200,
1297
+ enabled = true
1298
+ }) {
1299
+ const [offers, setOffers] = useState([]);
1300
+ const [isProcessing, setIsProcessing] = useState(false);
1301
+ const [processingError, setProcessingError] = useState();
1302
+ const [refetchTrigger, setRefetchTrigger] = useState(0);
1303
+ const isSupported = useMemo(
1304
+ () => isBazaarSupportedOnChain(chainId),
1305
+ [chainId]
1306
+ );
1307
+ const erc20OffersAddress = useMemo(
1308
+ () => isSupported ? getErc20OffersAddress(chainId) : void 0,
1309
+ [chainId, isSupported]
1310
+ );
1311
+ const hasErc20Offers = Boolean(erc20OffersAddress);
1312
+ const filter = useMemo(
1313
+ () => ({
1314
+ appAddress: erc20OffersAddress,
1315
+ topic: tokenAddress.toLowerCase()
1316
+ }),
1317
+ [erc20OffersAddress, tokenAddress]
1318
+ );
1319
+ const { count: totalCount, isLoading: isLoadingCount } = useNetMessageCount({
1320
+ chainId,
1321
+ filter,
1322
+ enabled: enabled && hasErc20Offers
1323
+ });
1324
+ const startIndex = useMemo(
1325
+ () => Math.max(0, totalCount - maxMessages),
1326
+ [totalCount, maxMessages]
1327
+ );
1328
+ const {
1329
+ messages,
1330
+ isLoading: isLoadingMessages,
1331
+ error: messagesError,
1332
+ refetch: refetchMessages
1333
+ } = useNetMessages({
1334
+ chainId,
1335
+ filter,
1336
+ startIndex,
1337
+ endIndex: totalCount,
1338
+ enabled: enabled && hasErc20Offers && totalCount > 0
1339
+ });
1340
+ useEffect(() => {
1341
+ if (!hasErc20Offers || !enabled) {
1342
+ setOffers([]);
1343
+ return;
1344
+ }
1345
+ if (!messages || messages.length === 0) {
1346
+ setOffers([]);
1347
+ return;
1348
+ }
1349
+ let cancelled = false;
1350
+ async function processOffers() {
1351
+ setIsProcessing(true);
1352
+ setProcessingError(void 0);
1353
+ try {
1354
+ const client = new BazaarClient({ chainId });
1355
+ const validOffers = await client.getErc20Offers({
1356
+ tokenAddress,
1357
+ excludeMaker,
1358
+ maxMessages
1359
+ });
1360
+ if (!cancelled) {
1361
+ setOffers(validOffers);
1362
+ }
1363
+ } catch (err) {
1364
+ if (!cancelled) {
1365
+ setProcessingError(err instanceof Error ? err : new Error(String(err)));
1366
+ setOffers([]);
1367
+ }
1368
+ } finally {
1369
+ if (!cancelled) {
1370
+ setIsProcessing(false);
1371
+ }
1372
+ }
1373
+ }
1374
+ processOffers();
1375
+ return () => {
1376
+ cancelled = true;
1377
+ };
1378
+ }, [chainId, tokenAddress, excludeMaker, maxMessages, messages, hasErc20Offers, enabled, refetchTrigger]);
1379
+ const refetch = () => {
1380
+ refetchMessages();
1381
+ setRefetchTrigger((t) => t + 1);
1382
+ };
1383
+ return {
1384
+ offers,
1385
+ isLoading: isLoadingCount || isLoadingMessages || isProcessing,
1386
+ error: messagesError || processingError,
1387
+ refetch
1388
+ };
1389
+ }
1390
+
1391
+ export { useBazaarCollectionOffers, useBazaarErc20Offers, useBazaarListings };
1392
+ //# sourceMappingURL=react.mjs.map
1393
+ //# sourceMappingURL=react.mjs.map