@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/README.md +198 -0
- package/dist/index.d.mts +921 -0
- package/dist/index.d.ts +921 -0
- package/dist/index.js +1371 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1318 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react.d.mts +178 -0
- package/dist/react.d.ts +178 -0
- package/dist/react.js +1397 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +1393 -0
- package/dist/react.mjs.map +1 -0
- package/dist/types-CY-6M9Ta.d.mts +247 -0
- package/dist/types-CY-6M9Ta.d.ts +247 -0
- package/package.json +96 -0
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
|