@net-protocol/bazaar 0.1.0 → 0.1.1

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.mjs CHANGED
@@ -354,6 +354,7 @@ var DEFAULT_SEAPORT_ADDRESS = "0x0000000000000068F116a894984e2DB1123eB395";
354
354
  var DEFAULT_BAZAAR_ADDRESS = "0x00000000E3dA5fC031282A39759bDDA78ae7fAE5";
355
355
  var DEFAULT_COLLECTION_OFFERS_ADDRESS = "0x0000000D43423E0A12CecB307a74591999b32B32";
356
356
  var DEFAULT_FEE_COLLECTOR_ADDRESS = "0x32D16C15410248bef498D7aF50D10Db1a546b9E5";
357
+ var DEFAULT_ERC20_BAZAAR_ADDRESS = "0x00000000a2d173a4610c85c7471a25b6bc216a70";
357
358
  var DEFAULT_NFT_FEE_BPS = 500;
358
359
  var BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS = "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
359
360
  var ERC721_OWNER_OF_HELPER_ADDRESS = "0x000000aa4eFa2e5A4a6002C7F08B6e8Ec8cf1dDa";
@@ -366,6 +367,7 @@ var BAZAAR_CHAIN_CONFIGS = {
366
367
  bazaarAddress: "0x000000058f3ade587388daf827174d0e6fc97595",
367
368
  collectionOffersAddress: "0x0000000f9c45efcff0f78d8b54aa6a40092d66dc",
368
369
  erc20OffersAddress: "0x0000000e23a89aa06f317306aa1ae231d3503082",
370
+ erc20BazaarAddress: "0x00000006557e3629e2fc50bbad0c002b27cac492",
369
371
  seaportAddress: DEFAULT_SEAPORT_ADDRESS,
370
372
  feeCollectorAddress: "0x66547ff4f7206e291F7BC157b54C026Fc6660961",
371
373
  nftFeeBps: 0,
@@ -455,6 +457,7 @@ var BAZAAR_CHAIN_CONFIGS = {
455
457
  bazaarAddress: "0x000000058f3ade587388daf827174d0e6fc97595",
456
458
  collectionOffersAddress: "0x0000000f9c45efcff0f78d8b54aa6a40092d66dc",
457
459
  erc20OffersAddress: "0x0000000e23a89aa06f317306aa1ae231d3503082",
460
+ erc20BazaarAddress: "0x00000006557e3629e2fc50bbad0c002b27cac492",
458
461
  seaportAddress: DEFAULT_SEAPORT_ADDRESS,
459
462
  feeCollectorAddress: "0x66547ff4f7206e291F7BC157b54C026Fc6660961",
460
463
  nftFeeBps: 0,
@@ -531,6 +534,9 @@ function getHighEthAddress(chainId) {
531
534
  function getErc20OffersAddress(chainId) {
532
535
  return BAZAAR_CHAIN_CONFIGS[chainId]?.erc20OffersAddress;
533
536
  }
537
+ function getErc20BazaarAddress(chainId) {
538
+ return BAZAAR_CHAIN_CONFIGS[chainId]?.erc20BazaarAddress ?? DEFAULT_ERC20_BAZAAR_ADDRESS;
539
+ }
534
540
  function decodeSeaportSubmission(messageData) {
535
541
  const [decoded] = decodeAbiParameters(BAZAAR_SUBMISSION_ABI, messageData);
536
542
  return {
@@ -752,6 +758,19 @@ function isErc20OfferValid(orderStatus, expirationDate, priceWei, buyerWethBalan
752
758
  }
753
759
  return true;
754
760
  }
761
+ function isErc20ListingValid(orderStatus, expirationDate, tokenAmount, sellerTokenBalance) {
762
+ if (orderStatus !== 2 /* OPEN */) {
763
+ return false;
764
+ }
765
+ const now = Math.floor(Date.now() / 1e3);
766
+ if (expirationDate <= now) {
767
+ return false;
768
+ }
769
+ if (sellerTokenBalance < tokenAmount) {
770
+ return false;
771
+ }
772
+ return true;
773
+ }
755
774
 
756
775
  // src/utils/parsing.ts
757
776
  function parseListingFromMessage(message, chainId) {
@@ -921,6 +940,58 @@ function sortErc20OffersByPricePerToken(offers) {
921
940
  return 0;
922
941
  });
923
942
  }
943
+ function parseErc20ListingFromMessage(message, chainId) {
944
+ try {
945
+ const submission = decodeSeaportSubmission(message.data);
946
+ const { parameters } = submission;
947
+ if (parameters.zone.toLowerCase() === NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS.toLowerCase()) {
948
+ return null;
949
+ }
950
+ const offerItem = parameters.offer[0];
951
+ if (!offerItem || offerItem.itemType !== 1 /* ERC20 */) {
952
+ return null;
953
+ }
954
+ const tokenAmount = offerItem.startAmount;
955
+ if (tokenAmount === BigInt(0)) {
956
+ return null;
957
+ }
958
+ const priceWei = getTotalConsiderationAmount(parameters);
959
+ if (priceWei === BigInt(0)) {
960
+ return null;
961
+ }
962
+ const pricePerTokenWei = priceWei / tokenAmount;
963
+ return {
964
+ maker: parameters.offerer,
965
+ tokenAddress: offerItem.token,
966
+ tokenAmount,
967
+ priceWei,
968
+ pricePerTokenWei,
969
+ price: formatPrice(priceWei),
970
+ pricePerToken: formatPrice(pricePerTokenWei),
971
+ currency: getCurrencySymbol(chainId),
972
+ expirationDate: Number(parameters.endTime),
973
+ orderHash: "0x",
974
+ // Will be computed later
975
+ orderStatus: 2 /* OPEN */,
976
+ // Will be validated later
977
+ messageData: message.data,
978
+ orderComponents: {
979
+ ...parameters,
980
+ counter: submission.counter
981
+ }
982
+ };
983
+ } catch {
984
+ return null;
985
+ }
986
+ }
987
+ function sortErc20ListingsByPricePerToken(listings) {
988
+ return [...listings].sort((a, b) => {
989
+ const diff = a.pricePerTokenWei - b.pricePerTokenWei;
990
+ if (diff < BigInt(0)) return -1;
991
+ if (diff > BigInt(0)) return 1;
992
+ return 0;
993
+ });
994
+ }
924
995
 
925
996
  // src/client/BazaarClient.ts
926
997
  var CHAIN_RPC_URLS = {
@@ -1216,6 +1287,87 @@ var BazaarClient = class {
1216
1287
  });
1217
1288
  return sortErc20OffersByPricePerToken(offers);
1218
1289
  }
1290
+ /**
1291
+ * Get valid ERC20 listings for a token
1292
+ *
1293
+ * Returns listings that are:
1294
+ * - OPEN status (not filled, cancelled, or expired)
1295
+ * - Not expired
1296
+ * - Seller has sufficient ERC20 token balance
1297
+ *
1298
+ * Results are sorted by price per token (lowest first). No deduplication —
1299
+ * all valid listings are returned (grouped by maker in the UI).
1300
+ */
1301
+ async getErc20Listings(options) {
1302
+ const { tokenAddress, excludeMaker, maxMessages = 200 } = options;
1303
+ const erc20BazaarAddress = getErc20BazaarAddress(this.chainId);
1304
+ const count = await this.netClient.getMessageCount({
1305
+ filter: {
1306
+ appAddress: erc20BazaarAddress,
1307
+ topic: tokenAddress.toLowerCase()
1308
+ }
1309
+ });
1310
+ if (count === 0) {
1311
+ return [];
1312
+ }
1313
+ const startIndex = Math.max(0, count - maxMessages);
1314
+ const messages = await this.netClient.getMessages({
1315
+ filter: {
1316
+ appAddress: erc20BazaarAddress,
1317
+ topic: tokenAddress.toLowerCase()
1318
+ },
1319
+ startIndex,
1320
+ endIndex: count
1321
+ });
1322
+ let listings = [];
1323
+ for (const message of messages) {
1324
+ const listing = parseErc20ListingFromMessage(message, this.chainId);
1325
+ if (!listing) continue;
1326
+ if (listing.tokenAddress.toLowerCase() !== tokenAddress.toLowerCase()) {
1327
+ continue;
1328
+ }
1329
+ if (excludeMaker && listing.maker.toLowerCase() === excludeMaker.toLowerCase()) {
1330
+ continue;
1331
+ }
1332
+ listings.push(listing);
1333
+ }
1334
+ if (listings.length === 0) {
1335
+ return [];
1336
+ }
1337
+ const seaport = createSeaportInstance(this.chainId, this.rpcUrl);
1338
+ for (const listing of listings) {
1339
+ const order = getSeaportOrderFromMessageData(listing.messageData);
1340
+ listing.orderHash = computeOrderHash(seaport, order.parameters, order.counter);
1341
+ }
1342
+ const orderHashes = listings.map((l) => l.orderHash);
1343
+ const statusInfos = await bulkFetchOrderStatuses(this.client, this.chainId, orderHashes);
1344
+ listings.forEach((listing, index) => {
1345
+ const statusInfo = statusInfos[index];
1346
+ listing.orderStatus = getOrderStatusFromInfo(listing.orderComponents, statusInfo);
1347
+ });
1348
+ listings = listings.filter(
1349
+ (l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > Math.floor(Date.now() / 1e3)
1350
+ );
1351
+ if (listings.length === 0) {
1352
+ return [];
1353
+ }
1354
+ const uniqueMakers = [...new Set(listings.map((l) => l.maker))];
1355
+ const balances = await bulkFetchErc20Balances(this.client, tokenAddress, uniqueMakers);
1356
+ const balanceMap = /* @__PURE__ */ new Map();
1357
+ uniqueMakers.forEach((maker, index) => {
1358
+ balanceMap.set(maker.toLowerCase(), balances[index]);
1359
+ });
1360
+ listings = listings.filter((listing) => {
1361
+ const balance = balanceMap.get(listing.maker.toLowerCase()) || BigInt(0);
1362
+ return isErc20ListingValid(
1363
+ listing.orderStatus,
1364
+ listing.expirationDate,
1365
+ listing.tokenAmount,
1366
+ balance
1367
+ );
1368
+ });
1369
+ return sortErc20ListingsByPricePerToken(listings);
1370
+ }
1219
1371
  /**
1220
1372
  * Get the chain ID this client is configured for
1221
1373
  */
@@ -1241,6 +1393,12 @@ var BazaarClient = class {
1241
1393
  getErc20OffersAddress() {
1242
1394
  return getErc20OffersAddress(this.chainId);
1243
1395
  }
1396
+ /**
1397
+ * Get the ERC20 bazaar (listings) contract address for this chain
1398
+ */
1399
+ getErc20BazaarAddress() {
1400
+ return getErc20BazaarAddress(this.chainId);
1401
+ }
1244
1402
  /**
1245
1403
  * Get the Seaport contract address for this chain
1246
1404
  */
@@ -1271,6 +1429,18 @@ var BazaarClient = class {
1271
1429
  }
1272
1430
  return this.prepareCancelOrder(offer.orderComponents);
1273
1431
  }
1432
+ /**
1433
+ * Prepare a transaction to cancel an ERC20 listing
1434
+ *
1435
+ * The listing must have been created by the caller.
1436
+ * Use the orderComponents from the Erc20Listing object returned by getErc20Listings().
1437
+ */
1438
+ prepareCancelErc20Listing(listing) {
1439
+ if (!listing.orderComponents) {
1440
+ throw new Error("Listing does not have order components");
1441
+ }
1442
+ return this.prepareCancelOrder(listing.orderComponents);
1443
+ }
1274
1444
  /**
1275
1445
  * Prepare a transaction to cancel a Seaport order
1276
1446
  *
@@ -1313,6 +1483,6 @@ var BazaarClient = class {
1313
1483
  }
1314
1484
  };
1315
1485
 
1316
- export { BAZAAR_COLLECTION_OFFERS_ABI, BAZAAR_SUBMISSION_ABI, BAZAAR_V2_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS, BazaarClient, ERC20_BULK_BALANCE_CHECKER_ABI, ERC20_BULK_BALANCE_CHECKER_ADDRESS, ERC721_OWNER_OF_HELPER_ABI, ERC721_OWNER_OF_HELPER_ADDRESS, ItemType, NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS, NET_SEAPORT_ZONE_ADDRESS, OrderType, SEAPORT_CANCEL_ABI, SeaportOrderStatus, bulkFetchErc20Balances, bulkFetchNftOwners, bulkFetchOrderStatuses, computeOrderHash, createBalanceMap, createOrderStatusMap, createOwnershipMap, createSeaportInstance, decodeSeaportSubmission, formatPrice, getBazaarAddress, getBazaarChainConfig, getBazaarSupportedChainIds, getBestCollectionOffer, getBestListingPerToken, getCollectionOffersAddress, getCurrencySymbol, getErc20OffersAddress, getFeeCollectorAddress, getHighEthAddress, getNftFeeBps, getOrderStatusFromInfo, getSeaportAddress, getSeaportOrderFromMessageData, getTotalConsiderationAmount, getWrappedNativeCurrency, isBazaarSupportedOnChain, isCollectionOfferValid, isErc20OfferValid, isListingValid, parseCollectionOfferFromMessage, parseErc20OfferFromMessage, parseListingFromMessage, sortErc20OffersByPricePerToken, sortListingsByPrice, sortOffersByPrice };
1486
+ export { BAZAAR_COLLECTION_OFFERS_ABI, BAZAAR_SUBMISSION_ABI, BAZAAR_V2_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS, BazaarClient, ERC20_BULK_BALANCE_CHECKER_ABI, ERC20_BULK_BALANCE_CHECKER_ADDRESS, ERC721_OWNER_OF_HELPER_ABI, ERC721_OWNER_OF_HELPER_ADDRESS, ItemType, NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS, NET_SEAPORT_ZONE_ADDRESS, OrderType, SEAPORT_CANCEL_ABI, SeaportOrderStatus, bulkFetchErc20Balances, bulkFetchNftOwners, bulkFetchOrderStatuses, computeOrderHash, createBalanceMap, createOrderStatusMap, createOwnershipMap, createSeaportInstance, decodeSeaportSubmission, formatPrice, getBazaarAddress, getBazaarChainConfig, getBazaarSupportedChainIds, getBestCollectionOffer, getBestListingPerToken, getCollectionOffersAddress, getCurrencySymbol, getErc20BazaarAddress, getErc20OffersAddress, getFeeCollectorAddress, getHighEthAddress, getNftFeeBps, getOrderStatusFromInfo, getSeaportAddress, getSeaportOrderFromMessageData, getTotalConsiderationAmount, getWrappedNativeCurrency, isBazaarSupportedOnChain, isCollectionOfferValid, isErc20ListingValid, isErc20OfferValid, isListingValid, parseCollectionOfferFromMessage, parseErc20ListingFromMessage, parseErc20OfferFromMessage, parseListingFromMessage, sortErc20ListingsByPricePerToken, sortErc20OffersByPricePerToken, sortListingsByPrice, sortOffersByPrice };
1317
1487
  //# sourceMappingURL=index.mjs.map
1318
1488
  //# sourceMappingURL=index.mjs.map