@net-protocol/bazaar 0.1.2 → 0.1.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.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @net-protocol/bazaar
2
2
 
3
- **Status: Alpha** - Usable but may have breaking changes over time. Suitable for early adopters and testing.
3
+ **Status: In Development** - This package is under active development. APIs may change without notice.
4
4
 
5
- NFT marketplace SDK for Net Protocol - read listings, collection offers, and ERC20 offers via Seaport.
5
+ SDK for Net Bazaar - a decentralized bazaar storing all orders onchain via Seaport.
6
6
 
7
7
  ## What is Net Bazaar?
8
8
 
9
- Net Bazaar is a decentralized NFT marketplace built on Net Protocol. It uses Seaport for order execution and Net Protocol for order storage and indexing.
9
+ Net Bazaar is a decentralized bazaar built on Net Protocol. All orders are stored onchain. It uses Seaport for order execution and Net Protocol for order storage and indexing.
10
10
 
11
11
  **Key features:**
12
12
  - **NFT Listings**: Buy and sell NFTs with native currency
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, c as GetErc20ListingsOptions, d as Erc20Listing, W as WriteTransactionConfig, S as SeaportOrderComponents, e as SeaportSubmission, f as SeaportOrderParameters, g as SeaportOrderStatusInfo, h as SeaportOrderStatus } from './types-KQgBECzI.mjs';
2
- export { j as ConsiderationItem, l as CreateCollectionOfferParams, k as CreateListingParams, I as ItemType, i as OfferItem, O as OrderType } from './types-KQgBECzI.mjs';
3
- import { Seaport } from '@opensea/seaport-js';
4
1
  import { PublicClient } from 'viem';
5
2
  import { NetMessage } from '@net-protocol/core';
3
+ import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, c as GetErc20ListingsOptions, d as Erc20Listing, W as WriteTransactionConfig, S as SeaportOrderComponents, e as SeaportSubmission, f as SeaportOrderParameters, g as SeaportOrderStatusInfo, h as SeaportOrderStatus } from './types-DDHh9yJB.mjs';
4
+ export { j as ConsiderationItem, l as CreateCollectionOfferParams, k as CreateListingParams, I as ItemType, i as OfferItem, O as OrderType } from './types-DDHh9yJB.mjs';
5
+ import { Seaport } from '@opensea/seaport-js';
6
6
 
7
7
  /**
8
- * BazaarClient - Client for interacting with Net Bazaar (NFT marketplace)
8
+ * BazaarClient - Client for interacting with Net Bazaar
9
9
  *
10
10
  * Provides methods for:
11
11
  * - Reading NFT listings and collection offers
@@ -20,6 +20,7 @@ declare class BazaarClient {
20
20
  constructor(params: {
21
21
  chainId: number;
22
22
  rpcUrl?: string;
23
+ publicClient?: PublicClient;
23
24
  });
24
25
  /**
25
26
  * Get valid NFT listings for a collection
@@ -32,6 +33,13 @@ declare class BazaarClient {
32
33
  * Results are deduplicated (one per token) and sorted by price (lowest first)
33
34
  */
34
35
  getListings(options: GetListingsOptions): Promise<Listing[]>;
36
+ /**
37
+ * Process pre-fetched messages into valid NFT listings.
38
+ *
39
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
40
+ * to avoid redundant RPC calls.
41
+ */
42
+ processListingsFromMessages(messages: NetMessage[], options: Pick<GetListingsOptions, "nftAddress" | "excludeMaker" | "includeExpired">): Promise<Listing[]>;
35
43
  /**
36
44
  * Get valid collection offers for a collection
37
45
  *
@@ -43,6 +51,13 @@ declare class BazaarClient {
43
51
  * Results are sorted by price (highest first)
44
52
  */
45
53
  getCollectionOffers(options: GetCollectionOffersOptions): Promise<CollectionOffer[]>;
54
+ /**
55
+ * Process pre-fetched messages into valid collection offers.
56
+ *
57
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
58
+ * to avoid redundant RPC calls.
59
+ */
60
+ processCollectionOffersFromMessages(messages: NetMessage[], options: Pick<GetCollectionOffersOptions, "nftAddress" | "excludeMaker">): Promise<CollectionOffer[]>;
46
61
  /**
47
62
  * Get valid ERC20 offers for a token
48
63
  *
@@ -56,6 +71,13 @@ declare class BazaarClient {
56
71
  * Results are sorted by price per token (highest first)
57
72
  */
