Source

modules/MintingManager.ts

import { MintMarketpAPI } from "../core/apis/mint.marketp.api";
import {
  GetMintedTransactionResponse,
  MintERC20Params,
  MintERC20Response,
  MintERC721Params,
  MintERC721Response,
  GetMintedAssetsResponse,
  GetMintedAssetsParams,
  BulkMintERC721Params,
  BulkMintERC721ResponseData,
  FeeType,
  FeeData,
  BulkMintQueueAsyncParams,
  BulkMintQueueAsyncResponseData,
  BulkMintQueueAsyncParamsAPI,
  BulkMintQueueParams,
  QueryMintingParams,
  QueryMintingResponse
} from "../types";
import { EnvTypes } from "../typesBundle";

/**
 * Create MintingManager module
 * @class MintingManager
 * @param {EnvTypes} env Environment type (DEV / STAGING / PREPROD / PROD)
 * @example <caption>MintingManager instantiation</caption>
  const mintingManager = new MintingManager(EnvTypes.STAGING);
 *
 */
export class MintingManager {
  private mintMarketpAPI: MintMarketpAPI;

  constructor(env: EnvTypes) {
    this.mintMarketpAPI = new MintMarketpAPI(env);
  }


  private async createMintTransactionERC20(
    data: MintERC20Params
  ): Promise<MintERC20Response | undefined>{
    let mintTransactionERC20: any;
    try {
      if (this.validationParams(Object.keys(data), data)) {

        const result = await this.mintMarketpAPI.createMintTransactionERC20(data);
        if (result?.status === "success") {
          mintTransactionERC20 = result.data;
        }
      }
    } catch (error) {
      return error;
    }
    return mintTransactionERC20;
  }

  /**
   * @summary Create a single NFT (MINTABLE_ERC721) through mint transaction in Myria system
   * @param {MintERC721Params} data Request params for minting ERC721
   * @returns {MintERC721Response | undefined} Mint response data for ERC721 (including assets / transactionInformation...)
   * @example <caption>Sample function for createMintTransactionERC721({})</caption>
   * 
    const mintingManager: MintingManager = new MintingManager(env);

    const starkKey = '0xabc....'; // Your registered stark key with Myria system 
    const contractAddress = '0xdf...'; // Unique smart contract address for collection
    const metadataApiUrl = 'https://metadata-example.com'; // Sample of base metadata url
    const tokenId = 1; // Your unique token ID to identify and locate the NFT asset in the storage
    
    const percentage = 10; // 10% fee for purchase transaction to return to creator
    const royaltyRecipient = '0xpad....'; // Sample wallet address of receiver (author/creator of the NFT)

    const params: MintERC721Params = {
      starkKey: starkKey,
      contractAddress: contractAddress,
      uri: `${metadataApiUrl}/${tokenId}`,
      tokenId: String(tokenId),
      description: "mry asset",
      fees: [
        {
          percentage: feePercentage,
          receiptAddress: royaltyRecipient,
          feeType: FeeType.ROYALTY
        },
      ],
    };
    const mintTransactionResponse = await mintingManager.createMintTransactionERC721(
      params
    );

   */
  public async createMintTransactionERC721(
    data: MintERC721Params
  ): Promise<MintERC721Response | undefined> {
    let mintTransactionERC721: any;
    try {
      if (this.validationParams(Object.keys(data), data)) {

        const feeData = this.getDefaultFeeData(data.fees);
        const dataWithFees = {...data};
        dataWithFees.fees = feeData;
        
        const result = await this.mintMarketpAPI.createMintTransactionERC721(dataWithFees);
        if (result?.status === "success") {
          mintTransactionERC721 = result.data;
        }
      }
    } catch (error) {
      return error;
    }
    return mintTransactionERC721;
  }

  public getDefaultFeeData(feesData: FeeData[]): FeeData[] { 
    return feesData.map((item: FeeData) => {
      if (!item.feeType) {
        return {
          ...item,
          feeType: FeeType.ROYALTY
        }
      }
      return item;
    });
  }

