const Utils = require("../Utils");
const UrlJoin = require("url-join");
const {FormatNFTDetails, FormatNFTMetadata, FormatNFT} = require("./Utils");
const MergeWith = require("lodash/mergeWith");
/**
* Methods
*
* @module ClientMethods
*/
/* USER INFO */
/**
* <b><i>Requires login</i></b>
*
* Retrieve information about the user, including the address, wallet type, and (for custodial users) email address.
*
* @methodGroup User
*
* @returns {Object} - User info
*/
exports.UserInfo = function() {
if(!this.loggedIn) { return; }
return {
name: this.__authorization.email || this.UserAddress(),
address: this.UserAddress() ,
email: this.__authorization.email,
walletType: this.__authorization.walletType,
walletName: this.__authorization.walletName
};
};
/**
* <b><i>Requires login</i></b>
*
* Retrieve the address of the current user.
*
* @methodGroup User
*
* @returns {string} - The address of the current user
*/
exports.UserAddress = function() {
if(!this.loggedIn) { return; }
return this.client.utils.DecodeSignedToken(this.AuthToken()).payload.adr;
};
/**
* <b><i>Requires login</i></b>
*
* Retrieve the fund balances for the current user
*
* @methodGroup User
* @returns {Promise<Object>} - Returns balances for the user. All values are in USD.
* <ul>
* <li>- totalWalletBalance - Total balance of the users sales and wallet balance purchases</li>
* <li>- availableWalletBalance - Balance available for purchasing items</li>
* <li>- pendingWalletBalance - Balance unavailable for purchasing items</li>
* <li>- withdrawableWalletBalance - Amount that is available for withdrawal</li>
* <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>
* </ul>
*/
exports.UserWalletBalance = async function(checkOnboard=false) {
if(!this.loggedIn) { return; }
// eslint-disable-next-line no-unused-vars
const { balance, usage_hold, payout_hold, locked_offer_balance, stripe_id, stripe_payouts_enabled } = await this.client.utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "bal"),
method: "GET",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
const userStripeId = stripe_id;
const userStripeEnabled = stripe_payouts_enabled;
const totalWalletBalance = parseFloat(balance || 0);
const lockedWalletBalance = parseFloat(locked_offer_balance || 0);
const availableWalletBalance = Math.max(0, totalWalletBalance - parseFloat(usage_hold || 0) - lockedWalletBalance);
const pendingWalletBalance = Math.max(0, totalWalletBalance - availableWalletBalance);
const withdrawableWalletBalance = Math.max(0, totalWalletBalance - parseFloat(Math.max(payout_hold, lockedWalletBalance) || 0));
if(checkOnboard && stripe_id && !stripe_payouts_enabled) {
// Refresh stripe enabled flag
const rootUrl = new URL(UrlJoin(window.location.origin, window.location.pathname)).toString();
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "onb", "stripe"),
method: "POST",
body: {
country: "US",
mode: this.mode,
refresh_url: rootUrl.toString(),
return_url: rootUrl.toString()
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
return await this.UserWalletBalance(false);
}
let balances = {
totalWalletBalance,
availableWalletBalance,
lockedWalletBalance,
pendingWalletBalance,
withdrawableWalletBalance,
};
if(userStripeEnabled) {
balances.userStripeId = userStripeId;
balances.userStripeEnabled = userStripeEnabled;
}
// TODO: integrate
/*
if(cryptoStore.usdcConnected) {
balances.usdcBalance = cryptoStore.phantomUSDCBalance;
}
*/
return balances;
};
/**
* Retrieve all valid names for filtering user items. Full item names are required for filtering results by name.
*
* Specify marketplace information to filter the results to only items offered in that marketplace.
*
* @methodGroup User
* @namedParams
* @param {string=} userAddress - Address of a user
* @param {Object=} marketplaceParams - Parameters of a marketplace to filter results by
*
* @returns {Promise<Array<String>>} - A list of item names
*/
exports.UserItemNames = async function({marketplaceParams, userAddress}={}) {
let filters = [];
if(marketplaceParams) {
filters.push(`tenant:eq:${(await this.MarketplaceInfo({marketplaceParams})).tenantId}`);
}
if(userAddress) {
filters.push(`wlt:eq:${Utils.FormatAddress(userAddress)}`);
}
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "names"),
method: "GET",
queryParams: { filter: filters }
})
);
};
/**
* Retrieve all valid edition names for filtering the specified item. Full edition names are required for filtering results by edition.
*
* Specify marketplace information to filter the results to only items offered in that marketplace.
*
* @methodGroup User
* @namedParams
* @param {string} displayName - Name of an item
*
* @returns {Promise<Array<String>>} - A list of item editions
*/
exports.UserItemEditionNames = async function({displayName}) {
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "editions"),
method: "GET",
queryParams: { filter: `meta/display_name:eq:${displayName}` }
})
);
};
/**
* Retrieve all valid attribute names and values. Full attribute names and values are required for filtering results by attribute.
*
* Specify marketplace information to filter the results to only items offered in that marketplace.
*
* @methodGroup User
* @namedParams
* @param {string=} userAddress - Address of a user
* @param {string=} displayName - Name of an item
* @param {Object=} marketplaceParams - Parameters of a marketplace to filter results by
*
* @returns {Promise<Array<String>>} - A list of item names
*/
exports.UserItemAttributes = async function({marketplaceParams, displayName, userAddress}={}) {
let filters = [];
if(marketplaceParams) {
filters.push(`tenant:eq:${(await this.MarketplaceInfo({marketplaceParams})).tenantId}`);
}
if(userAddress) {
filters.push(`wlt:eq:${Utils.FormatAddress(userAddress)}`);
}
if(displayName) {
filters.push(`meta/display_name:eq:${displayName}`);
}
const attributes = await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "attributes"),
method: "GET",
queryParams: {
filter: filters
}
})
);
return attributes
.map(({trait_type, values}) => ({ name: trait_type, values }))
.filter(({name}) =>
!["Content Fabric Hash", "Total Minted Supply", "Creator"].includes(name)
);
};
/**
* <b><i>Requires login</i></b>
*
* Retrieve items owned by the specified or current user matching the specified parameters.
*
* @methodGroup User
* @namedParams
* @param {boolean=} includeFullMetadata=false - If true, will include full metadata for each item. Ignored if not logged in.
* @param {string=} userAddress - Address of a user. If not specified, will return results for current user
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
* @param {string=} sortBy="default" - Sort order. Options: `default`, `meta/display_name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {string=} filter - Filter results by item name.
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {Array<integer>=} collectionIndexes - If filtering by marketplace, filter by collection(s). The index refers to the index in the array `marketplace.collections`
*
* @returns {Promise<Object>} - Results of the query and pagination info
*/
exports.UserItems = async function ({sortBy = "default", includeFullMetadata = false} = {}) {
if(includeFullMetadata && this.loggedIn) {
return this.FilteredQuery({mode: "owned-full-meta", sortBy, ...(arguments[0] || {})});
} else {
return this.FilteredQuery({mode: "owned", sortBy, ...(arguments[0] || {})});
}
};
/**
* Return all listings for the current user. Not paginated.
*
* @methodGroup User
* @namedParams
* @param {string=} userAddress - Address of a user. If not specified, will return results for current user
* @param {string=} sortBy="created" - Sort order. Options: `created`, `info/token_id`, `info/ordinal`, `price`, `nft/display_name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
*
* @returns {Promise<Array<Object>>} - List of current user's listings
*/
exports.UserListings = async function({userAddress, sortBy="created", sortDesc=false, contractAddress, tokenId, marketplaceParams}={}) {
return (
await this.FilteredQuery({
mode: "listings",
start: 0,
limit: 10000,
sortBy,
sortDesc,
sellerAddress: userAddress || this.UserAddress(),
marketplaceParams,
contractAddress,
tokenId,
includeCheckoutLocked: true
})
).results;
};
/**
* Return all sales for the current user. Not paginated.
*
* @methodGroup User
* @namedParams
* @param {string=} userAddress - Address of a user. If not specified, will return results for current user
* @param {string=} sortBy="created" - Sort order. Options: `created`, `price`, `name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
*
* @returns {Promise<Array<Object>>} - List of current user's sales
*/
exports.UserSales = async function({userAddress, sortBy="created", sortDesc=false, contractAddress, tokenId, startTime, endTime, lastNDays, marketplaceParams}={}) {
return (
await this.FilteredQuery({
mode: "sales",
start: 0,
limit: 10000,
sortBy,
sortDesc,
sellerAddress: userAddress || this.UserAddress(),
startTime,
endTime,
lastNDays,
marketplaceParams,
contractAddress,
tokenId
})
).results;
};
/**
* Return all transfers and sales for the current user. Not paginated.
*
* @methodGroup User
* @namedParams
* @param {string=} userAddress - Address of a user. If not specified, will return results for current user
* @param {string=} sortBy="created" - Sort order. Options: `created`, `price`, `name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
*
* @returns {Promise<Array<Object>>} - List of current user's sales
*/
exports.UserTransfers = async function({userAddress, sortBy="created", sortDesc=false, contractAddress, tokenId, startTime, endTime, lastNDays, marketplaceParams}={}) {
return (
await this.FilteredQuery({
mode: "transfers",
start: 0,
limit: 10000,
sortBy,
sortDesc,
sellerAddress: userAddress || this.UserAddress(),
marketplaceParams,
contractAddress,
tokenId,
startTime,
endTime,
lastNDays
})
).results;
};
/* TENANT */
/**
* Retrieve configuration information about the specified tenant, or the tenant associated with the specified contract.
*
* This information includes the royalty rate the tenant receives for secondary sales.
*
* @methodGroup Tenants
* @namedParams
* @param {string=} tenantId - The ID of the tenant for which to retrieve configuration
* @param {string=} contractAddress - The ID of an nft contract for which to retrieve configuration
*
* @returns {Promise<Object>} - The tenant configuration
*/
exports.TenantConfiguration = async function({tenantId, contractAddress}) {
try {
contractAddress = contractAddress ? Utils.FormatAddress(contractAddress) : undefined;
if(!this.tenantConfigs[contractAddress || tenantId]) {
this.tenantConfigs[contractAddress || tenantId] = await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: contractAddress ?
UrlJoin("as", "config", "nft", contractAddress) :
UrlJoin("as", "config", "tnt", tenantId),
method: "GET",
})
);
}
return this.tenantConfigs[contractAddress || tenantId];
} catch(error) {
this.Log("Failed to load tenant configuration", true, error);
return {};
}
};
/**
* Retrieve the current exchange rate for the specified currency to USD
*
* @methodGroup Tenants
* @namedParams
* @param {string} currency - The currency for which to retrieve the USD exchange rate
*/
exports.ExchangeRate = async function({currency}) {
if(!currency) {
throw Error("Eluvio Wallet Client: Invalid or missing currency in ExchangeRate");
}
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "xr", "ebanx", currency),
method: "GET"
})
);
};
/**
* Retrieve custom CSS for the specified tenant
*
* @methodGroup Tenants
* @namedParams
* @param {Object} tenantSlug
*
* @returns {Promise<string>} - The CSS of the tenant
*/
exports.TenantCSS = async function ({tenantSlug}) {
if(!this.cachedCSS[tenantSlug]) {
this.cachedCSS[tenantSlug] = await this.client.ContentObjectMetadata({
libraryId: this.mainSiteLibraryId,
objectId: this.mainSiteId,
metadataSubtree: UrlJoin("/public", "asset_metadata", "tenants", tenantSlug, "info", "branding", "wallet_css"),
authorizationToken: this.publicStaticToken
});
}
return this.cachedCSS[tenantSlug] || "";
};
/* MARKETPLACE */
/**
* Retrieve available stock for the specified marketplace, organized by SKU.
*
* If a user is logged in, stock information will also include how many of that item the user has purchased.
*
* @methodGroup Marketplaces
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
*
* @returns {Promise<Object>} - Stock info for items in the marketplace
*/
exports.MarketplaceStock = async function ({marketplaceParams, tenantId}) {
if(!tenantId) {
const marketplaceInfo = this.MarketplaceInfo({marketplaceParams});
tenantId = marketplaceInfo.tenantId;
}
if(this.loggedIn) {
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "nft", "info", tenantId),
method: "GET",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
}
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "nft", "stock", tenantId),
method: "GET"
})
);
};
/**
* Retrieve basic information about a specific available marketplace with the specified tenant/marketplace slug, ID, or hash.
*
* Includes the slugs, ID and hash of the marketplace, as well as branding information.
*
* To retrieve full metadata for the marketplace, use the <a href="#.Marketplace">Marketplace</a> method.
*
* @methodGroup Marketplaces
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
*
* @returns {Promise<Object>} - Info about the marketplace
*/
exports.MarketplaceInfo = function ({marketplaceParams}) {
let { tenantSlug, marketplaceSlug, marketplaceId, marketplaceHash } = (marketplaceParams || {});
let marketplaceInfo;
if(tenantSlug && marketplaceSlug) {
marketplaceInfo = (this.availableMarketplaces[tenantSlug] || {})[marketplaceSlug];
} else {
marketplaceId = marketplaceHash ? this.client.utils.DecodeVersionHash(marketplaceHash).objectId : marketplaceId;
marketplaceInfo = this.availableMarketplacesById[marketplaceId];
}
if(!marketplaceInfo) {
throw Error(`Eluvio Wallet Client: Unable to find marketplace with parameters ${JSON.stringify(arguments)}`);
}
return marketplaceInfo;
};
/**
* Retrieve custom CSS for the specified marketplace
*
* @methodGroup Marketplaces
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
*
* @returns {Promise<string>} - The CSS of the marketplace
*/
exports.MarketplaceCSS = async function ({marketplaceParams}) {
const marketplaceInfo = this.MarketplaceInfo({marketplaceParams});
const marketplaceHash = marketplaceInfo.marketplaceHash;
if(!this.cachedCSS[marketplaceHash]) {
this.cachedCSS[marketplaceHash] = await this.client.ContentObjectMetadata({
versionHash: marketplaceHash,
metadataSubtree: "public/asset_metadata/info/branding/custom_css",
authorizationToken: this.publicStaticToken,
noAuth: true
});
}
return this.cachedCSS[marketplaceHash] || "";
};
/**
* Retrieve info about all available marketplaces
*
* @methodGroup Marketplaces
* @namedParams
* @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.
* @param {boolean=} forceReload=false - If specified, a new request will be made to check the currently available marketplaces instead of returning cached info
*
* @returns {Promise<Object>} - Info about available marketplaces
*/
exports.AvailableMarketplaces = async function ({organizeById, forceReload=false}={}) {
if(forceReload) {
await this.LoadAvailableMarketplaces(true);
}
return {
...(organizeById ? this.availableMarketplacesById : this.availableMarketplaces)
};
};
/**
* Retrieve full information about the specified marketplace
*
* <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.
*
* @methodGroup Marketplaces
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
*
* @returns {Promise<Object>} - The full information for the marketplace
*/
exports.Marketplace = async function ({marketplaceParams}) {
return this.LoadMarketplace(marketplaceParams);
};
/* NFTS */
/**
* Return info about the specified NFT contract, including the cap, current total supply, and total minted and burned.
*
* @methodGroup NFTs
* @namedParams
* @param {string} contractAddress - The contract address of the NFT
*
* @returns {Promise<Object>} - Information about the specified contract
*/
exports.NFTContractStats = async function({contractAddress}) {
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "nft", "info", contractAddress),
method: "GET"
})
);
};
/**
* Load full info for the specified NFT
*
* @methodGroup NFTs
* @namedParams
* @param {string} contractAddress - The contract address of the NFT
* @param {string} tokenId - The token ID of the NFT
*/
exports.NFT = async function({tokenId, contractAddress}) {
let nft = FormatNFTDetails(
await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "nft", "info", contractAddress, tokenId),
method: "GET"
})
)
);
const assetMetadata = (await this.client.ContentObjectMetadata({
versionHash: nft.details.VersionHash,
metadataSubtree: "public/asset_metadata/nft",
produceLinkUrls: true
})) || {};
nft.metadata = MergeWith({}, assetMetadata, nft.metadata, (a, b) => b === null || b === "" ? a : undefined);
if(this.localization) {
const localizedMetadata = (await this.client.ContentObjectMetadata({
versionHash: nft.details.VersionHash,
metadataSubtree: UrlJoin("public", "asset_metadata", "localizations", this.localization, "nft"),
produceLinkUrls: true
})) || {};
nft.metadata = MergeWith({}, nft.metadata, localizedMetadata, (a, b) => b === null || b === "" ? a : undefined);
}
nft.config = await this.TenantConfiguration({contractAddress});
return FormatNFTMetadata(this, nft);
};
/**
* <b><i>Requires login</i></b>
*
* Transfer the specified NFT owned by the current user to the specified address
*
* @methodGroup NFTs
* @namedParams
* @param {string} contractAddress - The contract address of the NFT
* @param {string} tokenId - The token ID of the NFT
* @param {string} targetAddress - The address to which to transfer the NFT
*/
exports.TransferNFT = async function({contractAddress, tokenId, targetAddress}) {
if(!targetAddress || !Utils.ValidAddress(targetAddress)) {
throw Error("Eluvio Wallet Client: Invalid or missing target address in UserTransferNFT");
}
return await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "xfer"),
method: "POST",
body: {
contract: Utils.FormatAddress(contractAddress),
token: tokenId,
to_addr: Utils.FormatAddress(targetAddress)
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
/** LISTINGS */
/**
* Retrieve the status of the specified listing
*
* @methodGroup Listings
* @namedParams
* @param {string=} listingId - The ID of the listing
*
* @returns {Promise<Object>} - The status of the listing
*/
exports.ListingStatus = async function({listingId}) {
try {
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "mkt", "status", listingId),
method: "GET"
})
);
} catch(error) {
if(error.status === 404) { return; }
throw error;
}
};
/**
* Retrieve a specific listing
*
* NOTE: When a listing is sold or deleted, it will no longer be queryable with this API. Use <a href="#.ListingStatus">ListingStatus</a> instead.
*
* @methodGroup Listings
* @namedParams
* @param {string=} listingId - The ID of the listing
*
* @returns {Promise<Object>} - The listing
*/
exports.Listing = async function({listingId}) {
return FormatNFT(
this,
await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "mkt", "l", listingId),
method: "GET",
})
)
);
};
/**
* Retrieve listings matching the specified parameters.
*
* @methodGroup Listings
* @namedParams
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
* @param {string=} sortBy="created" - Sort order. Options: `created`, `info/token_id`, `info/ordinal`, `price`, `nft/display_name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {string=} filter - Filter results by item name.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the item name.
* You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
* @param {Array<string>=} editionFilters - Filter results by item edition.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the edition name.
* You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
* @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
* <br /><br />
* NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
* You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
* @param {Object=} priceRange - Filter min and/or max price (e.g. `{min: 1}` `{max: 2}` `{min: 1.50, max: 10.50})
* @param {string=} sellerAddress - Filter by a specific seller
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {Array<integer>=} collectionIndexes - If filtering by marketplace, filter by collection(s). The index refers to the index in the array `marketplace.collections`
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
* @param {boolean=} includeCheckoutLocked - If specified, listings which are currently in the checkout process (and not so currently purchasable) will be included in the results. By default they are excluded.
*
* @returns {Promise<Object>} - Results of the query and pagination info
*/
exports.Listings = async function() {
return this.FilteredQuery({mode: "listings", ...(arguments[0] || {})});
};
/**
* Retrieve stats for listings matching the specified parameters.
*
* @methodGroup Listings
* @namedParams
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
* @param {string=} sortBy="created" - Sort order. Options: `created`, `info/token_id`, `info/ordinal`, `price`, `nft/display_name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {string=} filter - Filter results by item name.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the item name.
* You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
* @param {Array<string>} editionFilters - Filter results by item edition.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the edition name.
* You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
* @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
* <br /><br />
* NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
* You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
* @param {Object=} priceRange - Filter min and/or max price (e.g. `{min: 1}` `{max: 2}` `{min: 1.50, max: 10.50})
* @param {string=} sellerAddress - Filter by a specific seller
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {Array<integer>=} collectionIndexes - If filtering by marketplace, filter by collection(s). The index refers to the index in the array `marketplace.collections`
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
*
* @returns {Promise<Object>} - Statistics about listings. All prices in USD.
*/
exports.ListingStats = async function() {
return this.FilteredQuery({mode: "listing-stats", ...(arguments[0] || {})});
};
/**
* Retrieve sales matching the specified parameters.
*
* @methodGroup Listings
* @namedParams
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
* @param {string=} sortBy="created" - Sort order. Options: `created`, `price`, `name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {string=} filter - Filter results by item name.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the item name.
* You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
* @param {Array<string>} editionFilters - Filter results by item edition.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the edition name.
* You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
* @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
* <br /><br />
* NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
* You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
* @param {string=} sellerAddress - Filter by a specific seller
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {Array<integer>=} collectionIndexes - If filtering by marketplace, filter by collection(s). The index refers to the index in the array `marketplace.collections`
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
*
* @returns {Promise<Object>} - Results of the query and pagination info
*/
exports.Sales = async function() {
return this.FilteredQuery({mode: "sales", ...(arguments[0] || {})});
};
/**
* Retrieve sales and transfers matching the specified parameters.
*
* @methodGroup Listings
* @namedParams
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
* @param {string=} sortBy="created" - Sort order. Options: `created`, `price`, `name`
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {string=} filter - Filter results by item name.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the item name.
* You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
* @param {Array<string>} editionFilters - Filter results by item edition.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the edition name.
* You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
* @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
* <br /><br />
* NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
* You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
* @param {string=} sellerAddress - Filter by a specific seller
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {Array<integer>=} collectionIndexes - If filtering by marketplace, filter by collection(s). The index refers to the index in the array `marketplace.collections`
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
*
* @returns {Promise<Object>} - Results of the query and pagination info
*/
exports.Transfers = async function() {
return this.FilteredQuery({mode: "transfers", ...(arguments[0] || {})});
};
/**
* Retrieve stats for listings matching the specified parameters.
*
* @methodGroup Listings
* @namedParams
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
* @param {string=} sortBy="created" -
* @param {boolean=} sortDesc=false - Sort results descending instead of ascending
* @param {string=} filter - Filter results by item name.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the item name.
* You can retrieve all available item names from the <a href="#.ListingNames">ListingNames method</a>.
* @param {Array<string>} editionFilters - Filter results by item edition.
* <br /><br />
* NOTE: This string must be an <b>exact match</b> on the edition name.
* You can retrieve all available item edition names from the <a href="#.ListingEditionNames">ListingEditionNames method</a>.
* @param {Array<Object>} attributeFilters - Filter results by item attributes. Each entry should include name and value (e.g. `[{name: "attribute-name", value: "attribute-value"}]`)
* <br /><br />
* NOTE: These filters must be an <b>exact match</b> on the attribute name and value.
* You can retrieve all available item attributes from the <a href="#.ListingAttributes">ListingAttributes method</a>.
* @param {string=} sellerAddress - Filter by a specific seller
* @param {string=} contractAddress - Filter results by the address of the NFT contract
* @param {string=} tokenId - Filter by token ID (if filtering by contract address)
* @param {string=} currency - Filter results by purchase currency. Available options: `usdc`
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {Array<integer>=} collectionIndexes - If filtering by marketplace, filter by collection(s). The index refers to the index in the array `marketplace.collections`
* @param {integer=} startTime - Filter by results listed after the specified time (in milliseconds since epoch)
* @param {integer=} endTime - Filter by results listed before the specified time (in milliseconds since epoch)
* @param {integer=} lastNDays - Filter by results listed in the past N days
*
* @returns {Promise<Object>} - Statistics about sales. All prices in USD.
*/
exports.SalesStats = async function() {
return this.FilteredQuery({mode: "sales-stats", ...(arguments[0] || {})});
};
/**
* Get the leaderboard rankings for the specified marketplace. If user address is specified, will return the ranking for the specified user (if present)
*
* @methodGroup Leaderboard
* @namedParams
* @param {Object=} marketplaceParams - Filter results by marketplace
* @param {string=} userAddress - Retrieve the ranking for a specific user
* @param {integer=} start=0 - PAGINATION: Index from which the results should start
* @param {integer=} limit=50 - PAGINATION: Maximum number of results to return
*
* @returns {Promise<Array|Object>} - Returns a list of leaderboard rankings or, if userAddress is specified, ranking for that user.
*/
exports.Leaderboard = async function({userAddress, marketplaceParams}) {
if(userAddress) {
let params = {
addr: Utils.FormatAddress(userAddress)
};
if(marketplaceParams) {
params.filter = [`tenant:eq:${(await this.MarketplaceInfo({marketplaceParams})).tenantId}`];
}
return ((await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "ranks"),
method: "GET",
queryParams: params
})
)) || [])[0];
}
return this.FilteredQuery({mode: "leaderboard", ...(arguments[0] || {})});
};
/**
* <b><i>Requires login</i></b>
*
* Create or update a listing for the specified item
*
* @methodGroup Listings
* @namedParams
* @param {string} contractAddress - The NFT contract address of the item
* @param {string} tokenId - The token ID of the item
* @param {number} price - The price of the listing, in USD
* @param {string=} listingId - (When editing a listing) The ID of the existing listing
*
* @returns {Promise<string>} - The listing ID of the created listing
*/
exports.CreateListing = async function({contractAddress, tokenId, price, listingId}) {
contractAddress = Utils.FormatAddress(contractAddress);
if(listingId) {
// Update
return await Utils.ResponseToFormat(
"text",
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt"),
method: "PUT",
body: {
id: listingId,
price: parseFloat(price)
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
} else {
// Create
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt"),
method: "POST",
body: {
contract: contractAddress,
token: tokenId,
price: parseFloat(price)
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
}
};
/**
* <b><i>Requires login</i></b>
*
* Remove the specified listing
*
* @methodGroup Listings
* @namedParams
* @param {string} listingId - The ID of the listing to remove
*/
exports.RemoveListing = async function({listingId}) {
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", listingId),
method: "DELETE",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
/**
* Retrieve all valid names for filtering listing sales names. Full item names are required for filtering sales results by name.
*
* Specify marketplace information to filter the results to only items offered in that marketplace.
*
* @methodGroup Listings
* @namedParams
* @param {Object} marketplaceParams - Parameters of a marketplace to filter results by
*
* @returns {Promise<Array<String>>} - A list of item names
*/
exports.SalesNames = async function({marketplaceParams}) {
let tenantId;
if(marketplaceParams) {
tenantId = (await this.MarketplaceInfo({marketplaceParams})).tenantId;
}
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "mkt", "names", "hst"),
method: "GET",
queryParams: tenantId ? { filter: `tenant:eq:${tenantId}` } : {}
})
);
};
/**
* Retrieve all valid names for filtering listings. Full item names are required for filtering listing results by name.
*
* Specify marketplace information to filter the results to only items offered in that marketplace.
*
* @methodGroup Listings
* @namedParams
* @param {Object} marketplaceParams - Parameters of a marketplace to filter results by
*
* @returns {Promise<Array<String>>} - A list of item names
*/
exports.ListingNames = async function({marketplaceParams}) {
let tenantId;
if(marketplaceParams) {
tenantId = (await this.MarketplaceInfo({marketplaceParams})).tenantId;
}
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "mkt", "names"),
method: "GET",
queryParams: tenantId ? { filter: `tenant:eq:${tenantId}` } : {}
})
);
};
/**
* Retrieve all valid edition names of the specified item. Full item edition names are required for filtering listing results by edition.
*
* @methodGroup Listings
* @namedParams
* @param {string} displayName - Display name of the item from which to request edition names
*
* @returns {Promise<Array<String>>} - A list of item editions
*/
exports.ListingEditionNames = async function({displayName}) {
return await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "mkt", "editions"),
queryParams: {
filter: `nft/display_name:eq:${displayName}`
},
method: "GET"
})
);
};
/**
* Retrieve names of all valid attributes for listed items. Full attribute names and values are required for filtering listing results by attributes.
*
* Specify marketplace information to filter the results to only items offered in that marketplace.
*
* @methodGroup Listings
* @namedParams
* @param {Object=} marketplaceParams - Parameters of a marketplace to filter results by
* @param {string=} displayName - Display name of the item from which to request attributes
*
* @returns {Promise<Array<String>>} - A list of valid attributes
*/
exports.ListingAttributes = async function({marketplaceParams, displayName}={}) {
let filters = [];
if(marketplaceParams) {
filters.push(`tenant:eq:${(await this.MarketplaceInfo({marketplaceParams})).tenantId}`);
}
if(displayName) {
filters.push(`nft/display_name:eq:${displayName}`);
}
const attributes = await Utils.ResponseToJson(
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "mkt", "attributes"),
method: "GET",
queryParams: {
filter: filters
}
})
);
return attributes
.map(({trait_type, values}) => ({ name: trait_type, values }))
.filter(({name}) =>
!["Content Fabric Hash", "Total Minted Supply", "Creator"].includes(name)
);
};
/* PURCHASE / CLAIM */
/**
* Claim the specified gift code
*
* Use the <a href="#.GiftClaimStatus">GiftClaimStatus</a> method to check minting status after claiming
*
* @methodGroup Purchase
* @namedParams
* @param {string} code - The OTP gift code to claim
*
* @returns {Promise<Object>} - Information about the claim, including the tenant associated with the item, the gift ID and the status op key
*/
exports.ClaimGift = async function({code}) {
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
method: "POST",
path: UrlJoin("as", "wlt", "gifts", "claim"),
body: {
otp_code: code
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
};
/**
* Claim the specified item from the specified marketplace
*
* Use the <a href="#.ClaimStatus">ClaimStatus</a> method to check minting status after claiming
*
* @methodGroup Purchase
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string} sku - The SKU of the item to claim
* @param {string=} email - Email address of the user. If specified, this will bind the user to the tenant of the specified marketplace
*/
exports.ClaimItem = async function({marketplaceParams, sku, email}) {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
await this.client.authClient.MakeAuthServiceRequest({
method: "POST",
path: UrlJoin("as", "wlt", "act", marketplaceInfo.tenant_id),
body: {
op: "nft-claim",
sid: marketplaceInfo.marketplaceId,
sku,
email
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
/**
* Redirect to the wallet app to purchase the specified item from the specified marketplace
*
* Use the <a href="#.PurchaseStatus">PurchaseStatus</a> method to check minting status after purchasing
*
* @methodGroup Purchase
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string} sku - The SKU of the item to claim
* @param {string=} confirmationId - Confirmation ID with which to reference this purchase. If not specified, a confirmation ID will be automatically generated. On success, the user will be returned to `successUrl` with the `confirmationId` as a URL parameter.
* @param {string} successUrl - The URL to redirect back to upon successful purchase
* @param {string} cancelUrl - The URL to redirect back to upon cancellation of purchase
*/
exports.PurchaseItem = async function({marketplaceParams, sku, confirmationId, successUrl, cancelUrl}) {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
window.location.href = this.FlowURL({
type: "action",
flow: "purchase",
marketplaceId: marketplaceInfo.marketplaceId,
parameters: {
sku,
confirmationId,
successUrl,
cancelUrl,
login: true,
auth: this.ClientAuthToken(),
}
});
};
/**
* Redirect to the wallet app to purchase the specified listing
*
* Use the <a href="#.PurchaseStatus">ListingPurchaseStatus</a> method to check minting status after purchasing
*
* @methodGroup Purchase
* @namedParams
* @param {Object=} marketplaceParams - Parameters of the marketplace
* @param {string} listingId - The SKU of the item to claim
* @param {string=} confirmationId - Confirmation ID with which to reference this purchase. If not specified, a confirmation ID will be automatically generated. On success, the user will be returned to `successUrl` with the `confirmationId` as a URL parameter.
* @param {string} successUrl - The URL to redirect back to upon successful purchase
* @param {string} cancelUrl - The URL to redirect back to upon cancellation of purchase
*/
exports.PurchaseListing = async function({marketplaceParams, listingId, confirmationId, successUrl, cancelUrl}) {
let marketplaceInfo;
if(marketplaceParams) {
marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
}
window.location.href = this.FlowURL({
type: "action",
flow: "purchase",
marketplaceId: marketplaceInfo && marketplaceInfo.marketplaceId,
parameters: {
listingId,
confirmationId,
successUrl,
cancelUrl,
login: true,
auth: this.ClientAuthToken(),
}
});
};
/* MINTING STATUS */
/**
* Return status of the specified listing purchase
*
* @methodGroup Status
* @namedParams
* @param {string} listingId - The ID of the listing
* @param {string} confirmationId - The confirmation ID of the purchase
*
* @returns {Promise<Object>} - The status of the purchase
*/
exports.ListingPurchaseStatus = async function({listingId, confirmationId}) {
try {
const listingStatus = await this.ListingStatus({listingId});
if(!listingStatus) {
throw Error("Unable to find info for listing " + listingId);
}
const statuses = await this.MintingStatus({tenantId: listingStatus.tenant});
return statuses
.find(status =>
status.op === "nft-transfer" &&
status.extra && status.extra[0] === confirmationId
) || { status: "none" };
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified marketplace purchase
*
* @methodGroup Status
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string} confirmationId - The confirmation ID of the purchase
*
* @returns {Promise<Object>} - The minting status of the purchaseed item(s)
*/
exports.PurchaseStatus = async function({marketplaceParams, confirmationId}) {
try {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
const statuses = await this.MintingStatus({tenantId: marketplaceInfo.tenant_id});
return statuses.find(status => status.op === "nft-buy" && status.confirmationId === confirmationId) || { status: "none" };
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified item claim
*
* @methodGroup Status
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string} sku - The SKU of the item claimed
*
* @returns {Promise<Object>} - The minting status of the claim
*/
exports.ClaimStatus = async function({marketplaceParams, sku}) {
try {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
const statuses = await this.MintingStatus({tenantId: marketplaceInfo.tenantId});
return statuses.find(status => status.op === "nft-claim" && status.marketplaceId === marketplaceInfo.marketplaceId && status.confirmationId === sku) || { status: "none" };
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified gift claim
*
* @methodGroup Status
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string=} confirmationId - The confirmation ID of the gift purchase
* @param {string=} giftId - The ID of the claimed gift
*
* @returns {Promise<Object>} - The transfer status of the gift claim
*/
exports.GiftClaimStatus = async function({marketplaceParams, confirmationId, giftId}) {
try {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
const statuses = await this.MintingStatus({tenantId: marketplaceInfo.tenantId});
// Status is a list of transfer statuses, may be multiple if quantity > 1
const responses = statuses.filter(status => status.op === "nft-transfer" && ((confirmationId && status.confirmationId === confirmationId) || (giftId && status.giftId === giftId))) || { status: "none" };
if(responses.length === 0) {
return { status: "none" };
} else {
if(responses.find(response => response.status === "failed")) {
return {
status: "failed",
op: "nft-transfer",
transfer_statuses: responses
};
} else if(responses.find(response => response.status !== "complete")) {
return {
status: "pending",
op: "nft-transfer",
transfer_statuses: responses
};
} else {
return {
status: "complete",
op: "nft-transfer",
transfer_statuses: responses,
items: responses.map(response => ({
token_addr: response.address,
token_id_str: response.tokenId
}))
};
}
}
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified entitlement claim
*
* @methodGroup Status
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string} purchaseId - The purchase ID of the entitlement, for confirmation of status
*
* @returns {Promise<Object>} - The mint status of the entitlement claim
*/
exports.EntitlementClaimStatus = async function({marketplaceParams, purchaseId}) {
try {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
const statuses = await this.MintingStatus({tenantId: marketplaceInfo.tenantId});
const responses = statuses.filter(status => status.op === "nft-claim-entitlement"
&& (purchaseId && purchaseId == status.confirmationId)) || { status: "none" };
if(responses.length === 0) {
return { status: "none" };
} else {
if(responses.find(response => response.status === "complete")) {
return {
status: "complete",
op: "nft-claim-entitlement",
items: [{
token_addr: responses[0].address,
token_id: responses[0].tokenId
}]
};
} else if(responses.find(response => response.status === "error")) {
return {
status: "error",
op: "nft-claim-entitlement",
};
} else {
return {
status: "pending",
op: "nft-claim-entitlement",
};
}
}
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified pack opening
*
* @methodGroup Status
* @namedParams
* @param {string} contractAddress - The NFT contract address of the opened pack
* @param {string} tokenId - The token ID of the opened pack
*
* @returns {Promise<Object>} - The status of the pack opening
*/
exports.PackOpenStatus = async function({contractAddress, tokenId}) {
try {
const tenantConfig = await this.TenantConfiguration({contractAddress});
const statuses = await this.MintingStatus({tenantId: tenantConfig.tenant});
return statuses.find(status => status.op === "nft-open" && Utils.EqualAddress(contractAddress, status.address) && status.tokenId === tokenId) || { status: "none" };
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified collection redemption
*
* @methodGroup Status
* @namedParams
* @param {Object} marketplaceParams - Parameters of the marketplace
* @param {string} confirmationId - The confirmation ID of the redemption
*
* @returns {Promise<Object>} - The status of the collection redemption
*/
exports.CollectionRedemptionStatus = async function({marketplaceParams, confirmationId}) {
try {
const statuses = await this.MintingStatus({marketplaceParams});
return statuses.find(status => status.op === "nft-redeem" && status.confirmationId === confirmationId) || { status: "none" };
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
/**
* Return status of the specified redeemable offer
*
* @methodGroup Status
* @namedParams
* @param {string=} tenantId - ID of the tenant for this NFT (not required if `marketplaceParams` is specified)
* @param {Object=} marketplaceParams - Parameters of the marketplace for this NFT (not required if `tenantId` is specified)
* @param {string} contractAddress - The address of the NFT contract
* @param {string} tokenId - The token ID of the NFT
* @param {string} offerId - The ID of the offer
*
* @returns {Promise<Object>} - The status of the offer redemption
*/
exports.RedeemableOfferStatus = async function({tenantId, marketplaceParams, contractAddress, tokenId, offerId}) {
try {
const statuses = await this.MintingStatus({marketplaceParams, tenantId});
contractAddress = Utils.FormatAddress(contractAddress);
return statuses.find(status =>
status.op === "nft-offer-redeem" &&
Utils.EqualAddress(status.address, contractAddress) &&
status.tokenId === (tokenId || "").toString() &&
status.offerId === (offerId || "").toString()
) || { status: "none" };
} catch(error) {
this.Log(error, true);
return { status: "unknown" };
}
};
exports.RedeemableCustomFulfillmentInfo = async function({redeemableTransactionId}) {
return await Utils.ResponseToJson(
this.stateStoreClient.Request({
method: "GET",
path: UrlJoin("code-fulfillment", this.network === "main" ? "main" : "demov3", "fulfill", redeemableTransactionId),
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
};
/* EVENTS */
exports.LoadDrop = async function({tenantSlug, eventSlug, dropId}) {
if(!this.drops){
this.drops = {};
}
if(!this.drops[tenantSlug]) {
this.drops[tenantSlug] = {};
}
if(!this.drops[tenantSlug][eventSlug]) {
this.drops[tenantSlug][eventSlug] = {};
}
if(!this.drops[tenantSlug][eventSlug][dropId]) {
const event = (await this.client.ContentObjectMetadata({
libraryId: this.mainSiteLibraryId,
objectId: this.mainSiteId,
metadataSubtree: UrlJoin("public", "asset_metadata", "tenants", tenantSlug, "sites", eventSlug, "info"),
resolveLinks: true,
linkDepthLimit: 2,
resolveIncludeSource: true,
produceLinkUrls: true,
select: [".", "drops"],
noAuth: true
})) || [];
const eventId = Utils.DecodeVersionHash(event["."].source).objectId;
event.drops.forEach(drop => {
drop = {
...drop,
eventId
};
this.drops[tenantSlug][eventSlug][drop.uuid] = drop;
this.drops[drop.uuid] = drop;
});
}
return this.drops[dropId];
};
exports.SubmitDropVote = async function({marketplaceParams, eventId, dropId, sku}) {
const marketplaceInfo = await this.MarketplaceInfo({marketplaceParams});
await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "act", marketplaceInfo.tenant_id),
method: "POST",
body: {
op: "vote-drop",
evt: eventId,
id: dropId,
itm: sku
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
exports.DropStatus = async function({marketplace, eventId, dropId}) {
try {
const response = await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "act", marketplace.tenant_id, eventId, dropId),
method: "GET",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
return response.sort((a, b) => a.ts > b.ts ? 1 : -1)[0] || { status: "none" };
} catch(error) {
this.Log(error, true);
return "";
}
};
/* OFFERS */
/**
* Retrieve offers for the specified parameters
*
* @methodGroup Offers
* @namedParams
* @param {string=} contractAddress - The address of an NFT contract
* @param {string=} tokenId - The token ID of an NFT
* @param {string=} buyerAddress - The address of the offerrer
* @param {string=} sellerAddress - The address of the offerree
* @param {Array<String>=} statuses - Status to filter results by. Allowed values: "ACTIVE", "ACCEPTED", "CANCELLED", "DECLINED", "INVALID"
* @param {number} start=0 - The index to start from
* @param {number=} limit=10 - The maximum number of results to return
*
* @returns {Promise<Array<Object>>} - Offers matching the specified filters
*/
exports.MarketplaceOffers = async function({contractAddress, tokenId, buyerAddress, sellerAddress, statuses, start=0, limit=10}) {
let path = UrlJoin("as", "mkt", "offers", "ls");
if(buyerAddress) {
path = UrlJoin(path, "b", Utils.FormatAddress(buyerAddress));
} else if(sellerAddress) {
path = UrlJoin(path, "s", Utils.FormatAddress(sellerAddress));
}
if(contractAddress) {
path = UrlJoin(path, "c", Utils.FormatAddress(contractAddress));
if(tokenId) {
path = UrlJoin(path, "t", tokenId);
}
}
let queryParams = {
start,
limit
};
if(statuses && statuses.length > 0) {
queryParams.include = statuses.join(",");
}
const offers = await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: path,
method: "GET",
queryParams
})
);
return offers
.map(offer => ({
...offer,
created: offer.created * 1000,
updated: offer.updated * 1000,
expiration: offer.expiration * 1000
}));
};
/**
* <b><i>Requires login</i></b>
*
* Create or update an offer on the specified NFT
*
* @methodGroup Offers
* @namedParams
* @param {string} contractAddress - The contract address of the NFT
* @param {string} tokenId - The token ID of the NFT
* @param {string=} offerId - IF modifying an existing offer, the ID of the offer
* @param {number} price - The amount to offer
* @param {number=} expiresAt - The time (in epoch ms) the offer will expire
*
* @returns {Promise<Object>} - Info about the created/updated offer
*/
exports.CreateMarketplaceOffer = async function({contractAddress, tokenId, offerId, price, expiresAt}) {
let response;
if(offerId) {
response = await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "offers", offerId),
method: "PUT",
body: {
price,
expiration: Math.floor(expiresAt / 1000)
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
} else {
response = await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "offers", contractAddress, tokenId),
method: "POST",
body: {
contract: contractAddress,
token: tokenId,
price,
expiration: Math.floor(expiresAt / 1000)
},
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
}
return response.offer_id;
};
/**
* <b><i>Requires login</i></b>
*
* Cancel the specified offer
*
* @methodGroup Offers
* @namedParams
* @param {string} offerId - The ID of the offer
*/
exports.RemoveMarketplaceOffer = async function({offerId}) {
return await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "offers", offerId),
method: "DELETE",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
/**
* <b><i>Requires login</i></b>
*
* Accept the specified offer
*
* @methodGroup Offers
* @namedParams
* @param {string} offerId - The ID of the offer
*/
exports.AcceptMarketplaceOffer = async function({offerId}) {
return await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "offers", "accept", offerId),
method: "PUT",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
/**
* <b><i>Requires login</i></b>
*
* Reject the specified offer
*
* @methodGroup Offers
* @namedParams
* @param {string} offerId - The ID of the offer
*/
exports.RejectMarketplaceOffer = async function({offerId}) {
return await this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "wlt", "mkt", "offers", "decline", offerId),
method: "PUT",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
});
};
/* Voting */
/**
* Retrieve the current status of the specified voting event
*
* @methodGroup Voting
* @namedParams
* @param {string} tenantId - The tenant ID of the marketplace in which the voting event is specified
* @param {string} votingEventId - The ID of the voting event
*
* @returns {Promise<Object>} - Info about the voting event, including the current user's votes and the current total voting tally
*/
exports.VoteStatus = async function ({tenantId, votingEventId}) {
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "votes", tenantId, votingEventId),
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
};
/**
* <b><i>Requires login</i></b>
*
* Cast a vote for the specified item in the specified voting event
*
* @methodGroup Voting
* @namedParams
* @param {string} tenantId - The tenant ID of the marketplace in which the voting event is specified
* @param {string} votingEventId - The ID of the voting event
* @param {string} sku - The SKU of the item to vote for
*
* @returns {Promise<Object>} - Info about the voting event, including the current user's votes and the current total voting tally
*/
exports.CastVote = async function ({tenantId, votingEventId, sku}) {
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "votes", tenantId, votingEventId, sku),
method: "POST",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
};
/**
* <b><i>Requires login</i></b>
*
* Revoke a previously cast vote for the specified item in the specified voting event
*
* @methodGroup Voting
* @namedParams
* @param {string} tenantId - The tenant ID of the marketplace in which the voting event is specified
* @param {string} votingEventId - The ID of the voting event
* @param {string} sku - The SKU of the item to vote for
*
* @returns {Promise<Object>} - Info about the voting event, including the current user's votes and the current total voting tally
*/
exports.RevokeVote = async function ({tenantId, votingEventId, sku}) {
return await Utils.ResponseToJson(
this.client.authClient.MakeAuthServiceRequest({
path: UrlJoin("as", "votes", tenantId, votingEventId, sku),
method: "DELETE",
headers: {
Authorization: `Bearer ${this.AuthToken()}`
}
})
);
};