58
73
  getErc20Offers(options: GetErc20OffersOptions): Promise<Erc20Offer[]>;
74
+ /**
75
+ * Process pre-fetched messages into valid ERC20 offers.
76
+ *
77
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
78
+ * to avoid redundant RPC calls.
79
+ */
80
+ processErc20OffersFromMessages(messages: NetMessage[], options: Pick<GetErc20OffersOptions, "tokenAddress" | "excludeMaker">): Promise<Erc20Offer[]>;
59
81
  /**
60
82
  * Get valid ERC20 listings for a token
61
83
  *
@@ -68,6 +90,13 @@ declare class BazaarClient {
68
90
  * all valid listings are returned (grouped by maker in the UI).
69
91
  */
70
92
  getErc20Listings(options: GetErc20ListingsOptions): Promise<Erc20Listing[]>;
93
+ /**
94
+ * Process pre-fetched messages into valid ERC20 listings.
95
+ *
96
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
97
+ * to avoid redundant RPC calls.
98
+ */
99
+ processErc20ListingsFromMessages(messages: NetMessage[], options: Pick<GetErc20ListingsOptions, "tokenAddress" | "excludeMaker">): Promise<Erc20Listing[]>;
71
100
  /**
72
101
  * Get the chain ID this client is configured for
73
102
  */
@@ -155,8 +184,8 @@ interface BazaarChainConfig {
155
184
  currencySymbol: string;
156
185
  }
157
186
  declare const BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS: "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
158
- declare const ERC721_OWNER_OF_HELPER_ADDRESS: "0x000000aa4eFa2e5A4a6002C7F08B6e8Ec8cf1dDa";
159
- declare const ERC20_BULK_BALANCE_CHECKER_ADDRESS: "0x000000b50a9f2923f2db931391824f6d1278f712";
187
+ declare const ERC721_OWNER_OF_HELPER_ADDRESS: "0x00000012E3eb0700925947fAF9cd1440319b4F37";
188
+ declare const ERC20_BULK_BALANCE_CHECKER_ADDRESS: "0x000000B50A9f2923F2DB931391824F6D1278f712";
160
189
  declare const NET_SEAPORT_ZONE_ADDRESS: "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
161
190
  declare const NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS: "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
162
191
  declare const NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS: "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, c as GetErc20ListingsOptions, d as Erc20Listing, W as WriteTransactionConfig, S as SeaportOrderComponents, e as SeaportSubmission, f as SeaportOrderParameters, g as SeaportOrderStatusInfo, h as SeaportOrderStatus } from './types-KQgBECzI.js';
2
- export { j as ConsiderationItem, l as CreateCollectionOfferParams, k as CreateListingParams, I as ItemType, i as OfferItem, O as OrderType } from './types-KQgBECzI.js';
3
- import { Seaport } from '@opensea/seaport-js';
4
1
  import { PublicClient } from 'viem';
5
2
  import { NetMessage } from '@net-protocol/core';
3
+ import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, c as GetErc20ListingsOptions, d as Erc20Listing, W as WriteTransactionConfig, S as SeaportOrderComponents, e as SeaportSubmission, f as SeaportOrderParameters, g as SeaportOrderStatusInfo, h as SeaportOrderStatus } from './types-DDHh9yJB.js';
4
+ export { j as ConsiderationItem, l as CreateCollectionOfferParams, k as CreateListingParams, I as ItemType, i as OfferItem, O as OrderType } from './types-DDHh9yJB.js';
5
+ import { Seaport } from '@opensea/seaport-js';
6
6
 
7
7
  /**
8
- * BazaarClient - Client for interacting with Net Bazaar (NFT marketplace)
8
+ * BazaarClient - Client for interacting with Net Bazaar
9
9
  *
10
10
  * Provides methods for:
11
11
  * - Reading NFT listings and collection offers
@@ -20,6 +20,7 @@ declare class BazaarClient {
20
20
  constructor(params: {
21
21
  chainId: number;
22
22
  rpcUrl?: string;
23
+ publicClient?: PublicClient;
23
24
  });
24
25
  /**
25
26
  * Get valid NFT listings for a collection
@@ -32,6 +33,13 @@ declare class BazaarClient {
32
33
  * Results are deduplicated (one per token) and sorted by price (lowest first)
33
34
  */
34
35
  getListings(options: GetListingsOptions): Promise<Listing[]>;
