@net-protocol/bazaar 0.1.0 → 0.1.2
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 +52 -3
- package/dist/index.d.ts +52 -3
- package/dist/index.js +203 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +199 -16
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +76 -3
- package/dist/react.d.ts +76 -3
- package/dist/react.js +320 -27
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +320 -28
- package/dist/react.mjs.map +1 -1
- package/dist/{types-CY-6M9Ta.d.mts → types-KQgBECzI.d.mts} +57 -1
- package/dist/{types-CY-6M9Ta.d.ts → types-KQgBECzI.d.ts} +57 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, W as WriteTransactionConfig, S as SeaportOrderComponents,
|
|
2
|
-
export {
|
|
1
|
+
import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, c as GetErc20ListingsOptions, d as Erc20Listing, W as WriteTransactionConfig, S as SeaportOrderComponents, e as SeaportSubmission, f as SeaportOrderParameters, g as SeaportOrderStatusInfo, h as SeaportOrderStatus } from './types-KQgBECzI.mjs';
|
|
2
|
+
export { j as ConsiderationItem, l as CreateCollectionOfferParams, k as CreateListingParams, I as ItemType, i as OfferItem, O as OrderType } from './types-KQgBECzI.mjs';
|
|
3
3
|
import { Seaport } from '@opensea/seaport-js';
|
|
4
4
|
import { PublicClient } from 'viem';
|
|
5
5
|
import { NetMessage } from '@net-protocol/core';
|
|
@@ -56,6 +56,18 @@ declare class BazaarClient {
|
|
|
56
56
|
* Results are sorted by price per token (highest first)
|
|
57
57
|
*/
|
|
58
58
|
getErc20Offers(options: GetErc20OffersOptions): Promise<Erc20Offer[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Get valid ERC20 listings for a token
|
|
61
|
+
*
|
|
62
|
+
* Returns listings that are:
|
|
63
|
+
* - OPEN status (not filled, cancelled, or expired)
|
|
64
|
+
* - Not expired
|
|
65
|
+
* - Seller has sufficient ERC20 token balance
|
|
66
|
+
*
|
|
67
|
+
* Results are sorted by price per token (lowest first). No deduplication —
|
|
68
|
+
* all valid listings are returned (grouped by maker in the UI).
|
|
69
|
+
*/
|
|
70
|
+
getErc20Listings(options: GetErc20ListingsOptions): Promise<Erc20Listing[]>;
|
|
59
71
|
/**
|
|
60
72
|
* Get the chain ID this client is configured for
|
|
61
73
|
*/
|
|
@@ -73,6 +85,10 @@ declare class BazaarClient {
|
|
|
73
85
|
* Only available on Base (8453) and HyperEVM (999)
|
|
74
86
|
*/
|
|
75
87
|
getErc20OffersAddress(): `0x${string}` | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Get the ERC20 bazaar (listings) contract address for this chain
|
|
90
|
+
*/
|
|
91
|
+
getErc20BazaarAddress(): `0x${string}`;
|
|
76
92
|
/**
|
|
77
93
|
* Get the Seaport contract address for this chain
|
|
78
94
|
*/
|
|
@@ -91,6 +107,13 @@ declare class BazaarClient {
|
|
|
91
107
|
* Use the orderComponents from the CollectionOffer object returned by getCollectionOffers().
|
|
92
108
|
*/
|
|
93
109
|
prepareCancelCollectionOffer(offer: CollectionOffer): WriteTransactionConfig;
|
|
110
|
+
/**
|
|
111
|
+
* Prepare a transaction to cancel an ERC20 listing
|
|
112
|
+
*
|
|
113
|
+
* The listing must have been created by the caller.
|
|
114
|
+
* Use the orderComponents from the Erc20Listing object returned by getErc20Listings().
|
|
115
|
+
*/
|
|
116
|
+
prepareCancelErc20Listing(listing: Erc20Listing): WriteTransactionConfig;
|
|
94
117
|
/**
|
|
95
118
|
* Prepare a transaction to cancel a Seaport order
|
|
96
119
|
*
|
|
@@ -116,6 +139,8 @@ interface BazaarChainConfig {
|
|
|
116
139
|
collectionOffersAddress: `0x${string}`;
|
|
117
140
|
/** ERC20 offers contract (only on Base and HyperEVM) */
|
|
118
141
|
erc20OffersAddress?: `0x${string}`;
|
|
142
|
+
/** ERC20 listings contract */
|
|
143
|
+
erc20BazaarAddress?: `0x${string}`;
|
|
119
144
|
/** Seaport contract address */
|
|
120
145
|
seaportAddress: `0x${string}`;
|
|
121
146
|
/** Fee collector address */
|
|
@@ -134,6 +159,7 @@ declare const ERC721_OWNER_OF_HELPER_ADDRESS: "0x000000aa4eFa2e5A4a6002C7F08B6e8
|
|
|
134
159
|
declare const ERC20_BULK_BALANCE_CHECKER_ADDRESS: "0x000000b50a9f2923f2db931391824f6d1278f712";
|
|
135
160
|
declare const NET_SEAPORT_ZONE_ADDRESS: "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
|
|
136
161
|
declare const NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS: "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
|
|
162
|
+
declare const NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS: "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
|
|
137
163
|
/**
|
|
138
164
|
* Get bazaar configuration for a chain
|
|
139
165
|
*/
|
|
@@ -183,6 +209,10 @@ declare function getHighEthAddress(chainId: number): `0x${string}` | undefined;
|
|
|
183
209
|
* Only deployed on Base (8453) and HyperEVM (999)
|
|
184
210
|
*/
|
|
185
211
|
declare function getErc20OffersAddress(chainId: number): `0x${string}` | undefined;
|
|
212
|
+
/**
|
|
213
|
+
* Get ERC20 bazaar (listings) contract address for a chain
|
|
214
|
+
*/
|
|
215
|
+
declare function getErc20BazaarAddress(chainId: number): `0x${string}`;
|
|
186
216
|
|
|
187
217
|
declare const BAZAAR_V2_ABI: readonly [{
|
|
188
218
|
readonly type: "function";
|
|
@@ -880,6 +910,13 @@ declare function isCollectionOfferValid(orderStatus: SeaportOrderStatus, expirat
|
|
|
880
910
|
* - Buyer has sufficient WETH balance
|
|
881
911
|
*/
|
|
882
912
|
declare function isErc20OfferValid(orderStatus: SeaportOrderStatus, expirationDate: number, priceWei: bigint, buyerWethBalance: bigint): boolean;
|
|
913
|
+
/**
|
|
914
|
+
* Validate that an ERC20 listing is still valid:
|
|
915
|
+
* - Order is OPEN
|
|
916
|
+
* - Not expired
|
|
917
|
+
* - Seller has sufficient ERC20 token balance
|
|
918
|
+
*/
|
|
919
|
+
declare function isErc20ListingValid(orderStatus: SeaportOrderStatus, expirationDate: number, tokenAmount: bigint, sellerTokenBalance: bigint): boolean;
|
|
883
920
|
|
|
884
921
|
/**
|
|
885
922
|
* Utilities for parsing Net messages into listings and offers
|
|
@@ -917,5 +954,17 @@ declare function parseErc20OfferFromMessage(message: NetMessage, chainId: number
|
|
|
917
954
|
* Sort ERC20 offers by price per token (highest first)
|
|
918
955
|
*/
|
|
919
956
|
declare function sortErc20OffersByPricePerToken(offers: Erc20Offer[]): Erc20Offer[];
|
|
957
|
+
/**
|
|
958
|
+
* Parse a Net message into an ERC20 listing
|
|
959
|
+
*
|
|
960
|
+
* ERC20 listings have the ERC20 token in the offer array (seller is offering tokens)
|
|
961
|
+
* and native currency payments in the consideration array.
|
|
962
|
+
* They do NOT use the collection offer zone (that would be an offer, not a listing).
|
|
963
|
+
*/
|
|
964
|
+
declare function parseErc20ListingFromMessage(message: NetMessage, chainId: number): Erc20Listing | null;
|
|
965
|
+
/**
|
|
966
|
+
* Sort ERC20 listings by price per token (lowest first)
|
|
967
|
+
*/
|
|
968
|
+
declare function sortErc20ListingsByPricePerToken(listings: Erc20Listing[]): Erc20Listing[];
|
|
920
969
|
|
|
921
|
-
export { BAZAAR_COLLECTION_OFFERS_ABI, BAZAAR_SUBMISSION_ABI, BAZAAR_V2_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS, type BazaarChainConfig, BazaarClient, CollectionOffer, ERC20_BULK_BALANCE_CHECKER_ABI, ERC20_BULK_BALANCE_CHECKER_ADDRESS, ERC721_OWNER_OF_HELPER_ABI, ERC721_OWNER_OF_HELPER_ADDRESS, Erc20Offer, GetCollectionOffersOptions, GetErc20OffersOptions, GetListingsOptions, Listing, NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS, NET_SEAPORT_ZONE_ADDRESS, SEAPORT_CANCEL_ABI, SeaportOrderComponents, SeaportOrderParameters, SeaportOrderStatus, SeaportOrderStatusInfo, SeaportSubmission, type WrappedNativeCurrency, WriteTransactionConfig, 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 };
|
|
970
|
+
export { BAZAAR_COLLECTION_OFFERS_ABI, BAZAAR_SUBMISSION_ABI, BAZAAR_V2_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS, type BazaarChainConfig, BazaarClient, CollectionOffer, ERC20_BULK_BALANCE_CHECKER_ABI, ERC20_BULK_BALANCE_CHECKER_ADDRESS, ERC721_OWNER_OF_HELPER_ABI, ERC721_OWNER_OF_HELPER_ADDRESS, Erc20Listing, Erc20Offer, GetCollectionOffersOptions, GetErc20ListingsOptions, GetErc20OffersOptions, GetListingsOptions, Listing, NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS, NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS, NET_SEAPORT_ZONE_ADDRESS, SEAPORT_CANCEL_ABI, SeaportOrderComponents, SeaportOrderParameters, SeaportOrderStatus, SeaportOrderStatusInfo, SeaportSubmission, type WrappedNativeCurrency, WriteTransactionConfig, 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, W as WriteTransactionConfig, S as SeaportOrderComponents,
|
|
2
|
-
export {
|
|
1
|
+
import { G as GetListingsOptions, L as Listing, a as GetCollectionOffersOptions, C as CollectionOffer, b as GetErc20OffersOptions, E as Erc20Offer, c as GetErc20ListingsOptions, d as Erc20Listing, W as WriteTransactionConfig, S as SeaportOrderComponents, e as SeaportSubmission, f as SeaportOrderParameters, g as SeaportOrderStatusInfo, h as SeaportOrderStatus } from './types-KQgBECzI.js';
|
|
2
|
+
export { j as ConsiderationItem, l as CreateCollectionOfferParams, k as CreateListingParams, I as ItemType, i as OfferItem, O as OrderType } from './types-KQgBECzI.js';
|
|
3
3
|
import { Seaport } from '@opensea/seaport-js';
|
|
4
4
|
import { PublicClient } from 'viem';
|
|
5
5
|
import { NetMessage } from '@net-protocol/core';
|
|
@@ -56,6 +56,18 @@ declare class BazaarClient {
|
|
|
56
56
|
* Results are sorted by price per token (highest first)
|
|
57
57
|
*/
|
|
58
58
|
getErc20Offers(options: GetErc20OffersOptions): Promise<Erc20Offer[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Get valid ERC20 listings for a token
|
|
61
|
+
*
|
|
62
|
+
* Returns listings that are:
|
|
63
|
+
* - OPEN status (not filled, cancelled, or expired)
|
|
64
|
+
* - Not expired
|
|
65
|
+
* - Seller has sufficient ERC20 token balance
|
|
66
|
+
*
|
|
67
|
+
* Results are sorted by price per token (lowest first). No deduplication —
|
|
68
|
+
* all valid listings are returned (grouped by maker in the UI).
|
|
69
|
+
*/
|
|
70
|
+
getErc20Listings(options: GetErc20ListingsOptions): Promise<Erc20Listing[]>;
|
|
59
71
|
/**
|
|
60
72
|
* Get the chain ID this client is configured for
|
|
61
73
|
*/
|
|
@@ -73,6 +85,10 @@ declare class BazaarClient {
|
|
|
73
85
|
* Only available on Base (8453) and HyperEVM (999)
|
|
74
86
|
*/
|
|
75
87
|
getErc20OffersAddress(): `0x${string}` | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Get the ERC20 bazaar (listings) contract address for this chain
|
|
90
|
+
*/
|
|
91
|
+
getErc20BazaarAddress(): `0x${string}`;
|
|
76
92
|
/**
|
|
77
93
|
* Get the Seaport contract address for this chain
|
|
78
94
|
*/
|
|
@@ -91,6 +107,13 @@ declare class BazaarClient {
|
|
|
91
107
|
* Use the orderComponents from the CollectionOffer object returned by getCollectionOffers().
|
|
92
108
|
*/
|
|
93
109
|
prepareCancelCollectionOffer(offer: CollectionOffer): WriteTransactionConfig;
|
|
110
|
+
/**
|
|
111
|
+
* Prepare a transaction to cancel an ERC20 listing
|
|
112
|
+
*
|
|
113
|
+
* The listing must have been created by the caller.
|
|
114
|
+
* Use the orderComponents from the Erc20Listing object returned by getErc20Listings().
|
|
115
|
+
*/
|
|
116
|
+
prepareCancelErc20Listing(listing: Erc20Listing): WriteTransactionConfig;
|
|
94
117
|
/**
|
|
95
118
|
* Prepare a transaction to cancel a Seaport order
|
|
96
119
|
*
|
|
@@ -116,6 +139,8 @@ interface BazaarChainConfig {
|
|
|
116
139
|
collectionOffersAddress: `0x${string}`;
|
|
117
140
|
/** ERC20 offers contract (only on Base and HyperEVM) */
|
|
118
141
|
erc20OffersAddress?: `0x${string}`;
|
|
142
|
+
/** ERC20 listings contract */
|
|
143
|
+
erc20BazaarAddress?: `0x${string}`;
|
|
119
144
|
/** Seaport contract address */
|
|
120
145
|
seaportAddress: `0x${string}`;
|
|
121
146
|
/** Fee collector address */
|
|
@@ -134,6 +159,7 @@ declare const ERC721_OWNER_OF_HELPER_ADDRESS: "0x000000aa4eFa2e5A4a6002C7F08B6e8
|
|
|
134
159
|
declare const ERC20_BULK_BALANCE_CHECKER_ADDRESS: "0x000000b50a9f2923f2db931391824f6d1278f712";
|
|
135
160
|
declare const NET_SEAPORT_ZONE_ADDRESS: "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
|
|
136
161
|
declare const NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS: "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
|
|
162
|
+
declare const NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS: "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
|
|
137
163
|
/**
|
|
138
164
|
* Get bazaar configuration for a chain
|
|
139
165
|
*/
|
|
@@ -183,6 +209,10 @@ declare function getHighEthAddress(chainId: number): `0x${string}` | undefined;
|
|
|
183
209
|
* Only deployed on Base (8453) and HyperEVM (999)
|
|
184
210
|
*/
|
|
185
211
|
declare function getErc20OffersAddress(chainId: number): `0x${string}` | undefined;
|
|
212
|
+
/**
|
|
213
|
+
* Get ERC20 bazaar (listings) contract address for a chain
|
|
214
|
+
*/
|
|
215
|
+
declare function getErc20BazaarAddress(chainId: number): `0x${string}`;
|
|
186
216
|
|
|
187
217
|
declare const BAZAAR_V2_ABI: readonly [{
|
|
188
218
|
readonly type: "function";
|
|
@@ -880,6 +910,13 @@ declare function isCollectionOfferValid(orderStatus: SeaportOrderStatus, expirat
|
|
|
880
910
|
* - Buyer has sufficient WETH balance
|
|
881
911
|
*/
|
|
882
912
|
declare function isErc20OfferValid(orderStatus: SeaportOrderStatus, expirationDate: number, priceWei: bigint, buyerWethBalance: bigint): boolean;
|
|
913
|
+
/**
|
|
914
|
+
* Validate that an ERC20 listing is still valid:
|
|
915
|
+
* - Order is OPEN
|
|
916
|
+
* - Not expired
|
|
917
|
+
* - Seller has sufficient ERC20 token balance
|
|
918
|
+
*/
|
|
919
|
+
declare function isErc20ListingValid(orderStatus: SeaportOrderStatus, expirationDate: number, tokenAmount: bigint, sellerTokenBalance: bigint): boolean;
|
|
883
920
|
|
|
884
921
|
/**
|
|
885
922
|
* Utilities for parsing Net messages into listings and offers
|
|
@@ -917,5 +954,17 @@ declare function parseErc20OfferFromMessage(message: NetMessage, chainId: number
|
|
|
917
954
|
* Sort ERC20 offers by price per token (highest first)
|
|
918
955
|
*/
|
|
919
956
|
declare function sortErc20OffersByPricePerToken(offers: Erc20Offer[]): Erc20Offer[];
|
|
957
|
+
/**
|
|
958
|
+
* Parse a Net message into an ERC20 listing
|
|
959
|
+
*
|
|
960
|
+
* ERC20 listings have the ERC20 token in the offer array (seller is offering tokens)
|
|
961
|
+
* and native currency payments in the consideration array.
|
|
962
|
+
* They do NOT use the collection offer zone (that would be an offer, not a listing).
|
|
963
|
+
*/
|
|
964
|
+
declare function parseErc20ListingFromMessage(message: NetMessage, chainId: number): Erc20Listing | null;
|
|
965
|
+
/**
|
|
966
|
+
* Sort ERC20 listings by price per token (lowest first)
|
|
967
|
+
*/
|
|
968
|
+
declare function sortErc20ListingsByPricePerToken(listings: Erc20Listing[]): Erc20Listing[];
|
|
920
969
|
|
|
921
|
-
export { BAZAAR_COLLECTION_OFFERS_ABI, BAZAAR_SUBMISSION_ABI, BAZAAR_V2_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS, type BazaarChainConfig, BazaarClient, CollectionOffer, ERC20_BULK_BALANCE_CHECKER_ABI, ERC20_BULK_BALANCE_CHECKER_ADDRESS, ERC721_OWNER_OF_HELPER_ABI, ERC721_OWNER_OF_HELPER_ADDRESS, Erc20Offer, GetCollectionOffersOptions, GetErc20OffersOptions, GetListingsOptions, Listing, NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS, NET_SEAPORT_ZONE_ADDRESS, SEAPORT_CANCEL_ABI, SeaportOrderComponents, SeaportOrderParameters, SeaportOrderStatus, SeaportOrderStatusInfo, SeaportSubmission, type WrappedNativeCurrency, WriteTransactionConfig, 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 };
|
|
970
|
+
export { BAZAAR_COLLECTION_OFFERS_ABI, BAZAAR_SUBMISSION_ABI, BAZAAR_V2_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ABI, BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS, type BazaarChainConfig, BazaarClient, CollectionOffer, ERC20_BULK_BALANCE_CHECKER_ABI, ERC20_BULK_BALANCE_CHECKER_ADDRESS, ERC721_OWNER_OF_HELPER_ABI, ERC721_OWNER_OF_HELPER_ADDRESS, Erc20Listing, Erc20Offer, GetCollectionOffersOptions, GetErc20ListingsOptions, GetErc20OffersOptions, GetListingsOptions, Listing, NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS, NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS, NET_SEAPORT_ZONE_ADDRESS, SEAPORT_CANCEL_ABI, SeaportOrderComponents, SeaportOrderParameters, SeaportOrderStatus, SeaportOrderStatusInfo, SeaportSubmission, type WrappedNativeCurrency, WriteTransactionConfig, 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 };
|
package/dist/index.js
CHANGED
|
@@ -356,18 +356,21 @@ var DEFAULT_SEAPORT_ADDRESS = "0x0000000000000068F116a894984e2DB1123eB395";
|
|
|
356
356
|
var DEFAULT_BAZAAR_ADDRESS = "0x00000000E3dA5fC031282A39759bDDA78ae7fAE5";
|
|
357
357
|
var DEFAULT_COLLECTION_OFFERS_ADDRESS = "0x0000000D43423E0A12CecB307a74591999b32B32";
|
|
358
358
|
var DEFAULT_FEE_COLLECTOR_ADDRESS = "0x32D16C15410248bef498D7aF50D10Db1a546b9E5";
|
|
359
|
+
var DEFAULT_ERC20_BAZAAR_ADDRESS = "0x00000000a2d173a4610c85c7471a25b6bc216a70";
|
|
359
360
|
var DEFAULT_NFT_FEE_BPS = 500;
|
|
360
361
|
var BULK_SEAPORT_ORDER_STATUS_FETCHER_ADDRESS = "0x0000009112ABCE652674b4fE3eD9C765B22d11A7";
|
|
361
362
|
var ERC721_OWNER_OF_HELPER_ADDRESS = "0x000000aa4eFa2e5A4a6002C7F08B6e8Ec8cf1dDa";
|
|
362
363
|
var ERC20_BULK_BALANCE_CHECKER_ADDRESS = "0x000000b50a9f2923f2db931391824f6d1278f712";
|
|
363
364
|
var NET_SEAPORT_ZONE_ADDRESS = "0x000000007F8c58fbf215bF91Bda7421A806cf3ae";
|
|
364
365
|
var NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS = "0x000000B799ec6D7aCC1B578f62bFc324c25DFC5A";
|
|
366
|
+
var NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS = "0x000000bC63761cbb05305632212e2f3AE2BE7a9B";
|
|
365
367
|
var BAZAAR_CHAIN_CONFIGS = {
|
|
366
368
|
// Base Mainnet
|
|
367
369
|
8453: {
|
|
368
370
|
bazaarAddress: "0x000000058f3ade587388daf827174d0e6fc97595",
|
|
369
371
|
collectionOffersAddress: "0x0000000f9c45efcff0f78d8b54aa6a40092d66dc",
|
|
370
372
|
erc20OffersAddress: "0x0000000e23a89aa06f317306aa1ae231d3503082",
|
|
373
|
+
erc20BazaarAddress: "0x00000006557e3629e2fc50bbad0c002b27cac492",
|
|
371
374
|
seaportAddress: DEFAULT_SEAPORT_ADDRESS,
|
|
372
375
|
feeCollectorAddress: "0x66547ff4f7206e291F7BC157b54C026Fc6660961",
|
|
373
376
|
nftFeeBps: 0,
|
|
@@ -457,6 +460,7 @@ var BAZAAR_CHAIN_CONFIGS = {
|
|
|
457
460
|
bazaarAddress: "0x000000058f3ade587388daf827174d0e6fc97595",
|
|
458
461
|
collectionOffersAddress: "0x0000000f9c45efcff0f78d8b54aa6a40092d66dc",
|
|
459
462
|
erc20OffersAddress: "0x0000000e23a89aa06f317306aa1ae231d3503082",
|
|
463
|
+
erc20BazaarAddress: "0x00000006557e3629e2fc50bbad0c002b27cac492",
|
|
460
464
|
seaportAddress: DEFAULT_SEAPORT_ADDRESS,
|
|
461
465
|
feeCollectorAddress: "0x66547ff4f7206e291F7BC157b54C026Fc6660961",
|
|
462
466
|
nftFeeBps: 0,
|
|
@@ -533,6 +537,9 @@ function getHighEthAddress(chainId) {
|
|
|
533
537
|
function getErc20OffersAddress(chainId) {
|
|
534
538
|
return BAZAAR_CHAIN_CONFIGS[chainId]?.erc20OffersAddress;
|
|
535
539
|
}
|
|
540
|
+
function getErc20BazaarAddress(chainId) {
|
|
541
|
+
return BAZAAR_CHAIN_CONFIGS[chainId]?.erc20BazaarAddress ?? DEFAULT_ERC20_BAZAAR_ADDRESS;
|
|
542
|
+
}
|
|
536
543
|
function decodeSeaportSubmission(messageData) {
|
|
537
544
|
const [decoded] = viem.decodeAbiParameters(BAZAAR_SUBMISSION_ABI, messageData);
|
|
538
545
|
return {
|
|
@@ -754,6 +761,19 @@ function isErc20OfferValid(orderStatus, expirationDate, priceWei, buyerWethBalan
|
|
|
754
761
|
}
|
|
755
762
|
return true;
|
|
756
763
|
}
|
|
764
|
+
function isErc20ListingValid(orderStatus, expirationDate, tokenAmount, sellerTokenBalance) {
|
|
765
|
+
if (orderStatus !== 2 /* OPEN */) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
769
|
+
if (expirationDate <= now) {
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
if (sellerTokenBalance < tokenAmount) {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
757
777
|
|
|
758
778
|
// src/utils/parsing.ts
|
|
759
779
|
function parseListingFromMessage(message, chainId) {
|
|
@@ -769,6 +789,7 @@ function parseListingFromMessage(message, chainId) {
|
|
|
769
789
|
}
|
|
770
790
|
const priceWei = getTotalConsiderationAmount(parameters);
|
|
771
791
|
const tokenId = offerItem.identifierOrCriteria.toString();
|
|
792
|
+
const targetFulfiller = parameters.zone.toLowerCase() === NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS.toLowerCase() && parameters.zoneHash !== "0x0000000000000000000000000000000000000000000000000000000000000000" ? parameters.zoneHash : void 0;
|
|
772
793
|
return {
|
|
773
794
|
maker: parameters.offerer,
|
|
774
795
|
nftAddress: offerItem.token,
|
|
@@ -785,7 +806,8 @@ function parseListingFromMessage(message, chainId) {
|
|
|
785
806
|
orderComponents: {
|
|
786
807
|
...parameters,
|
|
787
808
|
counter: submission.counter
|
|
788
|
-
}
|
|
809
|
+
},
|
|
810
|
+
targetFulfiller
|
|
789
811
|
};
|
|
790
812
|
} catch {
|
|
791
813
|
return null;
|
|
@@ -923,6 +945,58 @@ function sortErc20OffersByPricePerToken(offers) {
|
|
|
923
945
|
return 0;
|
|
924
946
|
});
|
|
925
947
|
}
|
|
948
|
+
function parseErc20ListingFromMessage(message, chainId) {
|
|
949
|
+
try {
|
|
950
|
+
const submission = decodeSeaportSubmission(message.data);
|
|
951
|
+
const { parameters } = submission;
|
|
952
|
+
if (parameters.zone.toLowerCase() === NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS.toLowerCase()) {
|
|
953
|
+
return null;
|
|
954
|
+
}
|
|
955
|
+
const offerItem = parameters.offer[0];
|
|
956
|
+
if (!offerItem || offerItem.itemType !== 1 /* ERC20 */) {
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
const tokenAmount = offerItem.startAmount;
|
|
960
|
+
if (tokenAmount === BigInt(0)) {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
const priceWei = getTotalConsiderationAmount(parameters);
|
|
964
|
+
if (priceWei === BigInt(0)) {
|
|
965
|
+
return null;
|
|
966
|
+
}
|
|
967
|
+
const pricePerTokenWei = priceWei / tokenAmount;
|
|
968
|
+
return {
|
|
969
|
+
maker: parameters.offerer,
|
|
970
|
+
tokenAddress: offerItem.token,
|
|
971
|
+
tokenAmount,
|
|
972
|
+
priceWei,
|
|
973
|
+
pricePerTokenWei,
|
|
974
|
+
price: formatPrice(priceWei),
|
|
975
|
+
pricePerToken: formatPrice(pricePerTokenWei),
|
|
976
|
+
currency: getCurrencySymbol(chainId),
|
|
977
|
+
expirationDate: Number(parameters.endTime),
|
|
978
|
+
orderHash: "0x",
|
|
979
|
+
// Will be computed later
|
|
980
|
+
orderStatus: 2 /* OPEN */,
|
|
981
|
+
// Will be validated later
|
|
982
|
+
messageData: message.data,
|
|
983
|
+
orderComponents: {
|
|
984
|
+
...parameters,
|
|
985
|
+
counter: submission.counter
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
} catch {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function sortErc20ListingsByPricePerToken(listings) {
|
|
993
|
+
return [...listings].sort((a, b) => {
|
|
994
|
+
const diff = a.pricePerTokenWei - b.pricePerTokenWei;
|
|
995
|
+
if (diff < BigInt(0)) return -1;
|
|
996
|
+
if (diff > BigInt(0)) return 1;
|
|
997
|
+
return 0;
|
|
998
|
+
});
|
|
999
|
+
}
|
|
926
1000
|
|
|
927
1001
|
// src/client/BazaarClient.ts
|
|
928
1002
|
var CHAIN_RPC_URLS = {
|
|
@@ -979,25 +1053,30 @@ var BazaarClient = class {
|
|
|
979
1053
|
* Results are deduplicated (one per token) and sorted by price (lowest first)
|
|
980
1054
|
*/
|
|
981
1055
|
async getListings(options) {
|
|
982
|
-
const { nftAddress, excludeMaker, maxMessages = 200 } = options;
|
|
1056
|
+
const { nftAddress, excludeMaker, maker, maxMessages = 200 } = options;
|
|
983
1057
|
const bazaarAddress = getBazaarAddress(this.chainId);
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1058
|
+
const filter = {
|
|
1059
|
+
appAddress: bazaarAddress,
|
|
1060
|
+
topic: nftAddress.toLowerCase(),
|
|
1061
|
+
maker
|
|
1062
|
+
};
|
|
1063
|
+
let startIndex;
|
|
1064
|
+
let endIndex;
|
|
1065
|
+
if (options.startIndex != null && options.endIndex != null) {
|
|
1066
|
+
startIndex = options.startIndex;
|
|
1067
|
+
endIndex = options.endIndex;
|
|
1068
|
+
} else {
|
|
1069
|
+
const count = await this.netClient.getMessageCount({ filter });
|
|
1070
|
+
if (count === 0) {
|
|
1071
|
+
return [];
|
|
988
1072
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
return [];
|
|
1073
|
+
startIndex = Math.max(0, count - maxMessages);
|
|
1074
|
+
endIndex = count;
|
|
992
1075
|
}
|
|
993
|
-
const startIndex = Math.max(0, count - maxMessages);
|
|
994
1076
|
const messages = await this.netClient.getMessages({
|
|
995
|
-
filter
|
|
996
|
-
appAddress: bazaarAddress,
|
|
997
|
-
topic: nftAddress.toLowerCase()
|
|
998
|
-
},
|
|
1077
|
+
filter,
|
|
999
1078
|
startIndex,
|
|
1000
|
-
endIndex
|
|
1079
|
+
endIndex
|
|
1001
1080
|
});
|
|
1002
1081
|
let listings = [];
|
|
1003
1082
|
for (const message of messages) {
|
|
@@ -1218,6 +1297,92 @@ var BazaarClient = class {
|
|
|
1218
1297
|
});
|
|
1219
1298
|
return sortErc20OffersByPricePerToken(offers);
|
|
1220
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Get valid ERC20 listings for a token
|
|
1302
|
+
*
|
|
1303
|
+
* Returns listings that are:
|
|
1304
|
+
* - OPEN status (not filled, cancelled, or expired)
|
|
1305
|
+
* - Not expired
|
|
1306
|
+
* - Seller has sufficient ERC20 token balance
|
|
1307
|
+
*
|
|
1308
|
+
* Results are sorted by price per token (lowest first). No deduplication —
|
|
1309
|
+
* all valid listings are returned (grouped by maker in the UI).
|
|
1310
|
+
*/
|
|
1311
|
+
async getErc20Listings(options) {
|
|
1312
|
+
const { tokenAddress, excludeMaker, maker, maxMessages = 200 } = options;
|
|
1313
|
+
const erc20BazaarAddress = getErc20BazaarAddress(this.chainId);
|
|
1314
|
+
const filter = {
|
|
1315
|
+
appAddress: erc20BazaarAddress,
|
|
1316
|
+
topic: tokenAddress.toLowerCase(),
|
|
1317
|
+
maker
|
|
1318
|
+
};
|
|
1319
|
+
let startIndex;
|
|
1320
|
+
let endIndex;
|
|
1321
|
+
if (options.startIndex != null && options.endIndex != null) {
|
|
1322
|
+
startIndex = options.startIndex;
|
|
1323
|
+
endIndex = options.endIndex;
|
|
1324
|
+
} else {
|
|
1325
|
+
const count = await this.netClient.getMessageCount({ filter });
|
|
1326
|
+
if (count === 0) {
|
|
1327
|
+
return [];
|
|
1328
|
+
}
|
|
1329
|
+
startIndex = Math.max(0, count - maxMessages);
|
|
1330
|
+
endIndex = count;
|
|
1331
|
+
}
|
|
1332
|
+
const messages = await this.netClient.getMessages({
|
|
1333
|
+
filter,
|
|
1334
|
+
startIndex,
|
|
1335
|
+
endIndex
|
|
1336
|
+
});
|
|
1337
|
+
let listings = [];
|
|
1338
|
+
for (const message of messages) {
|
|
1339
|
+
const listing = parseErc20ListingFromMessage(message, this.chainId);
|
|
1340
|
+
if (!listing) continue;
|
|
1341
|
+
if (listing.tokenAddress.toLowerCase() !== tokenAddress.toLowerCase()) {
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
if (excludeMaker && listing.maker.toLowerCase() === excludeMaker.toLowerCase()) {
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
listings.push(listing);
|
|
1348
|
+
}
|
|
1349
|
+
if (listings.length === 0) {
|
|
1350
|
+
return [];
|
|
1351
|
+
}
|
|
1352
|
+
const seaport = createSeaportInstance(this.chainId, this.rpcUrl);
|
|
1353
|
+
for (const listing of listings) {
|
|
1354
|
+
const order = getSeaportOrderFromMessageData(listing.messageData);
|
|
1355
|
+
listing.orderHash = computeOrderHash(seaport, order.parameters, order.counter);
|
|
1356
|
+
}
|
|
1357
|
+
const orderHashes = listings.map((l) => l.orderHash);
|
|
1358
|
+
const statusInfos = await bulkFetchOrderStatuses(this.client, this.chainId, orderHashes);
|
|
1359
|
+
listings.forEach((listing, index) => {
|
|
1360
|
+
const statusInfo = statusInfos[index];
|
|
1361
|
+
listing.orderStatus = getOrderStatusFromInfo(listing.orderComponents, statusInfo);
|
|
1362
|
+
});
|
|
1363
|
+
listings = listings.filter(
|
|
1364
|
+
(l) => l.orderStatus === 2 /* OPEN */ && l.expirationDate > Math.floor(Date.now() / 1e3)
|
|
1365
|
+
);
|
|
1366
|
+
if (listings.length === 0) {
|
|
1367
|
+
return [];
|
|
1368
|
+
}
|
|
1369
|
+
const uniqueMakers = [...new Set(listings.map((l) => l.maker))];
|
|
1370
|
+
const balances = await bulkFetchErc20Balances(this.client, tokenAddress, uniqueMakers);
|
|
1371
|
+
const balanceMap = /* @__PURE__ */ new Map();
|
|
1372
|
+
uniqueMakers.forEach((maker2, index) => {
|
|
1373
|
+
balanceMap.set(maker2.toLowerCase(), balances[index]);
|
|
1374
|
+
});
|
|
1375
|
+
listings = listings.filter((listing) => {
|
|
1376
|
+
const balance = balanceMap.get(listing.maker.toLowerCase()) || BigInt(0);
|
|
1377
|
+
return isErc20ListingValid(
|
|
1378
|
+
listing.orderStatus,
|
|
1379
|
+
listing.expirationDate,
|
|
1380
|
+
listing.tokenAmount,
|
|
1381
|
+
balance
|
|
1382
|
+
);
|
|
1383
|
+
});
|
|
1384
|
+
return sortErc20ListingsByPricePerToken(listings);
|
|
1385
|
+
}
|
|
1221
1386
|
/**
|
|
1222
1387
|
* Get the chain ID this client is configured for
|
|
1223
1388
|
*/
|
|
@@ -1243,6 +1408,12 @@ var BazaarClient = class {
|
|
|
1243
1408
|
getErc20OffersAddress() {
|
|
1244
1409
|
return getErc20OffersAddress(this.chainId);
|
|
1245
1410
|
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Get the ERC20 bazaar (listings) contract address for this chain
|
|
1413
|
+
*/
|
|
1414
|
+
getErc20BazaarAddress() {
|
|
1415
|
+
return getErc20BazaarAddress(this.chainId);
|
|
1416
|
+
}
|
|
1246
1417
|
/**
|
|
1247
1418
|
* Get the Seaport contract address for this chain
|
|
1248
1419
|
*/
|
|
@@ -1273,6 +1444,18 @@ var BazaarClient = class {
|
|
|
1273
1444
|
}
|
|
1274
1445
|
return this.prepareCancelOrder(offer.orderComponents);
|
|
1275
1446
|
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Prepare a transaction to cancel an ERC20 listing
|
|
1449
|
+
*
|
|
1450
|
+
* The listing must have been created by the caller.
|
|
1451
|
+
* Use the orderComponents from the Erc20Listing object returned by getErc20Listings().
|
|
1452
|
+
*/
|
|
1453
|
+
prepareCancelErc20Listing(listing) {
|
|
1454
|
+
if (!listing.orderComponents) {
|
|
1455
|
+
throw new Error("Listing does not have order components");
|
|
1456
|
+
}
|
|
1457
|
+
return this.prepareCancelOrder(listing.orderComponents);
|
|
1458
|
+
}
|
|
1276
1459
|
/**
|
|
1277
1460
|
* Prepare a transaction to cancel a Seaport order
|
|
1278
1461
|
*
|
|
@@ -1327,6 +1510,7 @@ exports.ERC721_OWNER_OF_HELPER_ABI = ERC721_OWNER_OF_HELPER_ABI;
|
|
|
1327
1510
|
exports.ERC721_OWNER_OF_HELPER_ADDRESS = ERC721_OWNER_OF_HELPER_ADDRESS;
|
|
1328
1511
|
exports.ItemType = ItemType;
|
|
1329
1512
|
exports.NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS = NET_SEAPORT_COLLECTION_OFFER_ZONE_ADDRESS;
|
|
1513
|
+
exports.NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS = NET_SEAPORT_PRIVATE_ORDER_ZONE_ADDRESS;
|
|
1330
1514
|
exports.NET_SEAPORT_ZONE_ADDRESS = NET_SEAPORT_ZONE_ADDRESS;
|
|
1331
1515
|
exports.OrderType = OrderType;
|
|
1332
1516
|
exports.SEAPORT_CANCEL_ABI = SEAPORT_CANCEL_ABI;
|
|
@@ -1348,6 +1532,7 @@ exports.getBestCollectionOffer = getBestCollectionOffer;
|
|
|
1348
1532
|
exports.getBestListingPerToken = getBestListingPerToken;
|
|
1349
1533
|
exports.getCollectionOffersAddress = getCollectionOffersAddress;
|
|
1350
1534
|
exports.getCurrencySymbol = getCurrencySymbol;
|
|
1535
|
+
exports.getErc20BazaarAddress = getErc20BazaarAddress;
|
|
1351
1536
|
exports.getErc20OffersAddress = getErc20OffersAddress;
|
|
1352
1537
|
exports.getFeeCollectorAddress = getFeeCollectorAddress;
|
|
1353
1538
|
exports.getHighEthAddress = getHighEthAddress;
|
|
@@ -1359,11 +1544,14 @@ exports.getTotalConsiderationAmount = getTotalConsiderationAmount;
|
|
|
1359
1544
|
exports.getWrappedNativeCurrency = getWrappedNativeCurrency;
|
|
1360
1545
|
exports.isBazaarSupportedOnChain = isBazaarSupportedOnChain;
|
|
1361
1546
|
exports.isCollectionOfferValid = isCollectionOfferValid;
|
|
1547
|
+
exports.isErc20ListingValid = isErc20ListingValid;
|
|
1362
1548
|
exports.isErc20OfferValid = isErc20OfferValid;
|
|
1363
1549
|
exports.isListingValid = isListingValid;
|
|
1364
1550
|
exports.parseCollectionOfferFromMessage = parseCollectionOfferFromMessage;
|
|
1551
|
+
exports.parseErc20ListingFromMessage = parseErc20ListingFromMessage;
|
|
1365
1552
|
exports.parseErc20OfferFromMessage = parseErc20OfferFromMessage;
|
|
1366
1553
|
exports.parseListingFromMessage = parseListingFromMessage;
|
|
1554
|
+
exports.sortErc20ListingsByPricePerToken = sortErc20ListingsByPricePerToken;
|
|
1367
1555
|
exports.sortErc20OffersByPricePerToken = sortErc20OffersByPricePerToken;
|
|
1368
1556
|
exports.sortListingsByPrice = sortListingsByPrice;
|
|
1369
1557
|
exports.sortOffersByPrice = sortOffersByPrice;
|