@eluvio/elv-client-js 3.1.97 → 3.2.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.
@@ -0,0 +1,1016 @@
1
+ const Utils = require("../Utils");
2
+ const UrlJoin = require("url-join");
3
+ const {FormatNFTDetails, FormatNFTMetadata, FormatNFT} = require("./Utils");
4
+
5
+ /**
6
+ * Methods
7
+ *
8
+ * @module ClientMethods
9
+ */
10
+
11
+ /* USER INFO */
12
+
13
+
14
+ /**
15
+ * <b><i>Requires login</i></b>
16
+ *
17
+ * Retrieve information about the user, including the address, wallet type, and (for custodial users) email address.
18
+ *
19
+ * @methodGroup User
20
+ *
21
+ * @returns {Object} - User info
22
+ */
23
+ exports.UserInfo = function() {
24
+ if(!this.loggedIn) { return; }
25
+
26
+ return {
27
+ address: this.UserAddress() ,
28
+ email: this.__authorization.email,
29
+ walletType: this.__authorization.walletType,
30
+ walletName: this.__authorization.walletName
31
+ };
32
+ };
33
+
34
+ /**
35
+ * <b><i>Requires login</i></b>
36
+ *
37
+ * Retrieve the address of the current user.
38
+ *
39
+ * @methodGroup User
40
+ *
41
+ * @returns {string} - The address of the current user
42
+ */
43
+ exports.UserAddress = function() {
44
+ if(!this.loggedIn) { return; }
45
+
46
+ return this.client.utils.DecodeSignedToken(this.AuthToken()).payload.adr;
47
+ };
48
+
49
+ /**
50
+ * <b><i>Requires login</i></b>
51
+ *
52
+ * Retrieve the fund balances for the current user
53
+ *
54
+ * @methodGroup User
55
+ * @returns {Promise<{Object}>} - Returns balances for the user. All values are in USD.
56
+ * <ul>
57
+ * <li>- totalWalletBalance - Total balance of the users sales and wallet balance purchases</li>
58
+ * <li>- availableWalletBalance - Balance available for purchasing items</li>
59
+ * <li>- pendingWalletBalance - Balance unavailable for purchasing items</li>
60
+ * <li>- withdrawableWalletBalance - Amount that is available for withdrawal</li>
61
+ * <li>- usedBalance - <i>(Only included if user has set up Solana link with the Phantom wallet)</i> Available USDC balance of the user's Solana wallet</li>
62
+ * </ul>
63
+ */
64
+ exports.UserWalletBalance = async function(checkOnboard=false) {
65
+ if(!this.loggedIn) { return; }
66
+
67
+ // eslint-disable-next-line no-unused-vars
68
+ const { balance, usage_hold, payout_hold, stripe_id, stripe_payouts_enabled } = await this.client.utils.ResponseToJson(
69
+ await this.client.authClient.MakeAuthServiceRequest({
70
+ path: UrlJoin("as", "wlt", "mkt", "bal"),
71
+ method: "GET",
72
+ headers: {
73
+ Authorization: `Bearer ${this.AuthToken()}`
74
+ }
75
+ })
76
+ );
77
+
78
+ const userStripeId = stripe_id;
79
+ const userStripeEnabled = stripe_payouts_enabled;
80
+ const totalWalletBalance = parseFloat(balance || 0);
81
+ const availableWalletBalance = Math.max(0, totalWalletBalance - parseFloat(usage_hold || 0));
82
+ const pendingWalletBalance = Math.max(0, totalWalletBalance - availableWalletBalance);
83
+ const withdrawableWalletBalance = Math.max(0, totalWalletBalance - parseFloat(payout_hold || 0));
84
+
85
+ if(checkOnboard && stripe_id && !stripe_payouts_enabled) {
86
+ // Refresh stripe enabled flag
87
+ const rootUrl = new URL(UrlJoin(window.location.origin, window.location.pathname)).toString();
88
+ await this.client.authClient.MakeAuthServiceRequest({
89
+ path: UrlJoin("as", "wlt", "onb", "stripe"),
90
+ method: "POST",
91
+ body: {
92
+ country: "US",
93
+ mode: this.mode,
94
+ refresh_url: rootUrl.toString(),
95
+ return_url: rootUrl.toString()
96
+ },
97
+ headers: {
98
+ Authorization: `Bearer ${this.AuthToken()}`
99
+ }
100
+ });
101
+
102
+ return await this.UserWalletBalance(false);
103
+ }
104
+
105
+ let balances = {
106
+ totalWalletBalance,
107
+ availableWalletBalance,
108
+ pendingWalletBalance,
109
+ withdrawableWalletBalance,
110
+ };
111
+
112
+ if(userStripeEnabled) {
113
+ balances.userStripeId = userStripeId;
114
+ balances.userStripeEnabled = userStripeEnabled;
115
+ }
116
+
117
+ // TODO: integrate
118
+ /*
119
+ if(cryptoStore.usdcConnected) {
120
+ balances.usdcBalance = cryptoStore.phantomUSDCBalance;
121
+ }
122
+
123
+ */
124
+
125
+ return balances;
126
+ };
127
+
128
+
129
+ /**
130
+ * <b><i>Requires login</i></b>
131
+ *
132
+ * Returns basic contract info about the items the current user owns, organized by contract address + token ID
133
+ *
134
+ * This method is significantly faster than <a href="#.UserItems">UserItems</a>, but does not include any NFT metadata.
135
+ *
136
+ * @methodGroup User
137
+ *
138
+ * @returns {Promise<Object>} - Basic info about all owned items.
139
+ */
140
+ exports.UserItemInfo = async function () {
141
+ if(!this.loggedIn) { return {}; }
142
+
143
+ const accountId = `iusr${Utils.AddressToHash(this.UserAddress())}`;
144
+ this.profileData = await this.client.ethClient.MakeProviderCall({
145
+ methodName: "send",
146
+ args: [
147
+ "elv_getAccountProfile",
148
+ [this.client.contentSpaceId, accountId]
149
+ ]
150
+ });
151
+
152
+ if(!this.profileData || !this.profileData.NFTs) { return {}; }
153
+
154
+ let nftInfo = {};
155
+ Object.keys(this.profileData.NFTs).map(tenantId =>
156
+ this.profileData.NFTs[tenantId].forEach(details => {
157
+ const versionHash = (details.TokenUri || "").split("/").find(s => (s || "").startsWith("hq__"));
158
+
159
+ if(!versionHash) {
160
+ return;
161
+ }
162
+
163
+ if(details.TokenHold) {
164
+ details.TokenHoldDate = new Date(parseInt(details.TokenHold) * 1000);
165
+ }
166
+
167
+ const contractAddress = Utils.FormatAddress(details.ContractAddr);
168
+ const key = `${contractAddress}-${details.TokenIdStr}`;
169
+ nftInfo[key] = {
170
+ ...details,
171
+ ContractAddr: Utils.FormatAddress(details.ContractAddr),
172
+ ContractId: `ictr${Utils.AddressToHash(details.ContractAddr)}`,
173
+ VersionHash: versionHash
174
+ };
175
+ })
176
+ );
177
+
178
+ this.nftInfo = nftInfo;
179
+
180
+ return this.nftInfo;
181
+ };
182
+
183
+
184
+ /**
185
+ * <b><i>Requires login</i></b>
186
+ *
187
+ * Retrieve items owned by the current user matching the specified parameters.
188
+ *
189
+ * @methodGroup User
190
+ * @namedParams
191
+ * @param {integer=} start=0 - PAGINATION: Index from which the results should start
192
+ * @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
193
+ * @param {string=} sortBy="created" - Sort order. Options: `default`, `meta/display_name`
194
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
195
+ * @param {string=} filter - Filter results by item name.
196
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
197
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
198
+ * @param {Object=} marketplaceParams - Filter results by marketplace
199
+ * @param {integer=} collectionIndex - If filtering by marketplace, filter by collection. The index refers to the index in the array `marketplace.collections`
200
+ *
201
+ * @returns {Promise<Object>} - Results of the query and pagination info
202
+ */
203
+ exports.UserItems = async function() {
204
+ return this.FilteredQuery({mode: "owned", ...(arguments[0] || {})});
205
+ };
206
+
207
+ /**
208
+ * Return all listings for the current user. Not paginated.
209
+ *
210
+ * @methodGroup User
211
+ * @namedParams
212
+ * @param {string=} sortBy="created" - Sort order. Options: `created`, `info/token_id`, `info/ordinal`, `price`, `nft/display_name`
213
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
214
+ * @param {Object=} marketplaceParams - Filter results by marketplace
215
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
216
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
217
+ *
218
+ * @returns {Promise<Array<Object>>} - List of current user's listings
219
+ */
220
+ exports.UserListings = async function({sortBy="created", sortDesc=false, contractAddress, tokenId, marketplaceParams}={}) {
221
+ return (
222
+ await this.FilteredQuery({
223
+ mode: "listings",
224
+ start: 0,
225
+ limit: 10000,
226
+ sortBy,
227
+ sortDesc,
228
+ sellerAddress: this.UserAddress(),
229
+ marketplaceParams,
230
+ contractAddress,
231
+ tokenId
232
+ })
233
+ ).results;
234
+ };
235
+
236
+ /**
237
+ * Return all sales for the current user. Not paginated.
238
+ *
239
+ * @methodGroup User
240
+ * @namedParams
241
+ * @param {string=} sortBy="created" - Sort order. Options: `created`, `price`, `name`
242
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
243
+ * @param {Object=} marketplaceParams - Filter results by marketplace
244
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
245
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
246
+ * @param {integer=} lastNDays - Filter by results listed in the past N days
247
+ *
248
+ * @returns {Promise<Array<Object>>} - List of current user's sales
249
+ */
250
+ exports.UserSales = async function({sortBy="created", sortDesc=false, contractAddress, tokenId, marketplaceParams}={}) {
251
+ return (
252
+ await this.FilteredQuery({
253
+ mode: "sales",
254
+ start: 0,
255
+ limit: 10000,
256
+ sortBy,
257
+ sortDesc,
258
+ sellerAddress: this.UserAddress(),
259
+ marketplaceParams,
260
+ contractAddress,
261
+ tokenId
262
+ })
263
+ ).results;
264
+ };
265
+
266
+
267
+ /* TENANT */
268
+
269
+ /**
270
+ * Retrieve configuration information about the specified tenant, or the tenant associated with the specified contract.
271
+ *
272
+ * This information includes the royalty rate the tenant receives for secondary sales.
273
+ *
274
+ * @methodGroup Tenants
275
+ * @namedParams
276
+ * @param {string=} tenantId - The ID of the tenant for which to retrieve configuration
277
+ * @param {string=} contractAddress - The ID of an nft contract for which to retrieve configuration
278
+ *
279
+ * @returns {Promise<{Object}>} - The tenant configuration
280
+ */
281
+ exports.TenantConfiguration = async function({tenantId, contractAddress}) {
282
+ try {
283
+ return await this.utils.ResponseToJson(
284
+ this.client.authClient.MakeAuthServiceRequest({
285
+ path: contractAddress ?
286
+ UrlJoin("as", "config", "nft", contractAddress) :
287
+ UrlJoin("as", "config", "tnt", tenantId),
288
+ method: "GET",
289
+ })
290
+ );
291
+ } catch(error) {
292
+ this.Log("Failed to load tenant configuration", true);
293
+ this.Log(error, true);
294
+
295
+ return {};
296
+ }
297
+ };
298
+
299
+
300
+ /* MARKETPLACE */
301
+
302
+ /**
303
+ * Retrieve available stock for the specified marketplace, organized by SKU.
304
+ *
305
+ * If a user is logged in, stock information will also include how many of that item the user has purchased.
306
+ *
307
+ * @methodGroup Marketplaces
308
+ * @namedParams
309
+ * @param {Object} marketplaceParams - Parameters of the marketplace
310
+ *
311
+ * @returns {Promise<Object>} - Stock info for items in the marketplace
312
+ */
313
+ exports.MarketplaceStock = async function ({marketplaceParams, tenantId}) {
314
+ if(!tenantId) {
315
+ const marketplaceInfo = this.MarketplaceInfo({marketplaceParams});
316
+ tenantId = marketplaceInfo.tenantId;
317
+ }
318
+
319
+ if(this.loggedIn) {
320
+ return await Utils.ResponseToJson(
321
+ this.client.authClient.MakeAuthServiceRequest({
322
+ path: UrlJoin("as", "wlt", "nft", "info", tenantId),
323
+ method: "GET",
324
+ headers: {
325
+ Authorization: `Bearer ${this.AuthToken()}`
326
+ }
327
+ })
328
+ );
329
+ }
330
+
331
+ return await Utils.ResponseToJson(
332
+ this.client.authClient.MakeAuthServiceRequest({
333
+ path: UrlJoin("as", "nft", "stock", tenantId),
334
+ method: "GET"
335
+ })
336
+ );
337
+ };
338
+
339
+ /**
340
+ * Retrieve basic information about a specific available marketplace with the specified tenant/marketplace slug, ID, or hash.
341
+ *
342
+ * Includes the slugs, ID and hash of the marketplace, as well as branding information.
343
+ *
344
+ * To retrieve full metadata for the marketplace, use the <a href="#.Marketplace">Marketplace</a> method.
345
+ *
346
+ * @methodGroup Marketplaces
347
+ * @namedParams
348
+ * @param {Object} marketplaceParams - Parameters of the marketplace
349
+ *
350
+ * @returns {Promise<Object>} - Info about the marketplace
351
+ */
352
+ exports.MarketplaceInfo = function ({marketplaceParams}) {
353
+ let { tenantSlug, marketplaceSlug, marketplaceId, marketplaceHash } = (marketplaceParams || {});
354
+
355
+ let marketplaceInfo;
356
+ if(tenantSlug && marketplaceSlug) {
357
+ marketplaceInfo = (this.availableMarketplaces[tenantSlug] || {})[marketplaceSlug];
358
+ } else {
359
+ marketplaceInfo = this.availableMarketplacesById[marketplaceId || this.client.utils.DecodeVersionHash(marketplaceHash).objectId];
360
+ }
361
+
362
+ if(!marketplaceInfo) {
363
+ throw Error(`Eluvio Wallet Client: Unable to find marketplace with parameters ${JSON.stringify(arguments)}`);
364
+ }
365
+
366
+ return marketplaceInfo;
367
+ };
368
+
369
+ /**
370
+ * Retrieve custom CSS for the specified marketplace
371
+ *
372
+ * @methodGroup Marketplaces
373
+ * @namedParams
374
+ * @param {Object} marketplaceParams - Parameters of the marketplace
375
+ *
376
+ * @returns {Promise<string>} - The CSS of the marketplace
377
+ */
378
+ exports.MarketplaceCSS = async function ({marketplaceParams}) {
379
+ const marketplaceInfo = this.MarketplaceInfo({marketplaceParams});
380
+
381
+ const marketplaceHash = marketplaceInfo.marketplaceHash;
382
+
383
+ if(!this.cachedCSS[marketplaceHash]) {
384
+ this.cachedCSS[marketplaceHash] = await this.client.ContentObjectMetadata({
385
+ versionHash: marketplaceHash,
386
+ metadataSubtree: "public/asset_metadata/info/branding/custom_css",
387
+ authorizationToken: this.publicStaticToken,
388
+ noAuth: true
389
+ });
390
+ }
391
+
392
+ return this.cachedCSS[marketplaceHash] || "";
393
+ };
394
+
395
+ /**
396
+ * Retrieve info about all available marketplaces
397
+ *
398
+ * @methodGroup Marketplaces
399
+ * @namedParams
400
+ * @param {boolean=} organizeById - By default, the returned marketplace info is organized by tenant and marketplace slug. If this option is enabled, the marketplaces will be organized by marketplace ID instead.
401
+ * @param {boolean=} forceReload=false - If specified, a new request will be made to check the currently available marketplaces instead of returning cached info
402
+ *
403
+ * @returns {Promise<{Object}>} - Info about available marketplaces
404
+ */
405
+ exports.AvailableMarketplaces = async function ({organizeById, forceReload=false}={}) {
406
+ if(forceReload) {
407
+ await this.LoadAvailableMarketplaces(true);
408
+ }
409
+
410
+ return {
411
+ ...(organizeById ? this.availableMarketplacesById : this.availableMarketplaces)
412
+ };
413
+ };
414
+
415
+ /**
416
+ * Retrieve full information about the specified marketplace
417
+ *
418
+ * <b><i>Note</i></b> - Upon changing login state, the marketplace should be retrieved again as permission info in marketplace items may be different depending on the current user's permissions.
419
+ *
420
+ * @methodGroup Marketplaces
421
+ * @namedParams
422
+ * @param {Object} marketplaceParams - Parameters of the marketplace
423
+ *
424
+ * @returns {Promise<Object>} - The full information for the marketplace
425
+ */
426
+ exports.Marketplace = async function ({marketplaceParams}) {
427
+ return this.LoadMarketplace(marketplaceParams);
428
+ };
429
+
430
+
431
+ /* NFTS */
432
+
433
+ /**
434
+ * Load full info for the specified NFT
435
+ *
436
+ * @methodGroup Items
437
+ * @namedParams
438
+ * @param {string} contractAddress - The contract address of the NFT
439
+ * @param {string} tokenId - The token ID of the NFT
440
+ */
441
+ exports.NFT = async function({tokenId, contractAddress}) {
442
+ let nft = FormatNFTDetails(
443
+ await Utils.ResponseToJson(
444
+ this.client.authClient.MakeAuthServiceRequest({
445
+ path: UrlJoin("as", "nft", "info", contractAddress, tokenId),
446
+ method: "GET"
447
+ })
448
+ )
449
+ );
450
+
451
+ nft.metadata = {
452
+ ...(
453
+ (await this.client.ContentObjectMetadata({
454
+ versionHash: nft.details.VersionHash,
455
+ metadataSubtree: "public/asset_metadata/nft",
456
+ produceLinkUrls: true
457
+ })) || {}
458
+ ),
459
+ ...(nft.metadata || {})
460
+ };
461
+
462
+ nft.config = await this.TenantConfiguration({contractAddress});
463
+
464
+ return FormatNFTMetadata(nft);
465
+ };
466
+
467
+ /** LISTINGS */
468
+
469
+ /**
470
+ * Retrieve the status of the specified listing
471
+ *
472
+ * @methodGroup Listings
473
+ * @namedParams
474
+ * @param {string=} listingId - The ID of the listing
475
+ *
476
+ * @returns {Promise<Object>} - The status of the listing
477
+ */
478
+ exports.ListingStatus = async function({listingId}) {
479
+ try {
480
+ return await Utils.ResponseToJson(
481
+ await this.client.authClient.MakeAuthServiceRequest({
482
+ path: UrlJoin("as", "mkt", "status", listingId),
483
+ method: "GET"
484
+ })
485
+ );
486
+ } catch(error) {
487
+ if(error.status === 404) { return; }
488
+
489
+ throw error;
490
+ }
491
+ };
492
+
493
+ /**
494
+ * Retrieve a specific listing
495
+ *
496
+ * NOTE: When a listing is sold or deleted, it will no longer be queryable with this API. Use <a href="#.ListingStatus">ListingStatus</a> instead.
497
+ *
498
+ * @methodGroup Listings
499
+ * @namedParams
500
+ * @param {string=} listingId - The ID of the listing
501
+ *
502
+ * @returns {Promise<Object>} - The listing
503
+ */
504
+ exports.Listing = async function({listingId}) {
505
+ return FormatNFT(
506
+ await Utils.ResponseToJson(
507
+ await this.client.authClient.MakeAuthServiceRequest({
508
+ path: UrlJoin("as", "mkt", "l", listingId),
509
+ method: "GET",
510
+ })
511
+ )
512
+ );
513
+ };
514
+
515
+
516
+ /**
517
+ * Retrieve listings matching the specified parameters.
518
+ *
519
+ * @methodGroup Listings
520
+ * @namedParams
521
+ * @param {integer=} start=0 - PAGINATION: Index from which the results should start
522
+ * @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
523
+ * @param {string=} sortBy="created" - Sort order. Options: `created`, `info/token_id`, `info/ordinal`, `price`, `nft/display_name`
524
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
525
+ * @param {string=} filter - Filter results by item name.
526
+ * <br /><br />
527
+ * NOTE: This string must be an <b>exact match</b> on the item name.
528
+ * You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
529
+ * @param {string=} editionFilter - Filter results by item edition.
530
+ * <br /><br />
531
+ * NOTE: This string must be an <b>exact match</b> on the edition name.
532
+ * You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
533
+ * @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
534
+ * <br /><br />
535
+ * NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
536
+ * You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
537
+ * @param {string=} sellerAddress - Filter by a specific seller
538
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
539
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
540
+ * @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
541
+ * @param {Object=} marketplaceParams - Filter results by marketplace
542
+ * @param {integer=} collectionIndex - If filtering by marketplace, filter by collection. The index refers to the index in the array `marketplace.collections`
543
+ * @param {integer=} lastNDays - Filter by results listed in the past N days
544
+ *
545
+ * @returns {Promise<Object>} - Results of the query and pagination info
546
+ */
547
+ exports.Listings = async function() {
548
+ return this.FilteredQuery({mode: "listings", ...(arguments[0] || {})});
549
+ };
550
+
551
+ /**
552
+ * Retrieve stats for listings matching the specified parameters.
553
+ *
554
+ * @methodGroup Listings
555
+ * @namedParams
556
+ * @param {integer=} start=0 - PAGINATION: Index from which the results should start
557
+ * @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
558
+ * @param {string=} sortBy="created" - Sort order. Options: `created`, `info/token_id`, `info/ordinal`, `price`, `nft/display_name`
559
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
560
+ * @param {string=} filter - Filter results by item name.
561
+ * <br /><br />
562
+ * NOTE: This string must be an <b>exact match</b> on the item name.
563
+ * You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
564
+ * @param {string=} editionFilter - Filter results by item edition.
565
+ * <br /><br />
566
+ * NOTE: This string must be an <b>exact match</b> on the edition name.
567
+ * You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
568
+ * @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
569
+ * <br /><br />
570
+ * NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
571
+ * You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
572
+ * @param {string=} sellerAddress - Filter by a specific seller
573
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
574
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
575
+ * @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
576
+ * @param {Object=} marketplaceParams - Filter results by marketplace
577
+ * @param {integer=} collectionIndex - If filtering by marketplace, filter by collection. The index refers to the index in the array `marketplace.collections`
578
+ * @param {integer=} lastNDays - Filter by results listed in the past N days
579
+ *
580
+ * @returns {Promise<Object>} - Statistics about listings. All prices in USD.
581
+ */
582
+ exports.ListingStats = async function() {
583
+ return this.FilteredQuery({mode: "listing-stats", ...(arguments[0] || {})});
584
+ };
585
+
586
+ /**
587
+ * Retrieve sales matching the specified parameters.
588
+ *
589
+ * @methodGroup Listings
590
+ * @namedParams
591
+ * @param {integer=} start=0 - PAGINATION: Index from which the results should start
592
+ * @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
593
+ * @param {string=} sortBy="created" - Sort order. Options: `created`, `price`, `name`
594
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
595
+ * @param {string=} filter - Filter results by item name.
596
+ * <br /><br />
597
+ * NOTE: This string must be an <b>exact match</b> on the item name.
598
+ * You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
599
+ * @param {string=} editionFilter - Filter results by item edition.
600
+ * <br /><br />
601
+ * NOTE: This string must be an <b>exact match</b> on the edition name.
602
+ * You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
603
+ * @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
604
+ * <br /><br />
605
+ * NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
606
+ * You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
607
+ * @param {string=} sellerAddress - Filter by a specific seller
608
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
609
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
610
+ * @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
611
+ * @param {Object=} marketplaceParams - Filter results by marketplace
612
+ * @param {integer=} collectionIndex - If filtering by marketplace, filter by collection. The index refers to the index in the array `marketplace.collections`
613
+ * @param {integer=} lastNDays - Filter by results listed in the past N days
614
+ *
615
+ * @returns {Promise<Object>} - Results of the query and pagination info
616
+ */
617
+ exports.Sales = async function() {
618
+ return this.FilteredQuery({mode: "sales", ...(arguments[0] || {})});
619
+ };
620
+
621
+ /**
622
+ * Retrieve stats for listings matching the specified parameters.
623
+ *
624
+ * @methodGroup Listings
625
+ * @namedParams
626
+ * @param {integer=} start=0 - PAGINATION: Index from which the results should start
627
+ * @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
628
+ * @param {string=} sortBy="created" -
629
+ * @param {boolean=} sortDesc=false - Sort results descending instead of ascending
630
+ * @param {string=} filter - Filter results by item name.
631
+ * <br /><br />
632
+ * NOTE: This string must be an <b>exact match</b> on the item name.
633
+ * You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
634
+ * @param {string=} editionFilter - Filter results by item edition.
635
+ * <br /><br />
636
+ * NOTE: This string must be an <b>exact match</b> on the edition name.
637
+ * You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
638
+ * @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
639
+ * <br /><br />
640
+ * NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
641
+ * You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
642
+ * @param {string=} sellerAddress - Filter by a specific seller
643
+ * @param {string=} contractAddress - Filter results by the address of the NFT contract
644
+ * @param {string=} tokenId - Filter by token ID (if filtering by contract address)
645
+ * @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
646
+ * @param {Object=} marketplaceParams - Filter results by marketplace
647
+ * @param {integer=} collectionIndex - If filtering by marketplace, filter by collection. The index refers to the index in the array `marketplace.collections`
648
+ * @param {integer=} lastNDays - Filter by results listed in the past N days
649
+ *
650
+ * @returns {Promise<Object>} - Statistics about sales. All prices in USD.
651
+ */
652
+ exports.SalesStats = async function() {
653
+ return this.FilteredQuery({mode: "sales-stats", ...(arguments[0] || {})});
654
+ };
655
+
656
+
657
+ /**
658
+ * <b><i>Requires login</i></b>
659
+ *
660
+ * Create or update a listing for the specified item
661
+ *
662
+ * @methodGroup Listings
663
+ * @namedParams
664
+ * @param {string} contractAddress - The NFT contract address of the item
665
+ * @param {string} tokenId - The token ID of the item
666
+ * @param {number} price - The price of the listing, in USD
667
+ * @param {string=} listingId - (When editing a listing) The ID of the existing listing
668
+ *
669
+ * @returns {Promise<string>} - The listing ID of the created listing
670
+ */
671
+ exports.CreateListing = async function({contractAddress, tokenId, price, listingId}) {
672
+ contractAddress = Utils.FormatAddress(contractAddress);
673
+
674
+ if(listingId) {
675
+ // Update
676
+ return await Utils.ResponseToFormat(
677
+ "text",
678
+ await this.client.authClient.MakeAuthServiceRequest({
679
+ path: UrlJoin("as", "wlt", "mkt"),
680
+ method: "PUT",
681
+ body: {
682
+ id: listingId,
683
+ price: parseFloat(price)
684
+ },
685
+ headers: {
686
+ Authorization: `Bearer ${this.AuthToken()}`
687
+ }
688
+ })
689
+ );
690
+ } else {
691
+ // Create
692
+ return await Utils.ResponseToJson(
693
+ await this.client.authClient.MakeAuthServiceRequest({
694
+ path: UrlJoin("as", "wlt", "mkt"),
695
+ method: "POST",
696
+ body: {
697
+ contract: contractAddress,
698
+ token: tokenId,
699
+ price: parseFloat(price)
700
+ },
701
+ headers: {
702
+ Authorization: `Bearer ${this.AuthToken()}`
703
+ }
704
+ })
705
+ );
706
+ }
707
+ };
708
+
709
+ /**
710
+ * <b><i>Requires login</i></b>
711
+ *
712
+ * Remove the specified listing
713
+ *
714
+ * @methodGroup Listings
715
+ * @namedParams
716
+ * @param {string} listingId - The ID of the listing to remove
717
+ */
718
+ exports.RemoveListing = async function({listingId}) {
719
+ await this.client.authClient.MakeAuthServiceRequest({
720
+ path: UrlJoin("as", "wlt", "mkt", listingId),
721
+ method: "DELETE",
722
+ headers: {
723
+ Authorization: `Bearer ${this.AuthToken()}`
724
+ }
725
+ });
726
+ };
727
+
728
+ /**
729
+ * Retrieve all valid names for filtering listings. Full item names are required for filtering listing results by name.
730
+ *
731
+ * Specify marketplace information to filter the results to only items offered in that marketplace.
732
+ *
733
+ * @methodGroup Listings
734
+ * @namedParams
735
+ * @param {Object} marketplaceParams - Parameters of a marketplace to filter results by
736
+ *
737
+ * @returns {Promise<Array<String>>} - A list of item names
738
+ */
739
+ exports.ListingNames = async function({marketplaceParams}) {
740
+ let tenantId;
741
+ if(marketplaceParams) {
742
+ tenantId = (await this.MarketplaceInfo({marketplaceParams})).tenantId;
743
+ }
744
+
745
+ return await Utils.ResponseToJson(
746
+ await this.client.authClient.MakeAuthServiceRequest({
747
+ path: UrlJoin("as", "mkt", "names"),
748
+ method: "GET",
749
+ queryParams: tenantId ? { filter: `tenant:eq:${tenantId}` } : {}
750
+ })
751
+ );
752
+ };
753
+
754
+
755
+ /**
756
+ * Retrieve all valid edition names of the specified item. Full item edition names are required for filtering listing results by edition.
757
+ *
758
+ * @methodGroup Listings
759
+ * @namedParams
760
+ * @param {string} displayName - Display name of the item from which to request edition names
761
+ *
762
+ * @returns {Promise<Array<String>>} - A list of item editions
763
+ */
764
+ exports.ListingEditionNames = async function({displayName}) {
765
+ return await Utils.ResponseToJson(
766
+ await this.client.authClient.MakeAuthServiceRequest({
767
+ path: UrlJoin("as", "mkt", "editions"),
768
+ queryParams: {
769
+ filter: `nft/display_name:eq:${displayName}`
770
+ },
771
+ method: "GET"
772
+ })
773
+ );
774
+ };
775
+
776
+ /**
777
+ * Retrieve names of all valid attributes for listed tiems. Full attribute names and values are required for filtering listing results by attributes.
778
+ *
779
+ * Specify marketplace information to filter the results to only items offered in that marketplace.
780
+ *
781
+ * @methodGroup Listings
782
+ * @namedParams
783
+ * @param {Object=} marketplaceParams - Parameters of a marketplace to filter results by
784
+ * @param {string=} displayName - Display name of the item from which to request attributes
785
+ *
786
+ * @returns {Promise<Array<String>>} - A list of valid attributes
787
+ */
788
+ exports.ListingAttributes = async function({marketplaceParams, displayName}={}) {
789
+ let filters = [];
790
+
791
+ if(marketplaceParams) {
792
+ filters.push(`tenant:eq:${(await this.MarketplaceInfo({marketplaceParams})).tenantId}`);
793
+ }
794
+
795
+ if(displayName) {
796
+ filters.push(`nft/display_name:eq:${displayName}`);
797
+ }
798
+
799
+ const attributes = await Utils.ResponseToJson(
800
+ await this.client.authClient.MakeAuthServiceRequest({
801
+ path: UrlJoin("as", "mkt", "attributes"),
802
+ method: "GET",
803
+ queryParams: {
804
+ filter: filters
805
+ }
806
+ })
807
+ );
808
+
809
+ return attributes
810
+ .map(({trait_type, values}) => ({ name: trait_type, values }))
811
+ .filter(({name}) =>
812
+ !["Content Fabric Hash", "Total Minted Supply", "Creator"].includes(name)
813
+ );
814
+ };
815
+
816
+ /* MINTING STATUS */
817
+
818
+ /**
819
+ * Return status of the specified listing purchase
820
+ *
821
+ * @methodGroup Status
822
+ * @namedParams
823
+ * @param {string} listingId - The ID of the listing
824
+ * @param {string} confirmationId - The confirmation ID of the purchase
825
+ *
826
+ * @returns {Promise<Object>} - The status of the purchase
827
+ */
828
+ exports.ListingPurchaseStatus = async function({listingId, confirmationId}) {
829
+ try {
830
+ const listingStatus = await this.ListingStatus({listingId});
831
+
832
+ if(!listingStatus) {
833
+ throw Error("Unable to find info for listing " + listingId);
834
+ }
835
+
836
+ const statuses = await this.MintingStatus({tenantId: listingStatus.tenant});
837
+
838
+ return statuses
839
+ .find(status =>
840
+ status.op === "nft-transfer" &&
841
+ status.extra && status.extra[0] === confirmationId
842
+ ) || { status: "none" };
843
+ } catch(error) {
844
+ this.Log(error, true);
845
+ return { status: "unknown" };
846
+ }
847
+ };
848
+
849
+ /**
850
+ * Return status of the specified marketplace purchase
851
+ *
852
+ * @methodGroup Status
853
+ * @namedParams
854
+ * @param {Object} marketplaceParams - Parameters of the marketplace
855
+ * @param {string} confirmationId - The confirmation ID of the purchase
856
+ *
857
+ * @returns {Promise<Object>} - The minting status of the purchaseed item(s)
858
+ */
859
+ exports.PurchaseStatus = async function({marketplaceParams, confirmationId}) {
860
+ try {
861
+ const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
862
+ const statuses = await this.MintingStatus({tenantId: marketplaceInfo.tenant_id});
863
+
864
+ return statuses.find(status => status.op === "nft-buy" && status.confirmationId === confirmationId) || { status: "none" };
865
+ } catch(error) {
866
+ this.Log(error, true);
867
+ return { status: "unknown" };
868
+ }
869
+ };
870
+
871
+ /**
872
+ * Return status of the specified item claim
873
+ *
874
+ * @methodGroup Status
875
+ * @namedParams
876
+ * @param {Object} marketplaceParams - Parameters of the marketplace
877
+ * @param {string} sku - The SKU of the item claimed
878
+ *
879
+ * @returns {Promise<Object>} - The minting status of the claim
880
+ */
881
+ exports.ClaimStatus = async function({marketplaceParams, sku}) {
882
+ try {
883
+ const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
884
+ const statuses = await this.MintingStatus({tenantId: marketplaceInfo.tenantId});
885
+
886
+ return statuses.find(status => status.op === "nft-claim" && status.marketplaceId === marketplaceInfo.marketplaceId && status.confirmationId === sku) || { status: "none" };
887
+ } catch(error) {
888
+ this.Log(error, true);
889
+ return { status: "unknown" };
890
+ }
891
+ };
892
+
893
+ /**
894
+ * Return status of the specified pack opening
895
+ *
896
+ * @methodGroup Status
897
+ * @namedParams
898
+ * @param {string} contractAddress - The NFT contract address of the opened pack
899
+ * @param {string} tokenId - The token ID of the opened pack
900
+ *
901
+ * @returns {Promise<Object>} - The status of the pack opening
902
+ */
903
+ exports.PackOpenStatus = async function({contractAddress, tokenId}) {
904
+ try {
905
+ const tenantConfig = await this.TenantConfiguration({contractAddress});
906
+
907
+ const statuses = await this.MintingStatus({tenantId: tenantConfig.tenant});
908
+
909
+ return statuses.find(status => status.op === "nft-open" && Utils.EqualAddress(contractAddress, status.address) && status.tokenId === tokenId) || { status: "none" };
910
+ } catch(error) {
911
+ this.Log(error, true);
912
+ return { status: "unknown" };
913
+ }
914
+ };
915
+
916
+ /**
917
+ * Return status of the specified collection redemption
918
+ *
919
+ * @methodGroup Status
920
+ * @namedParams
921
+ * @param {Object} marketplaceParams - Parameters of the marketplace
922
+ * @param {string} confirmationId - The confirmation ID of the redemption
923
+ *
924
+ * @returns {Promise<Object>} - The status of the collection redemption
925
+ */
926
+ exports.CollectionRedemptionStatus = async function({marketplaceParams, confirmationId}) {
927
+ try {
928
+ const statuses = await this.MintingStatus({marketplaceParams});
929
+
930
+ return statuses.find(status => status.op === "nft-redeem" && status.confirmationId === confirmationId) || { status: "none" };
931
+ } catch(error) {
932
+ this.Log(error, true);
933
+ return { status: "unknown" };
934
+ }
935
+ };
936
+
937
+ /* EVENTS */
938
+
939
+
940
+ exports.LoadDrop = async function({tenantSlug, eventSlug, dropId}) {
941
+ if(!this.drops){
942
+ this.drops = {};
943
+ }
944
+
945
+ if(!this.drops[tenantSlug]) {
946
+ this.drops[tenantSlug] = {};
947
+ }
948
+
949
+ if(!this.drops[tenantSlug][eventSlug]) {
950
+ this.drops[tenantSlug][eventSlug] = {};
951
+ }
952
+
953
+ if(!this.drops[tenantSlug][eventSlug][dropId]) {
954
+ const mainSiteHash = await this.client.LatestVersionHash({objectId: this.mainSiteId});
955
+ const event = (await this.client.ContentObjectMetadata({
956
+ versionHash: mainSiteHash,
957
+ metadataSubtree: UrlJoin("public", "asset_metadata", "tenants", tenantSlug, "sites", eventSlug, "info"),
958
+ resolveLinks: true,
959
+ linkDepthLimit: 2,
960
+ resolveIncludeSource: true,
961
+ produceLinkUrls: true,
962
+ select: [".", "drops"],
963
+ noAuth: true
964
+ })) || [];
965
+
966
+ const eventId = Utils.DecodeVersionHash(event["."].source).objectId;
967
+
968
+ event.drops.forEach(drop => {
969
+ drop = {
970
+ ...drop,
971
+ eventId
972
+ };
973
+
974
+ this.drops[tenantSlug][eventSlug][drop.uuid] = drop;
975
+ this.drops[drop.uuid] = drop;
976
+ });
977
+ }
978
+
979
+ return this.drops[dropId];
980
+ };
981
+
982
+ exports.SubmitDropVote = async function({marketplaceParams, eventId, dropId, sku}) {
983
+ const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
984
+ await this.client.authClient.MakeAuthServiceRequest({
985
+ path: UrlJoin("as", "wlt", "act", marketplaceInfo.tenant_id),
986
+ method: "POST",
987
+ body: {
988
+ op: "vote-drop",
989
+ evt: eventId,
990
+ id: dropId,
991
+ itm: sku
992
+ },
993
+ headers: {
994
+ Authorization: `Bearer ${this.AuthToken()}`
995
+ }
996
+ });
997
+ };
998
+
999
+ exports.DropStatus = async function({marketplace, eventId, dropId}) {
1000
+ try {
1001
+ const response = await Utils.ResponseToJson(
1002
+ this.client.authClient.MakeAuthServiceRequest({
1003
+ path: UrlJoin("as", "wlt", "act", marketplace.tenant_id, eventId, dropId),
1004
+ method: "GET",
1005
+ headers: {
1006
+ Authorization: `Bearer ${this.AuthToken()}`
1007
+ }
1008
+ })
1009
+ );
1010
+
1011
+ return response.sort((a, b) => a.ts > b.ts ? 1 : -1)[0] || { status: "none" };
1012
+ } catch(error) {
1013
+ this.Log(error, true);
1014
+ return "";
1015
+ }
1016
+ };