UNPKG

myria-core-sdk

Version:

Latest version SDK

659 lines (612 loc) 41.7 kB
import { CollectionAPI } from "./../core/apis/collection.api"; import { ProjectAPI } from "../core/apis"; /** * Create CollectionManager module * @class CollectionManager * @param {EnvTypes} env Environment type (DEV / STAGING / PREPROD / PROD) * @example <caption>CollectionManager Instantiation</caption> const collectionManager = new CollectionManager(EnvTypes.STAGING); * */ export class CollectionManager { constructor(env) { this.collectionAPI = new CollectionAPI(env); this.projectAPI = new ProjectAPI(env); } /** * @summary Create collection data * @param {CreateCollectionParams} payload Create collection request params (name / collectionImageUrl / description / contractAddress / metadataUrl) * @returns {CreateCollectionResponse} Collection details response * @throws {string} Exception: StarkKey is required * @throws {string} Exception: Name is required * @throws {string} Exception: ContractAddress is required * @throws {string} Exception: MetadataAPIUri is required * @throws {string} Exception: OwnerPublicKey is required * @throws {string} Exception: ProjectID is required * @throws {string} Exception: Create Collection failure with internal server error * @example <caption>Sample of createCollection({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const contractAddress: string = config.collection_contract_address; const metadataApiUrl: string = config.metadata_api_url; const publicKey: string = config.public_key; const projectId: number = config.project_id; const starkKey: string = config.stark_key; const params: CreateCollectionParams = { name: "Test Collection 1", description: "Collection Test with SDK", contractAddress: contractAddress, metadataApiUrl: metadataApiUrl, ownerPublicKey: publicKey, projectId: projectId, starkKey: starkKey, }; console.log("Creating the collection..."); const collectionResponse: CreateCollectionResponse = await collectionManager.createCollection(params); console.log("Created collection response:"); console.log(JSON.stringify(collectionResponse, null, 2)); */ async createCollection(payload) { let createCollectionData; if (!payload.starkKey) { throw new Error("StarkKey is required"); } if (!payload.name) { throw new Error("Name is required"); } if (!payload.contractAddress) { throw new Error("ContractAddress is required"); } if (!payload.metadataApiUrl) { throw new Error("MetadataAPIUri is required"); } if (!payload.ownerPublicKey) { throw new Error("OwnerPublicKey is required"); } if (!payload.projectId) { throw new Error("ProjectId is required"); } { try { const createCollectionRes = await this.collectionAPI.createCollection(payload); if ((createCollectionRes === null || createCollectionRes === void 0 ? void 0 : createCollectionRes.status) === "success") { createCollectionData = createCollectionRes === null || createCollectionRes === void 0 ? void 0 : createCollectionRes.data; } else { throw new Error("Create Collection failure with internal server error"); } } catch (error) { throw new Error(error); } } return createCollectionData; } /** * @summary Get collection list * @param {GetCollectionParams} GetCollectionParams query collections params * @returns {CommonPaginateDataTypes<CollectionResponse[]>} Paginable collections data * @example <caption>Sample of getCollectionList({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const params: GetCollectionParams = { limit: 10, page: 1, isHot: true }; console.log("Get the collection..."); const collections: CommonPaginateDataTypes<CollectionResponse[]> = await collectionManager.getCollectionList(params); console.log("List of collection response:"); console.log(JSON.stringify(collections, null, 2)); */ async getCollectionList(params) { const collectionList = await this.collectionAPI.getCollectionList(params); return collectionList; } /** * @summary Get collection by developer api key and account ID * @param {GetCollectionByApiKeyParams} GetCollectionByApiKeyParams Params to get collections by developer api key and account ID * @returns {CollectionResponse[]} List of collections * @example <caption>Sample of getCollectionsByApiKey({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const params: GetCollectionByApiKeyParams = { accountId: 'uuid-get-from-dev-portal', apiKey: 'api-key-get-from-dev-portal' }; console.log("Get the collection..."); const collections: CollectionResponse[] = await collectionManager.getCollectionsByApiKey(params); console.log("List of collection response:"); console.log(JSON.stringify(collections, null, 2)); */ async getCollectionsByApiKey(params) { if (!params.accountId) { throw new Error('Account ID is required'); } if (!params.apiKey) { throw new Error('Developer API Key is required'); } const collections = await this.collectionAPI.getCollectionsByApiKeyV2(params); return collections; } async getCollections(params) { if (!params.accountId) { throw new Error('Myria User ID is required'); } if (!params.apiKey) { throw new Error('Developer API Key is required'); } const reqProjects = await this.projectAPI.getProjectsByUserIDAndApiKey(params.accountId, params.apiKey); let listProjects = []; if ((reqProjects === null || reqProjects === void 0 ? void 0 : reqProjects.status) === 'success') { listProjects = reqProjects.data || []; } const listCollections = []; for (const project of listProjects) { // Get list projects const collectionResponseData = await this.projectAPI.getCollectionListByProjectId(project.id); if (collectionResponseData.status === 'success') { listCollections.push(...collectionResponseData.data.collections); } else { continue; } } return listCollections; } /** * @summary Get collection details by ID * @param {number} id unique ID of the collection * @returns {CollectionDetailsResponseData} Collection details information data * @throws {string} Exception: Collection ID is required * @throws {string} Exception: Get collection by ID failed with internal server error * @example <caption>Sample of getCollectionById(collectionId) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const collectionId = 1; console.log("Get the collection..."); const collectionData: CollectionDetailsResponseData = await collectionManager.getCollectionById(collectionId); console.log(JSON.stringify(collectionData, null, 2)); */ async getCollectionById(id) { if (!id) { throw new Error("Collection ID is required"); } const collectionResponse = await this.collectionAPI.getCollectionById(id); if ((collectionResponse === null || collectionResponse === void 0 ? void 0 : collectionResponse.status) !== 'success') { throw new Error("Get collection by ID failed with internal server error"); } return collectionResponse.data; } /** * @summary Get collection metadata schema by smart contract address (deployed address of the collection) * @param {string} contractAddress The smart contract address of the collection * @returns {CollectionMetadataSchemaParams[]} List of metadata fields of the specific collection * @throws {string} Exception: Contract address is required * @example <caption>Sample of getCollectionMetadataByAddress(contractAddress) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const contractAddress = '0xFCB75a9De034b9DEff9aD1a12142c12E51665F97'; console.log("Get the collection metadata..."); const collectionMetadata: APIResponseType<CollectionMetadataSchemaParams[]> = await collectionManager.getCollectionMetadataByAddress(contractAddress); console.log(JSON.stringify(collectionMetadata, null, 2)); */ async getCollectionMetadataByAddress(contractAddress) { if (!contractAddress) { throw new Error('Contract address is required'); } const collectionByAddress = await this.collectionAPI.getCollectionMetadataByAddress(contractAddress); return collectionByAddress; } /** * @summary Create collection metadata schema by smart contract address (deployed address of the collection contract) * @param {string} contractAddress Smart contract address of the collection * @param {CreateCollectionMetadataParams} payload The metadata structure params * @returns {CollectionDetailsResponseData} List of metadata fields of the specific collection * @throws {string} Exception: Contract address is required * @throws {string} Exception: StarkKey is required * @throws {string} Exception: Key name of metadata is required * @throws {string} Exception: Internal server error * @example <caption>Sample of createCollectionMetadataByAddress(contractAddress, {}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const contractAddress = '0xFCB75a9De034b9DEff9aD1a12142c12E51665F97'; const payload: CreateCollectionMetadataParams = { metadata: [ { name: 'rarity', type: 'string', filterable: true, }, { name: 'power', type: 'string', filterable: true, }, { name: 'level', type: 'enum', filterable: true, }, { name: 'color', type: 'string', filterable: false, }, ]; starkKey: '0xFC....', }; console.log("Get the collection metadata..."); const collectionMetadata: CollectionDetailsResponseData = await collectionManager.createCollectionMetadataByAddress(contractAddress, payload); console.log(JSON.stringify(collectionMetadata, null, 2)); */ async createCollectionMetadataByAddress(contractAddress, payload) { let collectionByAddress; if (!contractAddress) { throw new Error("Contract address is required"); } if (!payload.starkKey) { throw new Error("StarkKey is required"); } payload.metadata.forEach((data, index) => { if (!data.name) { throw new Error(`${index + 1}th item's name for metadata is required!`); } }); try { collectionByAddress = await this.collectionAPI.createCollectionMetadataByAddress(contractAddress, payload); if ((collectionByAddress === null || collectionByAddress === void 0 ? void 0 : collectionByAddress.status) === "success") { collectionByAddress = collectionByAddress === null || collectionByAddress === void 0 ? void 0 : collectionByAddress.data; } else { throw new Error("Create Collection by smart contract address failure with internal server error"); } } catch (error) { throw new Error(error); } return collectionByAddress; } /** * @summary Update metadata schema (change field key) by smart contract address * @param {string} contractAddress The unique smart contract address for the collection * @param {string} name The existing key property in the metadata schema (for example we have the property power, and we want to change to power_character) * @param {UpdateCollectionMetadataParams} payload The request params for Updated Metadata * @returns {CollectionDetailsResponseData} The collection details information (including new metadata updated...) * @throws {string} Exception: Internal server error * * @example <caption>Sample of updateCollectionMetadataByAddress(contractAddress, name, {}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const contractAddress = '0xFCB75a9De034b9DEff9aD1a12142c12E51665F97'; const name = 'Rarity'; const payload: UpdateCollectionMetadataParams = { name: 'Rarity_1', type: 'string', filterable: true, starkKey: '0x09af....' }; console.log("Update the collection metadata by smart contract address..."); const collectionData: CollectionDetailsResponseData = await collectionManager.updateCollectionMetadataByAddress(contractAddress, name, payload); console.log(JSON.stringify(collectionData, null, 2)); */ async updateCollectionMetadataByAddress(contractAddress, name, payload) { let collectionByAddress; if (this.validationParams({ contractAddress, name: payload.name, starkKey: payload.starkKey })) { try { collectionByAddress = await this.collectionAPI.updateCollectionMetadataByAddress(contractAddress, name, payload); if ((collectionByAddress === null || collectionByAddress === void 0 ? void 0 : collectionByAddress.status) === "success") { collectionByAddress = collectionByAddress === null || collectionByAddress === void 0 ? void 0 : collectionByAddress.data; } else { throw new Error("Update Collection Metadata by smart contract address failure due to internal server error"); } } catch (error) { throw new Error(error); } } return collectionByAddress; } /** * @summary Get the collection by the public ID (uuid) * @param {string} publicId The unique public ID (UUID) of the collection * @returns {CollectionDetailsResponseData} Collection details information data (metadata schema, name, description, metadataUrl...) * @throws {string} Exception: The public ID is required * @example <caption>Sample of getCollectionByPublicId(publicId) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const publicId = 'f1d203eb-cd15-49ff-8852-41846a508bd8'; // unique UUID of the collection console.log("Get collection by public ID..."); const collectionData: CollectionDetailsResponseData = await collectionManager.getCollectionByPublicId(publicId); console.log(JSON.stringify(collectionData, null, 2)); */ async getCollectionByPublicId(publicId) { if (!publicId) { throw new Error("The public ID is required"); } const collectionByPublicId = await this.collectionAPI.getCollectionByPublicId(publicId); return collectionByPublicId; } /** * @summary Get the asset list by collection ID * @param {GetAssetByCollectionParams} payload The request params for querying the list of the assets * @returns {CommonPaginateDataTypes<AssetListResponse[]>} The paginated data response (including the list of the assets) * @throws {string} Exception: The collectionID is required * @throws {string} Exception: The assetType is required * @example <caption>Sample of getAssetByCollectionId({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const payload: GetAssetByCollectionParams = { collectionId: 1, assetType: 'NON_SALE', limit: 100, page: 1 }; console.log("Get asset list by collection ID"); const assetsData: CommonPaginateDataTypes<AssetListResponse[]> = await collectionManager.getAssetByCollectionId(payload); console.log(JSON.stringify(assetsData, null, 2)); */ async getAssetByCollectionId(payload) { if (!payload.collectionId) { throw new Error('The collectionID is required'); } if (!payload.assetType) { throw new Error('The assetType is required'); } const expectedAssetType = payload.assetTypeOutput || 'ASSETS'; payload.assetTypeOutput = expectedAssetType; const listAssetByCollectionId = await this.collectionAPI.getAssetByCollectionId(payload); return listAssetByCollectionId; } /** * @description Get total purchased assets group by user * @param {GetTotalAssetsByOwnerParams} totalPurchasedAssets Request for query total assets by owner params * @returns {CommonPaginateDataTypes<OwnerAssetsCount[]>} owner assets count result * @throws {string} Exception: CollectionId is required * @throws {string} Exception: Stark key is required * @throws {string} Exception: Internal server error * @throws {string} Exception: Get list failed: ${err} * @example <caption>Sample of getTotalPurchasedAssetsGroupedByUser({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const payload: GetTotalAssetsByOwnerParams = { collectionId: 1, starkKey: '0xFA....' }; console.log("Get total purchased assets group by users"); const ownerData: CommonPaginateDataTypes<OwnerAssetsCount[]> = await collectionManager.getTotalPurchasedAssetsGroupedByUser(payload); console.log(JSON.stringify(ownerData, null, 2)); */ async getTotalPurchasedAssetsGroupedByUser(totalPurchasedAssets) { if (!totalPurchasedAssets.collectionId) { throw new Error('CollectionId is required'); } if (!totalPurchasedAssets.starkKey) { throw new Error("Stark key is required"); } let response; try { const result = await this.collectionAPI.getTotalPurchasedAssetsGroupedByUser(totalPurchasedAssets.collectionId, totalPurchasedAssets.starkKey, totalPurchasedAssets.paginationType); if (result.status === "success") { response = result.data; } else { throw new Error('Fetch ownerlist failed with internal server error'); } } catch (err) { throw new Error(`Get ownerlist failed: ${err}`); } return response; } /** * @description Update collection by contract address and stark key * @param {UpdateCollectionByContractAddressParams} payload The updated collection requests params * @returns {CollectionDetailsResponseData} The collection details information * @throws {string} Exception: Contract Address is required * @throws {string} Exception: Stark key is required * @throws {string} Exception: Internal server error * @example <caption>Sample of updateCollectionByContractAddress({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const payload: UpdateCollectionByContractAddressParams = { contractAddress: '0xFC...', name: 'New name of the collection', starkKey: '0xabc...', description: 'New description of collection', collectionImageUrl: 'https://collection-banner-url.com' iconUrl: 'https://icon-avatar-collection.com' }; console.log("Update collection by smart contract address"); const collectionData: CollectionDetailsResponseData = await collectionManager.updateCollectionByContractAddress(payload); console.log(JSON.stringify(collectionData, null, 2)); */ async updateCollectionByContractAddress(payload) { if (!payload.contractAddress) { throw new Error("Contract Address is required"); } if (!payload.starkKey) { throw new Error("StarkKey is required"); } let updateResult; try { const response = await this.collectionAPI.updateCollectionByContractAddress(payload); if ((response === null || response === void 0 ? void 0 : response.status) === "success") { updateResult = response === null || response === void 0 ? void 0 : response.data; } else { throw new Error("Update collection failed with internal server error"); } } catch (err) { throw new Error(err); } return updateResult; } /** * @description Update collection by contract address and partner api key * @param {UpdateCollectionByCollectionIdAndApiKeyParams} payload The updated collection requests params * @returns {CollectionDetailsResponseData} The collection details information * @throws {string} Exception: Contract Address is required * @throws {string} Exception: Stark key is required * @throws {string} Exception: Myria account ID is required * @example <caption>Sample of updateCollectionByCollectionIdAndApiKey({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const payload: UpdateCollectionByCollectionIdAndApiKeyParams = { collectionId: '1', // Number string name: 'New name of the collection', accountId: '0xabc...', apiKey: '0x1acd...', // Partner API key description: 'New description of collection', collectionImageUrl: 'https://collection-banner-url.com' iconUrl: 'https://icon-avatar-collection.com' }; console.log("Update collection by smart contract address"); const collectionData: CollectionDetailsResponseData = await collectionManager.updateCollectionByCollectionIdAndApiKey(payload); console.log(JSON.stringify(collectionData, null, 2)); */ async updateCollectionByCollectionIdAndApiKey(payload) { if (!payload.collectionId) { throw new Error("Contract ID is required"); } if (!payload.accountId) { throw new Error("Myria account ID is required"); } if (!payload.apiKey) { throw new Error("Partner Api Key is required"); } let updateResult; try { const response = await this.collectionAPI.updateCollectionByCollectionIdAndApiKey(payload); if ((response === null || response === void 0 ? void 0 : response.status) === "success") { updateResult = response === null || response === void 0 ? void 0 : response.data; } else { throw new Error("Update collection failed with internal server error"); } } catch (err) { throw new Error(err); } return updateResult; } async getAttributesByCollectionId(id) { const attributesResponse = await this.collectionAPI.getAttributesByCollectionId(id); return attributesResponse; } /** * @summary Create the collection by using myria user ID and api key * @param {CreateCollectionByApiKeyParams} payload create Collection request params (name,collectionImageUrl?,description?,iconUrl?,contractAddress,ownerPublicKey,metadataApiUrl,projectId,accountId,apiKey) * @throws {string} Exception: Collection name is required * @throws {string} Exception: Contract address is required * @throws {string} Exception: Owner PublicKey is required * @throws {string} Exception: metadataApiUrl is required * @throws {string} Exception: Project ID is required * @throws {string} Exception: ApiKey is required * @throws {string} Exception: Myria User ID is required * @throws {string} Exception: Create project failed with internal server error * @returns {CreateCollectionResponse | undefined} Collection information data * @example <caption>Sample of createCollectionByApiKey() on testnet environment</caption> * const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const params: CreateCollectionByApiKeyParams = { name: name_Collection; collectionImageUrl?: 'abc.png'; description?: description; iconUrl?: abc.png; contractAddress: contractAddress; ownerPublicKey: Owner PublicKey; metadataApiUrl: metaDataApiURL; projectId: 1; accountId: accountId; apiKey: APIKey }; console.log("Create the Collection..."); const newCollectionResponse: CreateCollectionResponse = await collectionManager.createCollectionByApiKey(params); console.log("Created Collection response:"); console.log(JSON.stringify(newCollectionResponse, null, 2)); */ async createCollectionByApiKey(payload) { if (!payload.apiKey) { throw new Error("API Key is required"); } if (!payload.accountId) { throw new Error("Account ID is required"); } if (!payload.name) { throw new Error("Collection Name is required"); } if (!payload.contractAddress) { throw new Error("Contract Address is required"); } if (!payload.projectId) { throw new Error("Project Id is required"); } if (!payload.ownerPublicKey) { throw new Error("Owner Public Key is required"); } let collectionCreateRes; try { const result = await this.collectionAPI.createCollectionByMyriaUserIdAndApiKey(payload); if (result.status === 'success') { collectionCreateRes = result.data; } else { throw new Error('Create Collection failed with internal server error'); } } catch (err) { throw new Error(`Create Collection failed with ${err}`); } return collectionCreateRes; } /** * @description Validate the metadata with metadataSchema and metaData URL test * @param {ValidateMetadataParams} payload The updated collection requests params * @returns {ValidateMetadataResponse} The collection details information * @throws {string} Exception: Metadata Schema is required * @throws {string} Exception: Asset Metadata Url is required * @throws {string} Http Status Code 500: Validate metadata failed with internal server error * @example <caption>Sample of validateMetadata({}) on Staging env</caption> const collectionManager: CollectionManager = new CollectionManager(EnvTypes.STAGING); const payload: ValidateMetadataParams = { metadata: [ { "name": "type", "type": "string", "filterable": true }, { "name": "rarity", "type": "string", "filterable": true } ], "assetMetadataUri": "https://myria.com/v1/sigil/metadata/1" }; console.log("Validate Metadata by metadata url and metadataSchema"); const validateMetadataRespond: CollectionDetailsResponseData = await collectionManager.validateMetadata(payload); console.log(JSON.stringify(validateMetadataRespond, null, 2)); */ async validateMetadata(payload) { if (!payload.metadata.length) { throw new Error("Metadata Schema is required"); } if (!payload.assetMetadataUri) { throw new Error("Asset Metadata Uri is required"); } let resultValidateMetadata; try { const response = await this.collectionAPI.validateMetadataSchemaWithTestMetadataUrl(payload); if ((response === null || response === void 0 ? void 0 : response.status) === "success") { resultValidateMetadata = response === null || response === void 0 ? void 0 : response.data; } else { throw new Error("Validate metadata failed with internal server error"); } } catch (err) { throw new Error(err); } return resultValidateMetadata; } validationParams(params) { for (const keys of params) { if (!params[keys]) { throw new Error(`Invalid ${keys}`); } } return true; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29sbGVjdGlvbk1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9Db2xsZWN0aW9uTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFzQkEsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBSTlELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFMUM7Ozs7Ozs7R0FPRztBQUNILE1BQU0sT0FBTyxpQkFBaUI7SUFJNUIsWUFBWSxHQUFhO1FBQ3ZCLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztNQW9DRTtJQUNLLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxPQUErQjtRQUMzRCxJQUFJLG9CQUE4QyxDQUFDO1FBQ25ELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFO1lBQ3JCLE1BQU0sSUFBSSxLQUFLLENBQUMsc0JBQXNCLENBQUMsQ0FBQztTQUN6QztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFO1lBQ2pCLE1BQU0sSUFBSSxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQztTQUNyQztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxFQUFFO1lBQzVCLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztTQUNoRDtRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFO1lBQzNCLE1BQU0sSUFBSSxLQUFLLENBQUMsNEJBQTRCLENBQUMsQ0FBQztTQUMvQztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFO1lBQzNCLE1BQU0sSUFBSSxLQUFLLENB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