@net-protocol/bazaar 0.1.5 → 0.1.7
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/index.d.mts +30 -3
- package/dist/index.d.ts +30 -3
- package/dist/index.js +198 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +197 -16
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +63 -5
- package/dist/react.d.ts +63 -5
- package/dist/react.js +306 -17
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +306 -18
- package/dist/react.mjs.map +1 -1
- package/dist/{types-CfGfQTJL.d.mts → types-5kMf461x.d.mts} +39 -3
- package/dist/{types-CfGfQTJL.d.ts → types-5kMf461x.d.ts} +39 -3
- package/package.json +2 -1
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-
|
|
2
|
+
import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing, S as Sale } from './types-5kMf461x.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* React hook for fetching NFT listings from Bazaar
|
|
@@ -8,8 +8,8 @@ import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing
|
|
|
8
8
|
interface UseBazaarListingsOptions {
|
|
9
9
|
/** Chain ID to query */
|
|
10
10
|
chainId: number;
|
|
11
|
-
/** NFT collection address */
|
|
12
|
-
nftAddress
|
|
11
|
+
/** NFT collection address (optional - if omitted, fetches recent listings across all collections) */
|
|
12
|
+
nftAddress?: `0x${string}`;
|
|
13
13
|
/** Exclude listings from this address */
|
|
14
14
|
excludeMaker?: `0x${string}`;
|
|
15
15
|
/** Only include listings from this address */
|
|
@@ -45,7 +45,8 @@ interface UseBazaarListingsResult {
|
|
|
45
45
|
* - Not expired
|
|
46
46
|
* - Seller still owns the NFT
|
|
47
47
|
*
|
|
48
|
-
* Results are deduplicated (one per token) and sorted by price (lowest first)
|
|
48
|
+
* Results are deduplicated (one per token) and sorted by price (lowest first).
|
|
49
|
+
* If `nftAddress` is omitted, fetches recent listings across all collections on the chain.
|
|
49
50
|
*
|
|
50
51
|
* @example
|
|
51
52
|
* ```tsx
|
|
@@ -255,4 +256,61 @@ interface UseBazaarErc20ListingsResult {
|
|
|
255
256
|
*/
|
|
256
257
|
declare function useBazaarErc20Listings({ chainId, tokenAddress, excludeMaker, maker, maxMessages, startIndex: startIndexOverride, endIndex: endIndexOverride, enabled, publicClient, }: UseBazaarErc20ListingsOptions): UseBazaarErc20ListingsResult;
|
|
257
258
|
|
|
258
|
-
|
|
259
|
+
/**
|
|
260
|
+
* React hook for fetching recent sales from Bazaar
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
interface UseBazaarSalesOptions {
|
|
264
|
+
/** Chain ID to query */
|
|
265
|
+
chainId: number;
|
|
266
|
+
/** NFT collection address */
|
|
267
|
+
nftAddress: `0x${string}`;
|
|
268
|
+
/** Maximum number of messages to fetch (default: 100) */
|
|
269
|
+
maxMessages?: number;
|
|
270
|
+
/** Whether the query is enabled (default: true) */
|
|
271
|
+
enabled?: boolean;
|
|
272
|
+
}
|
|
273
|
+
interface UseBazaarSalesResult {
|
|
274
|
+
/** Recent sales (sorted by timestamp, most recent first) */
|
|
275
|
+
sales: Sale[];
|
|
276
|
+
/** Whether the data is loading */
|
|
277
|
+
isLoading: boolean;
|
|
278
|
+
/** Error if any */
|
|
279
|
+
error: Error | undefined;
|
|
280
|
+
/** Refetch function */
|
|
281
|
+
refetch: () => void;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* React hook for fetching recent sales from Bazaar
|
|
285
|
+
*
|
|
286
|
+
* Sales data flows differently from listings/offers:
|
|
287
|
+
* - Net messages (appAddress=zone, topic=nftAddress) contain order hashes
|
|
288
|
+
* - Actual sale data is fetched from the bulk storage contract
|
|
289
|
+
*
|
|
290
|
+
* Results are sorted by timestamp (most recent first)
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```tsx
|
|
294
|
+
* const { sales, isLoading, error } = useBazaarSales({
|
|
295
|
+
* chainId: 8453,
|
|
296
|
+
* nftAddress: "0x...",
|
|
297
|
+
* maxMessages: 100,
|
|
298
|
+
* });
|
|
299
|
+
*
|
|
300
|
+
* if (isLoading) return <div>Loading...</div>;
|
|
301
|
+
* if (error) return <div>Error: {error.message}</div>;
|
|
302
|
+
*
|
|
303
|
+
* return (
|
|
304
|
+
* <ul>
|
|
305
|
+
* {sales.map((sale) => (
|
|
306
|
+
* <li key={sale.orderHash}>
|
|
307
|
+
* Token #{sale.tokenId} - {sale.price} {sale.currency}
|
|
308
|
+
* </li>
|
|
309
|
+
* ))}
|
|
310
|
+
* </ul>
|
|
311
|
+
* );
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
declare function useBazaarSales({ chainId, nftAddress, maxMessages, enabled, }: UseBazaarSalesOptions): UseBazaarSalesResult;
|
|
315
|
+
|
|
316
|
+
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-
|
|
2
|
+
import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing, S as Sale } from './types-5kMf461x.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* React hook for fetching NFT listings from Bazaar
|
|
@@ -8,8 +8,8 @@ import { L as Listing, C as CollectionOffer, E as Erc20Offer, d as Erc20Listing
|
|
|
8
8
|
interface UseBazaarListingsOptions {
|
|
9
9
|
/** Chain ID to query */
|
|
10
10
|
chainId: number;
|
|
11
|
-
/** NFT collection address */
|
|
12
|
-
nftAddress
|
|
11
|
+
/** NFT collection address (optional - if omitted, fetches recent listings across all collections) */
|
|
12
|
+
nftAddress?: `0x${string}`;
|
|
13
13
|
/** Exclude listings from this address */
|
|
14
14
|
excludeMaker?: `0x${string}`;
|
|
15
15
|
/** Only include listings from this address */
|
|
@@ -45,7 +45,8 @@ interface UseBazaarListingsResult {
|
|
|
45
45
|
* - Not expired
|
|
46
46
|
* - Seller still owns the NFT
|
|
47
47
|
*
|
|
48
|
-
* Results are deduplicated (one per token) and sorted by price (lowest first)
|
|
48
|
+
* Results are deduplicated (one per token) and sorted by price (lowest first).
|
|
49
|
+
* If `nftAddress` is omitted, fetches recent listings across all collections on the chain.
|
|
49
50
|
*
|
|
50
51
|
* @example
|
|
51
52
|
* ```tsx
|
|
@@ -255,4 +256,61 @@ interface UseBazaarErc20ListingsResult {
|
|
|
255
256
|
*/
|
|
256
257
|
declare function useBazaarErc20Listings({ chainId, tokenAddress, excludeMaker, maker, maxMessages, startIndex: startIndexOverride, endIndex: endIndexOverride, enabled, publicClient, }: UseBazaarErc20ListingsOptions): UseBazaarErc20ListingsResult;
|
|
257
258
|
|
|
258
|
-
|
|
259
|
+
/**
|
|
260
|
+
* React hook for fetching recent sales from Bazaar
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
interface UseBazaarSalesOptions {
|
|
264
|
+
/** Chain ID to query */
|
|
265
|
+
chainId: number;
|
|
266
|
+
/** NFT collection address */
|
|
267
|
+
nftAddress: `0x${string}`;
|
|
268
|
+
/** Maximum number of messages to fetch (default: 100) */
|
|
269
|
+
maxMessages?: number;
|
|
270
|
+
/** Whether the query is enabled (default: true) */
|
|
271
|
+
enabled?: boolean;
|
|
272
|
+
}
|
|
273
|
+
interface UseBazaarSalesResult {
|
|
274
|
+
/** Recent sales (sorted by timestamp, most recent first) */
|
|
275
|
+
sales: Sale[];
|
|
276
|
+
/** Whether the data is loading */
|
|
277
|
+
isLoading: boolean;
|
|
278
|
+
/** Error if any */
|
|
279
|
+
error: Error | undefined;
|
|
280
|
+
/** Refetch function */
|
|
281
|
+
refetch: () => void;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* React hook for fetching recent sales from Bazaar
|
|
285
|
+
*
|
|
286
|
+
* Sales data flows differently from listings/offers:
|
|
287
|
+
* - Net messages (appAddress=zone, topic=nftAddress) contain order hashes
|
|
288
|
+
* - Actual sale data is fetched from the bulk storage contract
|
|
289
|
+
*
|
|
290
|
+
* Results are sorted by timestamp (most recent first)
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```tsx
|
|
294
|
+
* const { sales, isLoading, error } = useBazaarSales({
|
|
295
|
+
* chainId: 8453,
|
|
296
|
+
* nftAddress: "0x...",
|
|
297
|
+
* maxMessages: 100,
|
|
298
|
+
* });
|
|
299
|
+
*
|
|
300
|
+
* if (isLoading) return <div>Loading...</div>;
|
|
301
|
+
* if (error) return <div>Error: {error.message}</div>;
|
|
302
|
+
*
|
|
303
|
+
* return (
|
|
304
|
+
* <ul>
|
|
305
|
+
* {sales.map((sale) => (
|
|
306
|
+
* <li key={sale.orderHash}>
|
|
307
|
+
* Token #{sale.tokenId} - {sale.price} {sale.currency}
|
|
308
|
+
* </li>
|
|
309
|
+
* ))}
|
|
310
|
+
* </ul>
|
|
311
|
+
* );
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
declare function useBazaarSales({ chainId, nftAddress, maxMessages, enabled, }: UseBazaarSalesOptions): UseBazaarSalesResult;
|
|
315
|
+
|
|
316
|
+
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
|
-
|
|
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"],
|
|
@@ -860,7 +934,7 @@ var BazaarClient = class {
|
|
|
860
934
|
const bazaarAddress = getBazaarAddress(this.chainId);
|
|
861
935
|
const filter = {
|
|
862
936
|
appAddress: bazaarAddress,
|
|
863
|
-
topic: nftAddress
|
|
937
|
+
topic: nftAddress?.toLowerCase(),
|
|
864
938
|
maker
|
|
865
939
|
};
|
|
866
940
|
let startIndex;
|
|
@@ -933,18 +1007,57 @@ var BazaarClient = class {
|
|
|
933
1007
|
}
|
|
934
1008
|
const openListings = listings.filter((l) => l.orderStatus === 2 /* OPEN */);
|
|
935
1009
|
const expiredListings = includeExpired ? listings.filter((l) => l.orderStatus === 1 /* EXPIRED */) : [];
|
|
936
|
-
|
|
937
|
-
const owners = await bulkFetchNftOwners(this.client, nftAddress, tokenIds);
|
|
1010
|
+
let validOpenListings;
|
|
938
1011
|
const beforeOwnership = openListings.length;
|
|
939
|
-
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1012
|
+
if (nftAddress) {
|
|
1013
|
+
const tokenIds = openListings.map((l) => l.tokenId);
|
|
1014
|
+
const owners = await bulkFetchNftOwners(this.client, nftAddress, tokenIds);
|
|
1015
|
+
validOpenListings = openListings.filter((listing, index) => {
|
|
1016
|
+
const owner = owners[index];
|
|
1017
|
+
return isListingValid(
|
|
1018
|
+
listing.orderStatus,
|
|
1019
|
+
listing.expirationDate,
|
|
1020
|
+
listing.maker,
|
|
1021
|
+
owner
|
|
1022
|
+
);
|
|
1023
|
+
});
|
|
1024
|
+
} else {
|
|
1025
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1026
|
+
for (const listing of openListings) {
|
|
1027
|
+
const key = listing.nftAddress.toLowerCase();
|
|
1028
|
+
const group = groups.get(key);
|
|
1029
|
+
if (group) {
|
|
1030
|
+
group.push(listing);
|
|
1031
|
+
} else {
|
|
1032
|
+
groups.set(key, [listing]);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
const groupEntries = Array.from(groups.entries());
|
|
1036
|
+
const ownerResults = await Promise.all(
|
|
1037
|
+
groupEntries.map(
|
|
1038
|
+
([addr, groupListings]) => bulkFetchNftOwners(
|
|
1039
|
+
this.client,
|
|
1040
|
+
addr,
|
|
1041
|
+
groupListings.map((l) => l.tokenId)
|
|
1042
|
+
)
|
|
1043
|
+
)
|
|
946
1044
|
);
|
|
947
|
-
|
|
1045
|
+
validOpenListings = [];
|
|
1046
|
+
groupEntries.forEach(([, groupListings], groupIndex) => {
|
|
1047
|
+
const owners = ownerResults[groupIndex];
|
|
1048
|
+
for (let i = 0; i < groupListings.length; i++) {
|
|
1049
|
+
const listing = groupListings[i];
|
|
1050
|
+
if (isListingValid(
|
|
1051
|
+
listing.orderStatus,
|
|
1052
|
+
listing.expirationDate,
|
|
1053
|
+
listing.maker,
|
|
1054
|
+
owners[i]
|
|
1055
|
+
)) {
|
|
1056
|
+
validOpenListings.push(listing);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
948
1061
|
console.log(tag, `after ownership filter: ${validOpenListings.length}/${beforeOwnership} (${beforeOwnership - validOpenListings.length} dropped)`);
|
|
949
1062
|
const dedupedOpen = sortListingsByPrice(getBestListingPerToken(validOpenListings));
|
|
950
1063
|
const activeTokenKeys = new Set(dedupedOpen.map((l) => `${l.nftAddress.toLowerCase()}-${l.tokenId}`));
|
|
@@ -1277,6 +1390,72 @@ var BazaarClient = class {
|
|
|
1277
1390
|
console.log(tag, `after balance filter: ${listings.length}/${beforeBalance} (${beforeBalance - listings.length} dropped)`);
|
|
1278
1391
|
return sortErc20ListingsByPricePerToken(listings);
|
|
1279
1392
|
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Get recent sales for a collection
|
|
1395
|
+
*
|
|
1396
|
+
* Sales are stored differently from listings: Net messages contain order hashes,
|
|
1397
|
+
* and the actual sale data is fetched from the bulk storage contract.
|
|
1398
|
+
*
|
|
1399
|
+
* Results are sorted by timestamp (most recent first)
|
|
1400
|
+
*/
|
|
1401
|
+
async getSales(options) {
|
|
1402
|
+
const { nftAddress, maxMessages = 100 } = options;
|
|
1403
|
+
const filter = {
|
|
1404
|
+
appAddress: NET_SEAPORT_ZONE_ADDRESS,
|
|
1405
|
+
topic: nftAddress.toLowerCase()
|
|
1406
|
+
};
|
|
1407
|
+
const count = await this.netClient.getMessageCount({ filter });
|
|
1408
|
+
if (count === 0) {
|
|
1409
|
+
return [];
|
|
1410
|
+
}
|
|
1411
|
+
const startIndex = Math.max(0, count - maxMessages);
|
|
1412
|
+
const messages = await this.netClient.getMessages({
|
|
1413
|
+
filter,
|
|
1414
|
+
startIndex,
|
|
1415
|
+
endIndex: count
|
|
1416
|
+
});
|
|
1417
|
+
return this.processSalesFromMessages(messages, options);
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Process pre-fetched messages into sales.
|
|
1421
|
+
*
|
|
1422
|
+
* Each message's data field contains an order hash. The actual sale data
|
|
1423
|
+
* is fetched from the bulk storage contract using these order hashes.
|
|
1424
|
+
*/
|
|
1425
|
+
async processSalesFromMessages(messages, options) {
|
|
1426
|
+
const tag = `[BazaarClient.processSales chain=${this.chainId}]`;
|
|
1427
|
+
if (messages.length === 0) {
|
|
1428
|
+
return [];
|
|
1429
|
+
}
|
|
1430
|
+
const orderHashes = messages.map((m) => m.data);
|
|
1431
|
+
console.log(tag, `fetching ${orderHashes.length} sale details from storage...`);
|
|
1432
|
+
const bulkKeys = orderHashes.map((hash) => ({
|
|
1433
|
+
key: hash,
|
|
1434
|
+
operator: NET_SEAPORT_ZONE_ADDRESS
|
|
1435
|
+
}));
|
|
1436
|
+
let storedResults;
|
|
1437
|
+
try {
|
|
1438
|
+
storedResults = await this.client.readContract({
|
|
1439
|
+
abi: storage.STORAGE_CONTRACT.abi,
|
|
1440
|
+
address: storage.STORAGE_CONTRACT.address,
|
|
1441
|
+
functionName: "bulkGet",
|
|
1442
|
+
args: [bulkKeys]
|
|
1443
|
+
});
|
|
1444
|
+
} catch (err) {
|
|
1445
|
+
console.error(tag, "bulk storage fetch failed:", err);
|
|
1446
|
+
return [];
|
|
1447
|
+
}
|
|
1448
|
+
const sales = [];
|
|
1449
|
+
for (const result of storedResults) {
|
|
1450
|
+
if (!result.value || result.value === "0x") continue;
|
|
1451
|
+
const sale = parseSaleFromStoredData(result.value, this.chainId);
|
|
1452
|
+
if (sale) {
|
|
1453
|
+
sales.push(sale);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
console.log(tag, `parsed ${sales.length}/${storedResults.length} sales`);
|
|
1457
|
+
return sales;
|
|
1458
|
+
}
|
|
1280
1459
|
/**
|
|
1281
1460
|
* Get the chain ID this client is configured for
|
|
1282
1461
|
*/
|
|
@@ -1423,7 +1602,7 @@ function useBazaarListings({
|
|
|
1423
1602
|
const filter = react.useMemo(
|
|
1424
1603
|
() => ({
|
|
1425
1604
|
appAddress: bazaarAddress,
|
|
1426
|
-
topic: nftAddress
|
|
1605
|
+
topic: nftAddress?.toLowerCase(),
|
|
1427
1606
|
maker
|
|
1428
1607
|
}),
|
|
1429
1608
|
[bazaarAddress, nftAddress, maker]
|
|
@@ -1447,7 +1626,7 @@ function useBazaarListings({
|
|
|
1447
1626
|
endIndex,
|
|
1448
1627
|
enabled: enabled && isSupported && (hasRangeOverride || totalCount > 0)
|
|
1449
1628
|
});
|
|
1450
|
-
const TAG = `[useBazaarListings chain=${chainId} nft=${nftAddress
|
|
1629
|
+
const TAG = `[useBazaarListings chain=${chainId} nft=${nftAddress?.slice(0, 10) ?? "all"}]`;
|
|
1451
1630
|
react.useEffect(() => {
|
|
1452
1631
|
console.log(TAG, {
|
|
1453
1632
|
enabled,
|
|
@@ -1863,10 +2042,120 @@ function useBazaarErc20Listings({
|
|
|
1863
2042
|
refetch
|
|
1864
2043
|
};
|
|
1865
2044
|
}
|
|
2045
|
+
function useBazaarSales({
|
|
2046
|
+
chainId,
|
|
2047
|
+
nftAddress,
|
|
2048
|
+
maxMessages = 100,
|
|
2049
|
+
enabled = true
|
|
2050
|
+
}) {
|
|
2051
|
+
const wagmiClient = wagmi.usePublicClient({ chainId });
|
|
2052
|
+
const [sales, setSales] = react.useState([]);
|
|
2053
|
+
const [isProcessing, setIsProcessing] = react.useState(false);
|
|
2054
|
+
const [processingError, setProcessingError] = react.useState();
|
|
2055
|
+
const [refetchTrigger, setRefetchTrigger] = react.useState(0);
|
|
2056
|
+
const isSupported = react.useMemo(
|
|
2057
|
+
() => isBazaarSupportedOnChain(chainId),
|
|
2058
|
+
[chainId]
|
|
2059
|
+
);
|
|
2060
|
+
const filter = react.useMemo(
|
|
2061
|
+
() => ({
|
|
2062
|
+
appAddress: NET_SEAPORT_ZONE_ADDRESS,
|
|
2063
|
+
topic: nftAddress.toLowerCase()
|
|
2064
|
+
}),
|
|
2065
|
+
[nftAddress]
|
|
2066
|
+
);
|
|
2067
|
+
const { count: totalCount, isLoading: isLoadingCount } = react$1.useNetMessageCount({
|
|
2068
|
+
chainId,
|
|
2069
|
+
filter,
|
|
2070
|
+
enabled: enabled && isSupported
|
|
2071
|
+
});
|
|
2072
|
+
const startIndex = react.useMemo(
|
|
2073
|
+
() => Math.max(0, totalCount - maxMessages),
|
|
2074
|
+
[totalCount, maxMessages]
|
|
2075
|
+
);
|
|
2076
|
+
const {
|
|
2077
|
+
messages,
|
|
2078
|
+
isLoading: isLoadingMessages,
|
|
2079
|
+
error: messagesError,
|
|
2080
|
+
refetch: refetchMessages
|
|
2081
|
+
} = react$1.useNetMessages({
|
|
2082
|
+
chainId,
|
|
2083
|
+
filter,
|
|
2084
|
+
startIndex,
|
|
2085
|
+
endIndex: totalCount,
|
|
2086
|
+
enabled: enabled && isSupported && totalCount > 0
|
|
2087
|
+
});
|
|
2088
|
+
const TAG = `[useBazaarSales chain=${chainId} nft=${nftAddress.slice(0, 10)}]`;
|
|
2089
|
+
react.useEffect(() => {
|
|
2090
|
+
console.log(TAG, {
|
|
2091
|
+
enabled,
|
|
2092
|
+
isSupported,
|
|
2093
|
+
totalCount,
|
|
2094
|
+
isLoadingCount,
|
|
2095
|
+
startIndex,
|
|
2096
|
+
endIndex: totalCount,
|
|
2097
|
+
messagesLength: messages?.length ?? 0,
|
|
2098
|
+
isLoadingMessages,
|
|
2099
|
+
messagesError: messagesError?.message
|
|
2100
|
+
});
|
|
2101
|
+
}, [enabled, isSupported, totalCount, isLoadingCount, startIndex, messages?.length, isLoadingMessages, messagesError]);
|
|
2102
|
+
react.useEffect(() => {
|
|
2103
|
+
if (!isSupported || !enabled) {
|
|
2104
|
+
setSales([]);
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
if (!messages || messages.length === 0) {
|
|
2108
|
+
setSales([]);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
let cancelled = false;
|
|
2112
|
+
async function processSales() {
|
|
2113
|
+
setIsProcessing(true);
|
|
2114
|
+
setProcessingError(void 0);
|
|
2115
|
+
console.log(TAG, `processing ${messages.length} messages...`);
|
|
2116
|
+
try {
|
|
2117
|
+
const client = new BazaarClient({ chainId, publicClient: wagmiClient });
|
|
2118
|
+
const parsedSales = await client.processSalesFromMessages(
|
|
2119
|
+
messages,
|
|
2120
|
+
{ nftAddress }
|
|
2121
|
+
);
|
|
2122
|
+
console.log(TAG, `processed \u2192 ${parsedSales.length} sales`);
|
|
2123
|
+
if (!cancelled) {
|
|
2124
|
+
setSales(parsedSales);
|
|
2125
|
+
}
|
|
2126
|
+
} catch (err) {
|
|
2127
|
+
console.error(TAG, "processing error:", err);
|
|
2128
|
+
if (!cancelled) {
|
|
2129
|
+
setProcessingError(err instanceof Error ? err : new Error(String(err)));
|
|
2130
|
+
setSales([]);
|
|
2131
|
+
}
|
|
2132
|
+
} finally {
|
|
2133
|
+
if (!cancelled) {
|
|
2134
|
+
setIsProcessing(false);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
processSales();
|
|
2139
|
+
return () => {
|
|
2140
|
+
cancelled = true;
|
|
2141
|
+
};
|
|
2142
|
+
}, [chainId, nftAddress, messages, isSupported, enabled, refetchTrigger]);
|
|
2143
|
+
const refetch = () => {
|
|
2144
|
+
refetchMessages();
|
|
2145
|
+
setRefetchTrigger((t) => t + 1);
|
|
2146
|
+
};
|
|
2147
|
+
return {
|
|
2148
|
+
sales,
|
|
2149
|
+
isLoading: isLoadingCount || isLoadingMessages || isProcessing,
|
|
2150
|
+
error: messagesError || processingError,
|
|
2151
|
+
refetch
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
1866
2154
|
|
|
1867
2155
|
exports.useBazaarCollectionOffers = useBazaarCollectionOffers;
|
|
1868
2156
|
exports.useBazaarErc20Listings = useBazaarErc20Listings;
|
|
1869
2157
|
exports.useBazaarErc20Offers = useBazaarErc20Offers;
|
|
1870
2158
|
exports.useBazaarListings = useBazaarListings;
|
|
2159
|
+
exports.useBazaarSales = useBazaarSales;
|
|
1871
2160
|
//# sourceMappingURL=react.js.map
|
|
1872
2161
|
//# sourceMappingURL=react.js.map
|