  /**
   * @summary Get transaction details for minting
   * @param {number} transactionId Unique sequence ID of transaction
   * @returns {GetMintedTransactionResponse} Details information of minted transaction
   * @throws {string} Exception: TransactionId is required
   * @throws {string} Exception: Get minted transaction details failure with error: ${serverError}
   */
  public async getMintTransactionDetails(
    transactionId: number
  ): Promise<GetMintedTransactionResponse> {

    if (!transactionId) {
      throw new Error("TransactionId is required");
    }

    let result;
    try {
      result = await this.mintMarketpAPI.requestGetMintTransaction(transactionId);
    } catch (error) {
      throw new Error(`Get minted transaction details failure with error: [${error}]`);
    }
    return result;
  }

  public async getMintedAssetByStarkKey(
    data: GetMintedAssetsParams
  ): Promise<GetMintedAssetsResponse> {
    const result = await this.mintMarketpAPI.requestGetMintStarkKey(data);
    try {
      if (!data.starkKey) {
        throw new Error("StarkKey is required");
      } else
        return result;
    }
    catch (error) { return error; }
  }

  /**
   * @summary Processes the bulk mint for list of assets (MINTABLE_ERC721)
   * @param {BulkMintERC721Params} payload Request for bulk mint list of ERC721
   * @returns {BulkMintERC721ResponseData} Bulk mint response data
   * @throws {string} Exception: Stark Key is required
   * @throws {string} Exception: Contract address is required
   * @throws {string} Exception: Assets length should be greater than 0
   * @throws {string} Exception: (x) th asset's uri is required
   * @throws {string} Exception: (x) th asset's tokenId is required
   * @throws {string} Http Status 500: Bulk mint failed with internal server error
   * @example <caption>Sample function for bulkMintNfts({})</caption>
   
    const mintingManager: MintingManager = new MintingManager(env);

    const feePercentage = 2;
    const startTokenId = 1;
    const endTokenId = 30;

    const starkKey: string = config.stark_key;
    const contractAddress: string = config.collection_contract_address;
    const metadataApiUrl: string = config.metadata_api_url;
    const royaltyRecipient: string = config.public_key;

    let assetsToMint = [];

    console.log("Preparing assets to mint...");
    for (let i = startTokenId; i <= endTokenId; i++) {
      const asset: MintAssetErc721Info = {
        uri: `${metadataApiUrl}/${i}`,
        tokenId: String(i),
        description: 'mry asset',
        fees: [{
          percentage: feePercentage,
          receiptAddress: royaltyRecipient,
          feeType: FeeType.ROYALTY
        }]
      };
      assetsToMint.push(asset);
    }
    console.log(assetsToMint);

    const params: BulkMintERC721Params = {
      starkKey: starkKey,
      contractAddress: contractAddress,
      assets: assetsToMint,
      isSupportGetBulkMetadata: true,
      fees: [{
        percentage: feePercentage,
        receiptAddress: royaltyRecipient,
        feeType: FeeType.ROYALTY
      }]
    };

    console.log("Initiating a bulk minting...");
    const mintResult: BulkMintERC721ResponseData = await mintingManager.bulkMintNfts(params);
    console.log("Bulk minting is completed. Result: ", mintResult);
   */
  public async bulkMintNfts(
    payload: BulkMintERC721Params
  ): Promise<BulkMintERC721ResponseData> {

    if (!payload.starkKey) {
      throw new Error("Stark Key is required");
    }

    if (!payload.contractAddress) {
      throw new Error("Contract address is required");
    }

    if (payload.assets.length === 0) {
      throw new Error("Assets length should be greater than 0");
    }

    payload.assets.forEach((asset: any, index: number) => {
      if (!asset.uri) {
        throw new Error(`${index + 1}th asset's uri is required`);
      }
      if (!asset.tokenId) {
        throw new Error(`${index + 1}th asset's tokenId is required`);
      }
    });

    let bulkMintResponse: BulkMintERC721ResponseData;

    const feeData = payload.fees ? this.getDefaultFeeData(payload.fees): [];
    const dataWithFees = {...payload};
    dataWithFees.fees = feeData;

    try {
      const response = await this.mintMarketpAPI.bulkMintERC721(dataWithFees);

      if (response.status === "success") {
        bulkMintResponse = response.data;
      } else {
        throw new Error("Bulk mint failed with internal server error");
      }
    } catch (err) {
      return err
    }

    return bulkMintResponse;
  }