36
+ /**
37
+ * Process pre-fetched messages into valid NFT listings.
38
+ *
39
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
40
+ * to avoid redundant RPC calls.
41
+ */
42
+ processListingsFromMessages(messages: NetMessage[], options: Pick<GetListingsOptions, "nftAddress" | "excludeMaker" | "includeExpired">): Promise<Listing[]>;
35
43
  /**
36
44
  * Get valid collection offers for a collection
37
45
  *
@@ -43,6 +51,13 @@ declare class BazaarClient {
43
51
  * Results are sorted by price (highest first)
44
52
  */
45
53
  getCollectionOffers(options: GetCollectionOffersOptions): Promise<CollectionOffer[]>;
54
+ /**
55
+ * Process pre-fetched messages into valid collection offers.
56
+ *
57
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
58
+ * to avoid redundant RPC calls.
59
+ */
60
+ processCollectionOffersFromMessages(messages: NetMessage[], options: Pick<GetCollectionOffersOptions, "nftAddress" | "excludeMaker">): Promise<CollectionOffer[]>;
46
61
  /**
47
62
  * Get valid ERC20 offers for a token
48
63
  *
@@ -56,6 +71,13 @@ declare class BazaarClient {
56
71
  * Results are sorted by price per token (highest first)
57
72
  */
58
73
  getErc20Offers(options: GetErc20OffersOptions): Promise<Erc20Offer[]>;
74
+ /**
75
+ * Process pre-fetched messages into valid ERC20 offers.
76
+ *
77
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
78
+ * to avoid redundant RPC calls.
79
+ */
80
+ processErc20OffersFromMessages(messages: NetMessage[], options: Pick<GetErc20OffersOptions, "tokenAddress" | "excludeMaker">): Promise<Erc20Offer[]>;
59
81
  /**
60
82
  * Get valid ERC20 listings for a token
61
83
  *
@@ -68,6 +90,13 @@ declare class BazaarClient {
68
90
  * all valid listings are returned (grouped by maker in the UI).
69
91
  */
70
92
  getErc20Listings(options: GetErc20ListingsOptions): Promise<Erc20Listing[]>;
93
+ /**
94
+ * Process pre-fetched messages into valid ERC20 listings.
95
+ *
96
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
97
+ * to avoid redundant RPC calls.
98
+ */
99
+ processErc20ListingsFromMessages(messages: NetMessage[], options: Pick<GetErc20ListingsOptions, "tokenAddress" | "excludeMaker">): Promise<Erc20Listing[]>;
71
100
  /**
72
101
  * Get the chain ID this client is configured for
73
102
  */
@@ -155,8 +184,8 @@ interface BazaarChainConfig {
155
184
  currencySymbol: string;
156
185
  }
157
186
  declare const BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS: "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
158
- declare const ERC721_OWNER_OF_HELPER_ADDRESS: "0x000000aa4eFa2e5A4a6002C7F08B6e8Ec8cf1dDa";
159
- declare const ERC20_BULK_BALANCE_CHECKER_ADDRESS: "0x000000b50a9f2923f2db931391824f6d1278f712";
187
+ declare const ERC721_OWNER_OF_HELPER_ADDRESS: "0x00000012E3eb0700925947fAF9cd1440319b4F37";
188
+ declare const ERC20_BULK_BALANCE_CHECKER_ADDRESS: "0x000000B50A9f2923F2DB931391824F6D1278f712";
160
189
  declare const NET_SEAPORT_ZONE_ADDRESS: "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
161
190
  declare const NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS: "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
162
191
  declare const NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS: "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
package/dist/index.js CHANGED
@@ -359,8 +359,8 @@ var DEFAULT_FEE_COLLECTOR_ADDRESS = "0x32D16C15410248bef498D7aF50D10Db1a546b9E5"
359
359
  var DEFAULT_ERC20_BAZAAR_ADDRESS = "0x00000000a2d173a4610c85c7471a25b6bc216a70";
360
360
  var DEFAULT_NFT_FEE_BPS = 500;
361
361
  var BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS = "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
362
- var ERC721_OWNER_OF_HELPER_ADDRESS = "0x000000aa4eFa2e5A4a6002C7F08B6e8Ec8cf1dDa";
363
- var ERC20_BULK_BALANCE_CHECKER_ADDRESS = "0x000000b50a9f2923f2db931391824f6d1278f712";
362
+ var ERC721_OWNER_OF_HELPER_ADDRESS = "0x00000012E3eb0700925947fAF9cd1440319b4F37";
363
+ var ERC20_BULK_BALANCE_CHECKER_ADDRESS = "0x000000B50A9f2923F2DB931391824F6D1278f712";
364
364
  var NET_SEAPORT_ZONE_ADDRESS = "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
