@net-protocol/bazaar 0.1.5 → 0.1.6

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.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PublicClient } from 'viem';
2
- import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing } from './types-CfGfQTJL.mjs';
2
+ import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing, S as Sale } from './types-BwfAmxpu.mjs';
3
3
 
4
4
  /**
5
5
  * React hook for fetching NFT listings from Bazaar
@@ -255,4 +255,61 @@ interface UseBazaarErc20ListingsResult {
255
255
  */
256
256
  declare function useBazaarErc20Listings({ chainId, tokenAddress, excludeMaker, maker, maxMessages, startIndex: startIndexOverride, endIndex: endIndexOverride, enabled, publicClient, }: UseBazaarErc20ListingsOptions): UseBazaarErc20ListingsResult;
257
257
 
258
- export { type UseBazaarCollectionOffersOptions, type UseBazaarCollectionOffersResult, type UseBazaarErc20ListingsOptions, type UseBazaarErc20ListingsResult, type UseBazaarErc20OffersOptions, type UseBazaarErc20OffersResult, type UseBazaarListingsOptions, type UseBazaarListingsResult, useBazaarCollectionOffers, useBazaarErc20Listings, useBazaarErc20Offers, useBazaarListings };
258
+ /**
259
+ * React hook for fetching recent sales from Bazaar
260
+ */
261
+
262
+ interface UseBazaarSalesOptions {
263
+ /** Chain ID to query */
264
+ chainId: number;
265
+ /** NFT collection address */
266
+ nftAddress: `0x${string}`;
267
+ /** Maximum number of messages to fetch (default: 100) */
268
+ maxMessages?: number;
269
+ /** Whether the query is enabled (default: true) */
270
+ enabled?: boolean;
271
+ }
272
+ interface UseBazaarSalesResult {
273
+ /** Recent sales (sorted by timestamp, most recent first) */
274
+ sales: Sale[];
275
+ /** Whether the data is loading */
276
+ isLoading: boolean;
277
+ /** Error if any */
278
+ error: Error | undefined;
279
+ /** Refetch function */
280
+ refetch: () => void;
281
+ }
282
+ /**
283
+ * React hook for fetching recent sales from Bazaar
284
+ *
285
+ * Sales data flows differently from listings/offers:
286
+ * - Net messages (appAddress=zone, topic=nftAddress) contain order hashes
287
+ * - Actual sale data is fetched from the bulk storage contract
288
+ *
289
+ * Results are sorted by timestamp (most recent first)
290
+ *
291
+ * @example
292
+ * ```tsx
293
+ * const { sales, isLoading, error } = useBazaarSales({
294
+ * chainId: 8453,
295
+ * nftAddress: "0x...",
296
+ * maxMessages: 100,
297
+ * });
298
+ *
299
+ * if (isLoading) return <div>Loading...</div>;
300
+ * if (error) return <div>Error: {error.message}</div>;
301
+ *
302
+ * return (
303
+ * <ul>
304
+ * {sales.map((sale) => (
305
+ * <li key={sale.orderHash}>
306
+ * Token #{sale.tokenId} - {sale.price} {sale.currency}
307
+ * </li>
308
+ * ))}
309
+ * </ul>
310
+ * );
311
+ * ```
312
+ */
313
+ declare function useBazaarSales({ chainId, nftAddress, maxMessages, enabled, }: UseBazaarSalesOptions): UseBazaarSalesResult;
314
+
315
+ export { type UseBazaarCollectionOffersOptions, type UseBazaarCollectionOffersResult, type UseBazaarErc20ListingsOptions, type UseBazaarErc20ListingsResult, type UseBazaarErc20OffersOptions, type UseBazaarErc20OffersResult, type UseBazaarListingsOptions, type UseBazaarListingsResult, type UseBazaarSalesOptions, type UseBazaarSalesResult, useBazaarCollectionOffers, useBazaarErc20Listings, useBazaarErc20Offers, useBazaarListings, useBazaarSales };
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PublicClient } from 'viem';
2
- import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing } from './types-CfGfQTJL.js';
2
+ import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing, S as Sale } from './types-BwfAmxpu.js';
3
3
 
4
4
  /**
5
5
  * React hook for fetching NFT listings from Bazaar
@@ -255,4 +255,61 @@ interface UseBazaarErc20ListingsResult {
255
255
  */