    /**
   * @summary Processes the bulk mint in queue for list of NFTs asynchronously (MINTABLE_ERC721)
   * @param {BulkMintQueueParams} payload Request params for bulk mint queue
   * @returns {BulkMintERC721ResponseData} Bulk mint queue response data
   * @throws {string} Exception: apiKey is required
   * @throws {string} Exception: RequestId is required
   * @throws {string} Exception: PartnerRefId is required
   * @throws {string} Exception: GroupRequestId is required
   * @throws {string} Exception: RequestDescription is required
   * @throws {string} Exception: AccountId is required
   * @throws {string} Exception: CollectionId is required
   * @throws {string} Exception: Assets length should be greater than 0
   * @throws {string} Exception: (x) th asset's tokenId is required
   * @throws {string} Exception: (x) th asset's description is required
   * @throws {string} Exception: (x) th asset's trackingId is required
   * @throws {string} Exception: (x) th asset's mintForStarkKey invalid
   * @throws {string} Http Status 500: Bulk mint failed with internal server error
   * @example <caption>Sample function for bulkMintNftsV2({})</caption>
   
    const mintingManager: MintingManager = new MintingManager(env);

    const feePercentage = 2;
    const startTokenId = 1;
    const endTokenId = 30;

    const requestId: string = config.requestId;
    const partnerRefId: string = config.partnerRefId;
    const groupRequestId: string = config.groupRequestId;
    const requestDescription: string = config.requestDescription;
    const accountId: string = config.accountId;
    const apiKey: string = config.apiKey;
    const collectionId: string = config.collectionId;
    const isSupportGetBulkMetadata: boolean = config.isSupportGetBulkMetadata;
    const royaltyRecipient: string = config.royaltyRecipient;

    let assetsToMintAsync: MintAssetErc721InfoAsync[] = [];

    console.log("Preparing assets to mint...");
    for (let i = startTokenId; i <= endTokenId; i++) {
      const asset: MintAssetErc721InfoAsync = {
        tokenId: String(i),
        description: 'mry asset',
        fees: [{
          percentage: feePercentage,
          receiptAddress: royaltyRecipient,
          feeType: FeeType.ROYALTY
        }],
        mintForStarkKey: '0x.....',
        trackingId: 'trackingID',
      };
      assetsToMintAsync.push(asset);
    }
    console.log(assetsToMintAsync);

    const params: BulkMintQueueParams = {
      apiKey,
      requestId,
      partnerRefId,
      groupRequestId,
      requestDescription,
      accountId,
      collectionId,
      assets: assetsToMintAsync,
      isSupportGetBulkMetadata,
      fees: [{
        percentage: feePercentage,
        receiptAddress: royaltyRecipient,
        feeType: FeeType.ROYALTY
      }]
    };

    console.log("Initiating a bulk minting...");
    const mintV2Result: BulkMintERC721ResponseData = await mintingManager.bulkMintNftsV2(params);
    console.log("Bulk minting is completed. Result: ", mintResult);
   */
    public async bulkMintNftsV2(
      payload: BulkMintQueueParams
    ): Promise<BulkMintERC721ResponseData> {
  
        
      if (!payload.apiKey) {
        throw new Error("Api key is required");
      }

      if (!payload.requestId) {
        throw new Error("RequestId is required");
      }
  
      if (!payload.partnerRefId) {
        throw new Error("PartnerRefId is required");
      }
      if (!payload.groupRequestId) {
        throw new Error("GroupRequestId is required");
      }
      if (!payload.requestDescription) {
        throw new Error("RequestDescription is required");
      }
      if (!payload.accountId) {
        throw new Error("AccountId is required");
      }
      if (!payload.collectionId) {
        throw new Error("CollectionId is required");
      }
      if (!(payload.assets.length > 0)) {
        throw new Error("Assets length should be greater than 0");
      }
  
      payload.assets.forEach((asset: any, index: number) => {
        if (!asset.tokenId) {
          throw new Error(`${index + 1}th asset's tokenId is required`);
        }
        if (!asset.description) {
          throw new Error(`${index + 1}th asset's description is required`);
        }
        if (!asset.trackingId) {
          throw new Error(`${index + 1}th asset's trackingId is required`);
        }
        if (asset.mintForStarkKey) {
          if(!asset.mintForStarkKey.includes('0x')) {
            throw new Error(`${index + 1}th asset's mintforStarkKey invalid!`);
          }
        }
      });
  
      let bulkMintResponse: BulkMintERC721ResponseData;
  
      const feeData = payload.fees ? this.getDefaultFeeData(payload.fees): [];
      const dataWithFees: BulkMintQueueAsyncParamsAPI = {...payload};
      dataWithFees.fees = feeData;
  
      try {
        const response = await this.mintMarketpAPI.requestBulkMintV2(dataWithFees, payload.apiKey);
  
        if (response.status === "success") {
          bulkMintResponse = response.data;
        } 
        else {
          throw new Error("Bulk mint failed with internal server error");
        }
      } catch (err) {
        throw new Error(err);
      }
  
      return bulkMintResponse;
    }