365
365
  var NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS = "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
366
366
  var NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS = "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
@@ -651,18 +651,26 @@ async function bulkFetchOrderStatuses(client, chainId, orderHashes) {
651
651
  return [];
652
652
  }
653
653
  const seaportAddress = getSeaportAddress(chainId);
654
- const results = await actions.readContract(client, {
655
- address: BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS,
656
- abi: BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI,
657
- functionName: "getOrderStatuses",
658
- args: [seaportAddress, orderHashes]
659
- });
660
- return results.map((r) => ({
661
- isValidated: r.isValidated,
662
- isCancelled: r.isCancelled,
663
- totalFilled: BigInt(r.totalFilled),
664
- totalSize: BigInt(r.totalSize)
665
- }));
654
+ try {
655
+ console.log(`[bulkFetchOrderStatuses] fetching ${orderHashes.length} statuses via ${BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS}`);
656
+ const results = await actions.readContract(client, {
657
+ address: BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS,
658
+ abi: BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI,
659
+ functionName: "getOrderStatuses",
660
+ args: [seaportAddress, orderHashes]
661
+ });
662
+ const statuses = results.map((r) => ({
663
+ isValidated: r.isValidated,
664
+ isCancelled: r.isCancelled,
665
+ totalFilled: BigInt(r.totalFilled),
666
+ totalSize: BigInt(r.totalSize)
667
+ }));
668
+ console.log(`[bulkFetchOrderStatuses] success: ${statuses.length} statuses`);
669
+ return statuses;
670
+ } catch (err) {
671
+ console.error(`[bulkFetchOrderStatuses] FAILED for ${orderHashes.length} hashes:`, err);
672
+ throw err;
673
+ }
666
674
  }
667
675
  async function createOrderStatusMap(client, chainId, orderHashes) {
668
676
  const statuses = await bulkFetchOrderStatuses(client, chainId, orderHashes);
@@ -677,16 +685,21 @@ async function bulkFetchNftOwners(client, nftAddress, tokenIds) {
677
685
  return [];
678
686
  }
679
687
  try {
688
+ console.log(`[bulkFetchNftOwners] fetching ${tokenIds.length} owners for ${nftAddress.slice(0, 10)} via ${ERC721_OWNER_OF_HELPER_ADDRESS}`);
680
689
  const owners = await actions.readContract(client, {
681
690
  address: ERC721_OWNER_OF_HELPER_ADDRESS,
682
691
  abi: ERC721_OWNER_OF_HELPER_ABI,
683
692
  functionName: "getTokenOwners",
684
693
  args: [nftAddress, tokenIds.map((id) => BigInt(id))]
685
694
  });
686
- return owners.map(
695
+ const result = owners.map(
687
696
  (owner) => owner === "0x0000000000000000000000000000000000000000" ? null : owner
688
697
  );
689
- } catch {
698
+ const validCount = result.filter((o) => o !== null).length;
699
+ console.log(`[bulkFetchNftOwners] success: ${validCount}/${tokenIds.length} have owners`);
700
+ return result;
701
+ } catch (err) {
702
+ console.error(`[bulkFetchNftOwners] FAILED for ${tokenIds.length} tokens \u2014 returning all null:`, err);
690
703
  return tokenIds.map(() => null);
691
704
  }
692
705
  }
@@ -703,14 +716,18 @@ async function bulkFetchErc20Balances(client, tokenAddress, addresses) {
703
716
  return [];
704
717
  }
705
718
  try {
719
+ console.log(`[bulkFetchErc20Balances] fetching ${addresses.length} balances for ${tokenAddress.slice(0, 10)}`);
706
720
  const balances = await actions.readContract(client, {
707
721
  address: ERC20_BULK_BALANCE_CHECKER_ADDRESS,
708
722
  abi: ERC20_BULK_BALANCE_CHECKER_ABI,
709
723
  functionName: "getBalances",
710
724
  args: [tokenAddress, addresses]
711
725
  });
712
- return balances.map((b) => BigInt(b));
713
- } catch {
726
+ const result = balances.map((b) => BigInt(b));
727
+ console.log(`[bulkFetchErc20Balances] success: ${result.length} balances`);
728
+ return result;
729
+ } catch (err) {
730
+ console.error(`[bulkFetchErc20Balances] FAILED for ${addresses.length} addresses \u2014 returning all zero:`, err);
714
731
  return addresses.map(() => BigInt(0));
715
732
  }
716
733
  }