256
256
  declare function useBazaarErc20Listings({ chainId, tokenAddress, excludeMaker, maker, maxMessages, startIndex: startIndexOverride, endIndex: endIndexOverride, enabled, publicClient, }: UseBazaarErc20ListingsOptions): UseBazaarErc20ListingsResult;
257
257
 
258
- export { type UseBazaarCollectionOffersOptions, type UseBazaarCollectionOffersResult, type UseBazaarErc20ListingsOptions, type UseBazaarErc20ListingsResult, type UseBazaarErc20OffersOptions, type UseBazaarErc20OffersResult, type UseBazaarListingsOptions, type UseBazaarListingsResult, useBazaarCollectionOffers, useBazaarErc20Listings, useBazaarErc20Offers, useBazaarListings };
258
+ /**
259
+ * React hook for fetching recent sales from Bazaar
260
+ */
261
+
262
+ interface UseBazaarSalesOptions {
263
+ /** Chain ID to query */
264
+ chainId: number;
265
+ /** NFT collection address */
266
+ nftAddress: `0x${string}`;
267
+ /** Maximum number of messages to fetch (default: 100) */
268
+ maxMessages?: number;
269
+ /** Whether the query is enabled (default: true) */
270
+ enabled?: boolean;
271
+ }
272
+ interface UseBazaarSalesResult {
273
+ /** Recent sales (sorted by timestamp, most recent first) */
274
+ sales: Sale[];
275
+ /** Whether the data is loading */
276
+ isLoading: boolean;
277
+ /** Error if any */
278
+ error: Error | undefined;
279
+ /** Refetch function */
280
+ refetch: () => void;
281
+ }
282
+ /**
283
+ * React hook for fetching recent sales from Bazaar
284
+ *
285
+ * Sales data flows differently from listings/offers:
286
+ * - Net messages (appAddress=zone, topic=nftAddress) contain order hashes
287
+ * - Actual sale data is fetched from the bulk storage contract
288
+ *
289
+ * Results are sorted by timestamp (most recent first)
290
+ *
291
+ * @example
292
+ * ```tsx
293
+ * const { sales, isLoading, error } = useBazaarSales({
294
+ * chainId: 8453,
295
+ * nftAddress: "0x...",
296
+ * maxMessages: 100,
297
+ * });
298
+ *
299
+ * if (isLoading) return <div>Loading...</div>;
300
+ * if (error) return <div>Error: {error.message}</div>;
301
+ *
302
+ * return (
303
+ * <ul>
304
+ * {sales.map((sale) => (
305
+ * <li key={sale.orderHash}>
306
+ * Token #{sale.tokenId} - {sale.price} {sale.currency}
307
+ * </li>
308
+ * ))}
309
+ * </ul>
310
+ * );
311
+ * ```
312
+ */
313
+ declare function useBazaarSales({ chainId, nftAddress, maxMessages, enabled, }: UseBazaarSalesOptions): UseBazaarSalesResult;
314
+
315
+ export { type UseBazaarCollectionOffersOptions, type UseBazaarCollectionOffersResult, type UseBazaarErc20ListingsOptions, type UseBazaarErc20ListingsResult, type UseBazaarErc20OffersOptions, type UseBazaarErc20OffersResult, type UseBazaarListingsOptions, type UseBazaarListingsResult, type UseBazaarSalesOptions, type UseBazaarSalesResult, useBazaarCollectionOffers, useBazaarErc20Listings, useBazaarErc20Offers, useBazaarListings, useBazaarSales };
package/dist/react.js CHANGED
@@ -8,6 +8,7 @@ var core = require('@net-protocol/core');
8
8
  var seaportJs = require('@opensea/seaport-js');
9
9
  var ethers = require('ethers');
10
10
  var actions = require('viem/actions');
11
+ var storage = require('@net-protocol/storage');
11
12
 
12
13
  // src/hooks/useBazaarListings.ts
13
14
 
@@ -185,6 +186,7 @@ var DEFAULT_NFT_FEE_BPS = 500;
185
186
  var BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS = "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
186
187
  var ERC721_OWNER_OF_HELPER_ADDRESS = "0x00000012E3eb0700925947fAF9cd1440319b4F37";