  /**
   * @summary Processes the bulk mint in queue for list of NFTs asynchronously (MINTABLE_ERC721)
   * @param {BulkMintQueueAsyncParams} payload Request params for bulk mint queue
   * @returns {BulkMintQueueAsyncResponseData} Bulk mint queue response data
   * @throws {string} Exception: apiKey is required
   * @throws {string} Exception: RequestId is required
   * @throws {string} Exception: PartnerRefId is required
   * @throws {string} Exception: GroupRequestId is required
   * @throws {string} Exception: RequestDescription is required
   * @throws {string} Exception: AccountId is required
   * @throws {string} Exception: CollectionId is required
   * @throws {string} Exception: Assets length should be greater than 0
   * @throws {string} Exception: (x) th asset's tokenId is required
   * @throws {string} Exception: (x) th asset's description is required
   * @throws {string} Exception: (x) th asset's trackingId is required
   * @throws {string} Exception: (x) th asset's mintForStarkKey invalid
   * @throws {string} Http Status 500: Bulk mint failed with internal server error
   * @example <caption>Sample function for bulkMintNftsQueueAsync({})</caption>
   
    const mintingManager: MintingManager = new MintingManager(env);

    const feePercentage = 2;
    const startTokenId = 1;
    const endTokenId = 30;

    const requestId: string = config.requestId;
    const partnerRefId: string = config.partnerRefId;
    const groupRequestId: string = config.groupRequestId;
    const requestDescription: string = config.requestDescription;
    const accountId: string = config.accountId;
    const apiKey: string = config.apiKey;
    const collectionId: string = config.collectionId;
    const isSupportGetBulkMetadata: boolean = config.isSupportGetBulkMetadata;
    const royaltyRecipient: string = config.royaltyRecipient;

    let assetsToMintAsync = [];

    console.log("Preparing assets to mint...");
    for (let i = startTokenId; i <= endTokenId; i++) {
      const asset: MintAssetErc721InfoAsync = {
        tokenId: String(i),
        description: 'mry asset',
        fees: [{
          percentage: feePercentage,
          receiptAddress: royaltyRecipient,
          feeType: FeeType.ROYALTY
        }],
        mintForStarkKey: '0x.....',
        trackingId: 'trackingID',
      };
      assetsToMintAsync.push(asset);
    }
    console.log(assetsToMintAsync);

    const params: BulkMintQueueAsyncParams = {
      apiKey,
      requestId,
      partnerRefId,
      groupRequestId,
      requestDescription,
      accountId,
      collectionId,
      assets: assetsToMintAsync,
      isSupportGetBulkMetadata,
      fees: [{
        percentage: feePercentage,
        receiptAddress: royaltyRecipient,
        feeType: FeeType.ROYALTY
      }]
    };

    console.log("Initiating a bulk minting...");
    const mintV2Result: BulkMintQueueAsyncResponseData = await mintingManager.bulkMintNftsQueueAsync(params);
    console.log("Bulk minting is completed. Result: ", mintResult);
   */
    public async bulkMintNftsQueueAsync(
      payload: BulkMintQueueAsyncParams
    ): Promise<BulkMintQueueAsyncResponseData> {
  
      if (!payload.apiKey) {
        throw new Error("Api key is required");
      }

      if (!payload.requestId) {
        throw new Error("RequestId is required");
      }
  
      if (!payload.partnerRefId) {
        throw new Error("PartnerRefId is required");
      }
      if (!payload.groupRequestId) {
        throw new Error("GroupRequestId is required");
      }
      if (!payload.requestDescription) {
        throw new Error("RequestDescription is required");
      }
      if (!payload.accountId) {
        throw new Error("AccountId is required");
      }
      if (!payload.collectionId) {
        throw new Error("CollectionId is required");
      }
      if (!(payload.assets.length > 0)) {
        throw new Error("Assets length should be greater than 0");
      }
  
      payload.assets.forEach((asset: any, index: number) => {
        if (!asset.tokenId) {
          throw new Error(`${index + 1}th asset's tokenId is required`);
        }
        if (!asset.description) {
          throw new Error(`${index + 1}th asset's description is required`);
        }
        if (!asset.trackingId) {
          throw new Error(`${index + 1}th asset's trackingId is required`);
        }
        if (asset.mintForStarkKey) {
          if(!asset.mintForStarkKey.includes('0x')) {
            throw new Error(`${index + 1}th asset's mintforStarkKey invalid!`);
          }
        }
      });
  
      let bulkMintAsyncResponse: BulkMintQueueAsyncResponseData;
  
      const feeData = payload.fees ? this.getDefaultFeeData(payload.fees): [];
      const dataWithFees: BulkMintQueueAsyncParamsAPI = {...payload};
      dataWithFees.fees = feeData;
  
      try {
        const response = await this.mintMarketpAPI.requestBulkMintAsync(dataWithFees, payload.apiKey);
  
        if (response.status === "success") {
          bulkMintAsyncResponse = response.data;
        } else {
          throw new Error("Bulk mint failed with internal server error");
        }
      } catch (err) {
        throw new Error(err);
      }
  
      return bulkMintAsyncResponse;
    }