@@ -1017,26 +1034,30 @@ var BazaarClient = class {
1017
1034
  }
1018
1035
  this.chainId = params.chainId;
1019
1036
  this.rpcUrl = params.rpcUrl || CHAIN_RPC_URLS[params.chainId]?.[0] || "";
1020
- if (!this.rpcUrl) {
1021
- throw new Error(`No RPC URL available for chain ${params.chainId}`);
1037
+ if (params.publicClient) {
1038
+ this.client = params.publicClient;
1039
+ } else {
1040
+ if (!this.rpcUrl) {
1041
+ throw new Error(`No RPC URL available for chain ${params.chainId}`);
1042
+ }
1043
+ const config = getBazaarChainConfig(params.chainId);
1044
+ this.client = viem.createPublicClient({
1045
+ chain: viem.defineChain({
1046
+ id: params.chainId,
1047
+ name: `Chain ${params.chainId}`,
1048
+ nativeCurrency: {
1049
+ name: config.wrappedNativeCurrency.name.replace("Wrapped ", ""),
1050
+ symbol: config.currencySymbol.toUpperCase(),
1051
+ decimals: 18
1052
+ },
1053
+ rpcUrls: {
1054
+ default: { http: [this.rpcUrl] }
1055
+ }
1056
+ }),
1057
+ transport: viem.http(this.rpcUrl),
1058
+ batch: { multicall: true }
1059
+ });
1022
1060
  }
1023
- const config = getBazaarChainConfig(params.chainId);
1024
- this.client = viem.createPublicClient({
1025
- chain: viem.defineChain({
1026
- id: params.chainId,
1027
- name: `Chain ${params.chainId}`,
1028
- nativeCurrency: {
1029
- name: config.wrappedNativeCurrency.name.replace("Wrapped ", ""),
1030
- symbol: config.currencySymbol.toUpperCase(),
1031
- decimals: 18
1032
- },
1033
- rpcUrls: {
1034
- default: { http: [this.rpcUrl] }
1035
- }
1036
- }),
1037
- transport: viem.http(this.rpcUrl),
1038
- batch: { multicall: true }
1039
- });
1040
1061
  this.netClient = new core.NetClient({
1041
1062
  chainId: params.chainId,
1042
1063
  overrides: params.rpcUrl ? { rpcUrls: [params.rpcUrl] } : void 0
@@ -1078,6 +1099,17 @@ var BazaarClient = class {
1078
1099
  startIndex,
1079
1100
  endIndex
1080
1101
  });
1102
+ return this.processListingsFromMessages(messages, options);
1103
+ }
1104
+ /**
1105
+ * Process pre-fetched messages into valid NFT listings.
1106
+ *
1107
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
1108
+ * to avoid redundant RPC calls.
1109
+ */
1110
+ async processListingsFromMessages(messages, options) {
1111
+ const { nftAddress, excludeMaker, includeExpired = false } = options;
1112
+ const tag = `[BazaarClient.processListings chain=${this.chainId}]`;
1081
1113
  let listings = [];
1082
1114
  for (const message of messages) {
1083
1115
  const listing = parseListingFromMessage(message, this.chainId);
@@ -1087,6 +1119,7 @@ var BazaarClient = class {
1087
1119
  }
1088
1120
  listings.push(listing);
1089
1121
  }
1122
+ console.log(tag, `parsed ${listings.length}/${messages.length} messages into listings`);
1090
1123
  if (listings.length === 0) {
1091
1124
  return [];
1092
1125
  }
@@ -1101,15 +1134,27 @@ var BazaarClient = class {
1101
1134
  const statusInfo = statusInfos[index];
1102
1135
  listing.orderStatus = getOrderStatusFromInfo(listing.orderComponents, statusInfo);
1103
1136
  });
1137
+ const statusCounts = {};
1138
+ const now = Math.floor(Date.now() / 1e3);
1139
+ let expiredCount = 0;
1140
+ for (const l of listings) {
1141
+ statusCounts[l.orderStatus] = (statusCounts[l.orderStatus] || 0) + 1;
1142
+ if (l.expirationDate <= now) expiredCount++;
1143
+ }
1144
+ console.log(tag, `order statuses:`, statusCounts, `expired: ${expiredCount}`);
1104
1145
  listings = listings.filter(
1105
- (l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > Math.floor(Date.now() / 1e3)
1146
+ (l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > now || includeExpired && l.orderStatus === 1 /* EXPIRED */
1106
1147
  );
1148
+ console.log(tag, `after status filter: ${listings.length} (OPEN${includeExpired ? " + EXPIRED" : ""})`);
1107
1149
  if (listings.length === 0) {
1108
1150
  return [];
1109
1151
  }
1110
- const tokenIds = listings.map((l) => l.tokenId);
1152
+ const openListings = listings.filter((l) => l.orderStatus === 2 /* OPEN */);
1153
+ const expiredListings = includeExpired ? listings.filter((l) => l.orderStatus === 1 /* EXPIRED */) : [];
1154
+ const tokenIds = openListings.map((l) => l.tokenId);
1111
1155
  const owners = await bulkFetchNftOwners(this.client, nftAddress, tokenIds);
1112
- listings = listings.filter((listing, index) => {
1156
+ const beforeOwnership = openListings.length;
1157
+ const validOpenListings = openListings.filter((listing, index) => {
1113
1158
  const owner = owners[index];
1114
1159
  return isListingValid(
1115
1160
  listing.orderStatus,
@@ -1118,7 +1163,16 @@ var BazaarClient = class {
1118
1163
  owner
1119
1164
  );
1120
1165
  });
1121
- return sortListingsByPrice(getBestListingPerToken(listings));
1166
+ console.log(tag, `after ownership filter: ${validOpenListings.length}/${beforeOwnership} (${beforeOwnership - validOpenListings.length} dropped)`);
1167
+ const dedupedOpen = sortListingsByPrice(getBestListingPerToken(validOpenListings));
1168
+ const activeTokenKeys = new Set(dedupedOpen.map((l) => `${l.nftAddress.toLowerCase()}-${l.tokenId}`));
1169
+ const uniqueExpired = getBestListingPerToken(
1170
+ expiredListings.filter((l) => !activeTokenKeys.has(`${l.nftAddress.toLowerCase()}-${l.tokenId}`))
1171
+ );
1172
+ const sortedExpired = sortListingsByPrice(uniqueExpired);
1173
+ const result = [...dedupedOpen, ...sortedExpired];
1174
+ console.log(tag, `final: ${result.length} listings (${dedupedOpen.length} open, ${sortedExpired.length} expired)`);
1175
+ return result;
1122
1176
  }
1123
1177
  /**
1124
1178
  * Get valid collection offers for a collection
@@ -1133,10 +1187,6 @@ var BazaarClient = class {
1133
1187
  async getCollectionOffers(options) {
1134
1188
  const { nftAddress, excludeMaker, maxMessages = 100 } = options;
1135
1189
  const collectionOffersAddress = getCollectionOffersAddress(this.chainId);
1136
- const weth = getWrappedNativeCurrency(this.chainId);
1137
- if (!weth) {
1138
- return [];
1139
- }
1140
1190
  const count = await this.netClient.getMessageCount({
1141
1191
  filter: {
1142
1192
  appAddress: collectionOffersAddress,
@@ -1155,6 +1205,21 @@ var BazaarClient = class {
1155
1205
  startIndex,
1156
1206
  endIndex: count
1157
1207
  });
1208
+ return this.processCollectionOffersFromMessages(messages, options);
1209
+ }
1210
+ /**
1211
+ * Process pre-fetched messages into valid collection offers.
1212
+ *
1213
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
1214
+ * to avoid redundant RPC calls.
1215
+ */
1216
+ async processCollectionOffersFromMessages(messages, options) {
1217
+ const { nftAddress, excludeMaker } = options;
1218
+ const tag = `[BazaarClient.processCollectionOffers chain=${this.chainId}]`;
1219
+ const weth = getWrappedNativeCurrency(this.chainId);
1220
+ if (!weth) {
1221
+ return [];
1222
+ }
1158
1223
  let offers = [];
1159
1224
  for (const message of messages) {
1160
1225
  const offer = parseCollectionOfferFromMessage(message, this.chainId);
@@ -1169,6 +1234,7 @@ var BazaarClient = class {
1169
1234
  }
1170
1235
  offers.push(offer);
1171
1236
  }
1237
+ console.log(tag, `parsed ${offers.length}/${messages.length} messages into offers`);
1172
1238
  if (offers.length === 0) {
1173
1239
  return [];
1174
1240
  }
@@ -1183,9 +1249,11 @@ var BazaarClient = class {
1183
1249
  const statusInfo = statusInfos[index];
1184
1250
  offer.orderStatus = getOrderStatusFromInfo(offer.orderComponents, statusInfo);
1185
1251
  });
1252
+ const now = Math.floor(Date.now() / 1e3);
1186
1253
  offers = offers.filter(
1187
- (o) => o.orderStatus === 2 /* OPEN */ && o.expirationDate > Math.floor(Date.now() / 1e3)
1254
+ (o) => o.orderStatus === 2 /* OPEN */ && o.expirationDate > now
1188
1255
  );
1256
+ console.log(tag, `after status filter: ${offers.length} OPEN & not expired`);
1189
1257
  if (offers.length === 0) {
1190
1258
  return [];
1191
1259
  }
@@ -1195,6 +1263,7 @@ var BazaarClient = class {
1195
1263
  uniqueMakers.forEach((maker, index) => {
1196
1264
  balanceMap.set(maker.toLowerCase(), balances[index]);
1197
1265
  });
1266
+ const beforeBalance = offers.length;
1198
1267
  offers = offers.filter((offer) => {
1199
1268
  const balance = balanceMap.get(offer.maker.toLowerCase()) || BigInt(0);
1200
1269
  return isCollectionOfferValid(
@@ -1204,6 +1273,7 @@ var BazaarClient = class {
1204
1273
  balance
1205
1274
  );
1206
1275
  });
1276
+ console.log(tag, `after balance filter: ${offers.length}/${beforeBalance} (${beforeBalance - offers.length} dropped)`);
1207
1277
  return sortOffersByPrice(offers);
1208
1278
  }
1209
1279
  /**
@@ -1221,8 +1291,7 @@ var BazaarClient = class {
1221
1291
  async getErc20Offers(options) {
1222
1292
  const { tokenAddress, excludeMaker, maxMessages = 200 } = options;
1223
1293
  const erc20OffersAddress = getErc20OffersAddress(this.chainId);
1224
- const weth = getWrappedNativeCurrency(this.chainId);
1225
- if (!erc20OffersAddress || !weth) {
1294
+ if (!erc20OffersAddress) {
1226
1295
  return [];
1227
1296
  }
1228
1297
  const count = await this.netClient.getMessageCount({
@@ -1243,6 +1312,21 @@ var BazaarClient = class {
1243
1312
  startIndex,
1244
1313
  endIndex: count
1245
1314
  });
1315
+ return this.processErc20OffersFromMessages(messages, options);
1316
+ }
1317
+ /**
1318
+ * Process pre-fetched messages into valid ERC20 offers.
1319
+ *
1320
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
1321
+ * to avoid redundant RPC calls.
1322
+ */
1323
+ async processErc20OffersFromMessages(messages, options) {
1324
+ const { tokenAddress, excludeMaker } = options;
1325
+ const tag = `[BazaarClient.processErc20Offers chain=${this.chainId}]`;
1326
+ const weth = getWrappedNativeCurrency(this.chainId);
1327
+ if (!weth) {
1328
+ return [];
1329
+ }
1246
1330
  let offers = [];
1247
1331
  for (const message of messages) {
1248
1332
  const offer = parseErc20OfferFromMessage(message, this.chainId);
@@ -1260,6 +1344,7 @@ var BazaarClient = class {
1260
1344
  }
1261
1345
  offers.push(offer);
1262
1346
  }
1347
+ console.log(tag, `parsed ${offers.length}/${messages.length} messages into offers`);
1263
1348
  if (offers.length === 0) {
1264
1349
  return [];
1265
1350
  }
@@ -1274,9 +1359,11 @@ var BazaarClient = class {
1274
1359
  const statusInfo = statusInfos[index];
1275
1360
  offer.orderStatus = getOrderStatusFromInfo(offer.orderComponents, statusInfo);
1276
1361
  });
1362
+ const now = Math.floor(Date.now() / 1e3);
1277
1363
  offers = offers.filter(
1278
- (o) => o.orderStatus === 2 /* OPEN */ && o.expirationDate > Math.floor(Date.now() / 1e3)
1364
+ (o) => o.orderStatus === 2 /* OPEN */ && o.expirationDate > now
1279
1365
  );
1366
+ console.log(tag, `after status filter: ${offers.length} OPEN & not expired`);
1280
1367
  if (offers.length === 0) {
1281
1368
  return [];
1282
1369
  }
@@ -1286,6 +1373,7 @@ var BazaarClient = class {
1286
1373
  uniqueMakers.forEach((maker, index) => {
1287
1374
  balanceMap.set(maker.toLowerCase(), balances[index]);
1288
1375
  });
1376
+ const beforeBalance = offers.length;
1289
1377
  offers = offers.filter((offer) => {
1290
1378
  const balance = balanceMap.get(offer.maker.toLowerCase()) || BigInt(0);
1291
1379
  return isErc20OfferValid(
@@ -1295,6 +1383,7 @@ var BazaarClient = class {
1295
1383
  balance
1296
1384
  );
1297
1385
  });
1386
+ console.log(tag, `after balance filter: ${offers.length}/${beforeBalance} (${beforeBalance - offers.length} dropped)`);
1298
1387
  return sortErc20OffersByPricePerToken(offers);
1299
1388
  }
1300
1389
  /**
@@ -1334,6 +1423,17 @@ var BazaarClient = class {
1334
1423
  startIndex,
1335
1424
  endIndex
1336
1425
  });
1426
+ return this.processErc20ListingsFromMessages(messages, options);
1427
+ }
1428
+ /**
1429
+ * Process pre-fetched messages into valid ERC20 listings.
1430
+ *
1431
+ * Use this when messages have already been fetched (e.g. via useNetMessages)
1432
+ * to avoid redundant RPC calls.
1433
+ */
1434
+ async processErc20ListingsFromMessages(messages, options) {
1435
+ const { tokenAddress, excludeMaker } = options;
1436
+ const tag = `[BazaarClient.processErc20Listings chain=${this.chainId}]`;
1337
1437
  let listings = [];
1338
1438
  for (const message of messages) {
1339
1439
  const listing = parseErc20ListingFromMessage(message, this.chainId);
@@ -1346,6 +1446,7 @@ var BazaarClient = class {
1346
1446
  }
1347
1447
  listings.push(listing);
1348
1448
  }
1449
+ console.log(tag, `parsed ${listings.length}/${messages.length} messages into listings`);
1349
1450
  if (listings.length === 0) {
1350
1451
  return [];
1351
1452
  }
@@ -1360,18 +1461,28 @@ var BazaarClient = class {
1360
1461
  const statusInfo = statusInfos[index];
1361
1462
  listing.orderStatus = getOrderStatusFromInfo(listing.orderComponents, statusInfo);
1362
1463
  });
1464
+ const statusCounts = {};
1465
+ const now = Math.floor(Date.now() / 1e3);
1466
+ let expiredCount = 0;
1467
+ for (const l of listings) {
1468
+ statusCounts[l.orderStatus] = (statusCounts[l.orderStatus] || 0) + 1;
1469
+ if (l.expirationDate <= now) expiredCount++;
1470
+ }
1471
+ console.log(tag, `order statuses:`, statusCounts, `expired: ${expiredCount}`);
1363
1472
  listings = listings.filter(
1364
- (l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > Math.floor(Date.now() / 1e3)
1473
+ (l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > now
1365
1474
  );
1475
+ console.log(tag, `after status filter: ${listings.length} OPEN & not expired`);
1366
1476
  if (listings.length === 0) {
1367
1477
  return [];
1368
1478
  }
1369
1479
  const uniqueMakers = [...new Set(listings.map((l) => l.maker))];
1370
1480
  const balances = await bulkFetchErc20Balances(this.client, tokenAddress, uniqueMakers);
1371
1481
  const balanceMap = /* @__PURE__ */ new Map();
1372
- uniqueMakers.forEach((maker2, index) => {
1373
- balanceMap.set(maker2.toLowerCase(), balances[index]);
1482
+ uniqueMakers.forEach((maker, index) => {
1483
+ balanceMap.set(maker.toLowerCase(), balances[index]);
1374
1484
  });
1485
+ const beforeBalance = listings.length;
1375
1486
  listings = listings.filter((listing) => {
1376
1487
  const balance = balanceMap.get(listing.maker.toLowerCase()) || BigInt(0);
1377
1488
  return isErc20ListingValid(
@@ -1381,6 +1492,7 @@ var BazaarClient = class {
1381
1492
  balance
1382
1493
  );
1383
1494
  });
1495
+ console.log(tag, `after balance filter: ${listings.length}/${beforeBalance} (${beforeBalance - listings.length} dropped)`);
1384
1496
  return sortErc20ListingsByPricePerToken(listings);
1385
1497
  }
1386
1498
  /**