187
188
  var ERC20_BULK_BALANCE_CHECKER_ADDRESS = "0x000000B50A9f2923F2DB931391824F6D1278f712";
189
+ var NET_SEAPORT_ZONE_ADDRESS = "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
188
190
  var NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS = "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
189
191
  var NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS = "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
190
192
  var BAZAAR_CHAIN_CONFIGS = {
@@ -581,8 +583,6 @@ function isErc20ListingValid(orderStatus, expirationDate, tokenAmount, sellerTok
581
583
  }
582
584
  return true;
583
585
  }
584
-
585
- // src/utils/parsing.ts
586
586
  function parseListingFromMessage(message, chainId) {
587
587
  try {
588
588
  const submission = decodeSeaportSubmission(message.data);
@@ -796,8 +796,82 @@ function sortErc20ListingsByPricePerToken(listings) {
796
796
  return 0;
797
797
  });
798
798
  }
799
-
800
- // src/client/BazaarClient.ts
799
+ var ZONE_STORED_SALE_ABI = [
800
+ { type: "uint256" },
801
+ // timestamp
802
+ { type: "uint256" },
803
+ // netTotalMessageLength
804
+ { type: "uint256" },
805
+ // netTotalMessageForAppTopicLength
806
+ {
807
+ name: "zoneParameters",
808
+ type: "tuple",
809
+ internalType: "struct ZoneParameters",
810
+ components: [
811
+ { name: "orderHash", type: "bytes32", internalType: "bytes32" },
812
+ { name: "fulfiller", type: "address", internalType: "address" },
813
+ { name: "offerer", type: "address", internalType: "address" },
814
+ {
815
+ name: "offer",
816
+ type: "tuple[]",
817
+ internalType: "struct SpentItem[]",
818
+ components: [
819
+ { name: "itemType", type: "uint8", internalType: "enum ItemType" },
820
+ { name: "token", type: "address", internalType: "address" },
821
+ { name: "identifier", type: "uint256", internalType: "uint256" },
822
+ { name: "amount", type: "uint256", internalType: "uint256" }
823
+ ]
824
+ },
825
+ {
826
+ name: "consideration",
827
+ type: "tuple[]",
828
+ internalType: "struct ReceivedItem[]",
829
+ components: [
830
+ { name: "itemType", type: "uint8", internalType: "enum ItemType" },
831
+ { name: "token", type: "address", internalType: "address" },
832
+ { name: "identifier", type: "uint256", internalType: "uint256" },
833
+ { name: "amount", type: "uint256", internalType: "uint256" },
834
+ { name: "recipient", type: "address", internalType: "address payable" }
835
+ ]
836
+ },
837
+ { name: "extraData", type: "bytes", internalType: "bytes" },
838
+ { name: "orderHashes", type: "bytes32[]", internalType: "bytes32[]" },
839
+ { name: "startTime", type: "uint256", internalType: "uint256" },
840
+ { name: "endTime", type: "uint256", internalType: "uint256" },
841
+ { name: "zoneHash", type: "bytes32", internalType: "bytes32" }
842
+ ]
843
+ }
844
+ ];
845
+ function parseSaleFromStoredData(storedData, chainId) {
846
+ try {
847
+ const cleanedData = "0x" + (storedData.startsWith("0x") ? storedData.slice(2) : storedData);
848
+ const [timestamp, , , zoneParameters] = viem.decodeAbiParameters(
849
+ ZONE_STORED_SALE_ABI,
850
+ cleanedData
851
+ );
852
+ const offerItem = zoneParameters.offer[0];
853
+ if (!offerItem) return null;
854
+ const totalConsideration = zoneParameters.consideration.reduce(
855
+ (acc, item) => acc + item.amount,
856
+ BigInt(0)
857
+ );
858
+ return {
859
+ seller: zoneParameters.offerer,
860
+ buyer: zoneParameters.fulfiller,
861
+ tokenAddress: offerItem.token,
862
+ tokenId: offerItem.identifier.toString(),
863
+ amount: offerItem.amount,
864
+ itemType: offerItem.itemType,
865
+ priceWei: totalConsideration,
866
+ price: parseFloat(viem.formatEther(totalConsideration)),
867
+ currency: getCurrencySymbol(chainId),
868
+ timestamp: Number(timestamp),
869
+ orderHash: zoneParameters.orderHash
870
+ };
871
+ } catch {
872
+ return null;
873
+ }
874
+ }
801
875
  var CHAIN_RPC_URLS = {
802
876
  8453: ["https://base-mainnet.public.blastapi.io", "https://mainnet.base.org"],
803
877
  84532: ["https://sepolia.base.org"],
@@ -1277,6 +1351,72 @@ var BazaarClient = class {
1277
1351
  console.log(tag, `after balance filter: ${listings.length}/${beforeBalance} (${beforeBalance - listings.length} dropped)`);
1278
1352
  return sortErc20ListingsByPricePerToken(listings);
1279
1353
  }
1354
+ /**
1355
+ * Get recent sales for a collection
1356
+ *
1357
+ * Sales are stored differently from listings: Net messages contain order hashes,
1358
+ * and the actual sale data is fetched from the bulk storage contract.
1359
+ *
1360
+ * Results are sorted by timestamp (most recent first)
1361
+ */
1362
+ async getSales(options) {
1363
+ const { nftAddress, maxMessages = 100 } = options;
1364
+ const filter = {
1365
+ appAddress: NET_SEAPORT_ZONE_ADDRESS,
1366
+ topic: nftAddress.toLowerCase()
1367
+ };
1368
+ const count = await this.netClient.getMessageCount({ filter });
1369
+ if (count === 0) {
1370
+ return [];
1371
+ }
1372
+ const startIndex = Math.max(0, count - maxMessages);
1373
+ const messages = await this.netClient.getMessages({
1374
+ filter,
1375
+ startIndex,
1376
+ endIndex: count
1377
+ });
1378
+ return this.processSalesFromMessages(messages, options);
1379
+ }
1380
+ /**
1381
+ * Process pre-fetched messages into sales.
1382
+ *
1383
+ * Each message's data field contains an order hash. The actual sale data
1384
+ * is fetched from the bulk storage contract using these order hashes.
1385
+ */
1386
+ async processSalesFromMessages(messages, options) {
1387
+ const tag = `[BazaarClient.processSales chain=${this.chainId}]`;
1388
+ if (messages.length === 0) {
1389
+ return [];
1390
+ }
1391
+ const orderHashes = messages.map((m) => m.data);
1392
+ console.log(tag, `fetching ${orderHashes.length} sale details from storage...`);
1393
+ const bulkKeys = orderHashes.map((hash) => ({
1394
+ key: hash,
1395
+ operator: NET_SEAPORT_ZONE_ADDRESS
1396
+ }));
1397
+ let storedResults;
1398
+ try {
1399
+ storedResults = await this.client.readContract({
1400
+ abi: storage.STORAGE_CONTRACT.abi,
1401
+ address: storage.STORAGE_CONTRACT.address,
1402
+ functionName: "bulkGet",
1403
+ args: [bulkKeys]
1404
+ });
1405
+ } catch (err) {
1406
+ console.error(tag, "bulk storage fetch failed:", err);
1407
+ return [];
1408
+ }
1409
+ const sales = [];
1410
+ for (const result of storedResults) {
1411
+ if (!result.value || result.value === "0x") continue;
1412
+ const sale = parseSaleFromStoredData(result.value, this.chainId);
1413
+ if (sale) {
1414
+ sales.push(sale);
1415
+ }
1416
+ }
1417
+ console.log(tag, `parsed ${sales.length}/${storedResults.length} sales`);
1418
+ return sales;
1419
+ }
1280
1420
  /**
1281
1421
  * Get the chain ID this client is configured for
1282
1422
  */
@@ -1863,10 +2003,120 @@ function useBazaarErc20Listings({
1863
2003
  refetch
1864
2004
  };
1865
2005
  }
2006
+ function useBazaarSales({
2007
+ chainId,
2008
+ nftAddress,
2009
+ maxMessages = 100,
2010
+ enabled = true
2011
+ }) {
2012
+ const wagmiClient = wagmi.usePublicClient({ chainId });
2013
+ const [sales, setSales] = react.useState([]);
2014
+ const [isProcessing, setIsProcessing] = react.useState(false);
2015
+ const [processingError, setProcessingError] = react.useState();
2016
+ const [refetchTrigger, setRefetchTrigger] = react.useState(0);
2017
+ const isSupported = react.useMemo(
2018
+ () => isBazaarSupportedOnChain(chainId),
2019
+ [chainId]
2020
+ );
2021
+ const filter = react.useMemo(
2022
+ () => ({
2023
+ appAddress: NET_SEAPORT_ZONE_ADDRESS,
2024
+ topic: nftAddress.toLowerCase()
2025
+ }),
2026
+ [nftAddress]
2027
+ );
2028
+ const { count: totalCount, isLoading: isLoadingCount } = react$1.useNetMessageCount({
2029
+ chainId,
2030
+ filter,
2031
+ enabled: enabled && isSupported
2032
+ });
2033
+ const startIndex = react.useMemo(
2034
+ () => Math.max(0, totalCount - maxMessages),
2035
+ [totalCount, maxMessages]
2036
+ );
2037
+ const {
2038
+ messages,
2039
+ isLoading: isLoadingMessages,
2040
+ error: messagesError,
2041
+ refetch: refetchMessages
2042
+ } = react$1.useNetMessages({
2043
+ chainId,
2044
+ filter,
2045
+ startIndex,
2046
+ endIndex: totalCount,
2047
+ enabled: enabled && isSupported && totalCount > 0
2048
+ });
2049
+ const TAG = `[useBazaarSales chain=${chainId} nft=${nftAddress.slice(0, 10)}]`;
2050
+ react.useEffect(() => {
2051
+ console.log(TAG, {
2052
+ enabled,
2053
+ isSupported,
2054
+ totalCount,
2055
+ isLoadingCount,
2056
+ startIndex,
2057
+ endIndex: totalCount,
2058
+ messagesLength: messages?.length ?? 0,
2059
+ isLoadingMessages,
2060
+ messagesError: messagesError?.message
2061
+ });
2062
+ }, [enabled, isSupported, totalCount, isLoadingCount, startIndex, messages?.length, isLoadingMessages, messagesError]);
2063
+ react.useEffect(() => {
2064
+ if (!isSupported || !enabled) {
2065
+ setSales([]);
2066
+ return;
2067
+ }
2068
+ if (!messages || messages.length === 0) {
2069
+ setSales([]);
2070
+ return;
2071
+ }
2072
+ let cancelled = false;
2073
+ async function processSales() {
2074
+ setIsProcessing(true);
2075
+ setProcessingError(void 0);
2076
+ console.log(TAG, `processing ${messages.length} messages...`);
2077
+ try {
2078
+ const client = new BazaarClient({ chainId, publicClient: wagmiClient });
2079
+ const parsedSales = await client.processSalesFromMessages(
2080
+ messages,
2081
+ { nftAddress }
2082
+ );
2083
+ console.log(TAG, `processed \u2192 ${parsedSales.length} sales`);
2084
+ if (!cancelled) {
2085
+ setSales(parsedSales);
2086
+ }
2087
+ } catch (err) {
2088
+ console.error(TAG, "processing error:", err);
2089
+ if (!cancelled) {
2090
+ setProcessingError(err instanceof Error ? err : new Error(String(err)));
2091
+ setSales([]);
2092
+ }
2093
+ } finally {
2094
+ if (!cancelled) {
2095
+ setIsProcessing(false);
2096
+ }
2097
+ }
2098
+ }
2099
+ processSales();
2100
+ return () => {
2101
+ cancelled = true;
2102
+ };
2103
+ }, [chainId, nftAddress, messages, isSupported, enabled, refetchTrigger]);
2104
+ const refetch = () => {
2105
+ refetchMessages();
2106
+ setRefetchTrigger((t) => t + 1);
2107
+ };
2108
+ return {
2109
+ sales,
2110
+ isLoading: isLoadingCount || isLoadingMessages || isProcessing,
2111
+ error: messagesError || processingError,
2112
+ refetch
2113
+ };
2114
+ }
1866
2115
 
1867
2116
  exports.useBazaarCollectionOffers = useBazaarCollectionOffers;
1868
2117
  exports.useBazaarErc20Listings = useBazaarErc20Listings;
1869
2118
  exports.useBazaarErc20Offers = useBazaarErc20Offers;
1870
2119
  exports.useBazaarListings = useBazaarListings;
2120
+ exports.useBazaarSales = useBazaarSales;
1871
2121
  //# sourceMappingURL=react.js.map
1872
2122
  //# sourceMappingURL=react.js.map