    /**
   * @summary Processes query the list of NFTs which have been minted asynchronously (MINTABLE_ERC721)
   * @param {QueryMintingParams} payload Request params for querying the minted assets
   * @returns {QueryMintingResponse} Bulk mint queue response data
   * @throws {string} Exception: DeveloperApiKey is required
   * @throws {string} Exception: Group request ID is required
   * @throws {string} Exception: PartnerRefId is required
   * @example <caption>Sample function for QueryMintingParams({})</caption>
   
    const mintingManager: MintingManager = new MintingManager(env);

    const params: QueryMintingParams = {
      partnerRefId: 'Project ID of game which can be retrieved via Developer Portal',
      groupRequestId: 'Random group request ID',
      developerApiKey: 'Developer API key which can be retrieved via Developer Portal'
    }

    console.log("Initiating the request query for minting assets...");
    const queryMintedAssetResponse: QueryMintingResponse = await mintingManager.queryMintingAssets(params);
    console.log("Querying minted result: ", queryMintedAssetResponse);
   */
    public async queryMintingAssets(params: QueryMintingParams): Promise<QueryMintingResponse> {
      if (!params.developerApiKey) {
        throw new Error('Developer API Key is required');
      }

      if (!params.groupRequestId) {
        throw new Error('Group request ID is required');
      }

      if (!params.partnerRefId) {
        throw new Error('Partner reference ID is required');
      }

      try {
        const mintingResponse = await this.mintMarketpAPI.queryMintingAssets(params);

        if (mintingResponse.status === 'success') {
          return mintingResponse.data;
        }
        throw new Error('There is internal server error, please check again with response error');

      } catch (err) {
        throw new Error(err);
      }
      
    }

  // TODO to be refactor using schema validation
  validationParams(params: string[], data: any): boolean {
    for (const key of params) {
      if (key === "description") {
        continue;
      }
      if (!data[key]) {
        throw new Error(`${key} is required`);
      }
    }
    return true;
  }
}