UNPKG

myria-core-sdk

Version:

Latest version SDK

972 lines (949 loc) 91.9 kB
import Web3 from "web3"; import { AssetMarketpAPI, CommonAPI } from "../core/apis"; import { TransactionAPI } from "../core/apis/transaction.api"; import { TokenType } from "../types"; import { CommonModule } from "./CommonModule"; import { QUANTUM_USDT, TOKEN_ADDRESS_USDT } from "../utils/Constants"; const QUANTUM_ERC20 = "10000000000"; /** * Create TransactionManager module * @class TransactionManager * @param {IMyriaClient} IMyriaClient Interface of Myria Client * @example <caption>TransactionManage instance.</caption> * // + Approach 1: const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const moduleFactory = ModuleFactory.getInstance(mClient); const transactionManager = moduleFactory.getTransactionManager(); // + Approach 2: const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const transactionManager = new TransactionManager(mClient); */ export class TransactionManager { constructor(mClient) { this.transactionAPI = new TransactionAPI(mClient.env); this.commonModule = new CommonModule(mClient); this.commonAPI = new CommonAPI(mClient.env); this.assetMarketplaceAPI = new AssetMarketpAPI(mClient.env); } /** * @summary Get transaction history list by stark key with paging options. This is designed for the purposes of small history reports sorted by latest date. * @description Paging can be shown one after another (you cannot request for page 3 without first seeing page 1 and 2, for example, as you need details of last transaction of previous page to query for next page). * @param {TransactionPagingDetails} TransactionPagingDetails Request params to query the list of transactions * @returns {TransactionData[]} List of transaction details */ async getTransactionList(payload) { const transactionList = await this.transactionAPI.getTransactionList(payload); return transactionList; } /** * @summary Get transaction details by transaction ID * @param {number} transactionId Unique ID of transaction * @returns {TransactionData} Transaction details information (including transactionStatus and createdAt, updatedAt...) * @throws {string} Exception: Transaction ID should be valided and greater than 0 * @throws {string} Exception: Get transaction details failed with internal server error */ async getTransactionDetails(transactionId) { if (!transactionId || transactionId === 0) { throw new Error("Transaction ID should be valided and greater than 0"); } const transactionResponseData = await this.transactionAPI.getTransactionDetails(transactionId); if (transactionResponseData.status !== "success") { throw new Error("Get transaction details failed with internal server error"); } return transactionResponseData.data; } /** * @summary Single Transfer ERC-721 (MINTABLE NFT) Token * + The function is just supported for MINTABLE_ERC721 only * @description After transfer was triggered, we can query the status of the transaction with the following functions: * + getTransactionDetails(transactionId: number) {return TransactionData} * + getTransactionsByPartnerRefID(partnerRefID: string) {return []TransactionData} * @param {TransferERC721Params} transferParams Transfer ERC-721 token params (including sender and receiver's information) * @throws {string} Exception: Sender vault is not found * @throws {string} Exception: Receiver vault is not found * @throws {string} Http Status 400 - Sender/Receiver vaults does not exist * @throws {string} Http Status 400 - Signature is invalid * @throws {string} Http Status 400 - Vault IDs does not have enough funds * @returns {TransferResponse} Transaction details (such as transactionID, transactionStatus...) */ async transferERC721Token(transferParams) { var _a, _b, _c; const requestSenderVaultERC721 = { starkKey: transferParams.senderPublicKey, tokenId: transferParams.tokenId, tokenAddress: transferParams.tokenAddress, }; const requestReceiverVaultERC721 = { starkKey: transferParams.receiverPublicKey, tokenId: transferParams.tokenId, tokenAddress: transferParams.tokenAddress, }; const senderVault = await this.commonAPI.createVaultForMintableERC721(requestSenderVaultERC721); if (senderVault.status !== "success") { const error = { message: "Retrieve vaults ERC721 for sender has failed", code: "", error: requestReceiverVaultERC721 }; throw Error(JSON.stringify(error)); } const receiverVault = await this.commonAPI.createVaultForMintableERC721(requestReceiverVaultERC721); if (receiverVault.status !== "success") { const error = { message: "Retrieve vaults ERC721 for receiver has failed", code: "", error: JSON.stringify(requestReceiverVaultERC721) }; throw Error(JSON.stringify(error)); } const transferCommonParams = { senderVaultId: (_a = senderVault.data) === null || _a === void 0 ? void 0 : _a.vaultId, senderPublicKey: transferParams.senderPublicKey, senderWalletAddress: transferParams.senderWalletAddress, receiverVaultId: (_b = receiverVault.data) === null || _b === void 0 ? void 0 : _b.vaultId, receiverPublicKey: transferParams.receiverPublicKey, assetId: (_c = senderVault.data) === null || _c === void 0 ? void 0 : _c.assetId, quantizedAmount: transferParams.quantizedAmount, tokenType: TokenType.MINTABLE_ERC721, groupRequestId: transferParams.groupRequestId, partnerRefId: transferParams.partnerRefId, myriaPrivateStarkKey: transferParams.myriaPrivateKey, path: "/v1/transactions/transfer", }; const transferResult = await this.transferTokenCommon(transferCommonParams); return transferResult; } /** * @summary Asynchronous bulk transfer for NFT Tokens (such as: ERC-721 Tokens, Marketplace NFTs) * - Function only supports MINTABLE_ERC721 and NFTs which are minted on Myria System * @param {TransferTokenParams} transferTokenParams Data regarding sender and receivers relevant for the transfer. * @description After bulk transfer was triggered, we can query the status of the batch with the following functions: * + getTransactionsByGroupRequestIDAndPartnerRefID(groupRequestID: string, partnerRefID: string) * + getTransactionsByPartnerRefID(partnerRefID: string) * @returns {BulkTransferTokenResponse} Transaction data list which have been captured and validated * - Response structure consist of 3 group of transactions failed[] / success[] / validationFailed[] * - All transactions in failed[], relate to failures due to not enough funds or other internal server errors. These transactions cannot be processed. * - All transactions in validationFailed[], relate to failures due to validation such as L2's signature. These can be retried with amended data. * - All transactions in success[], indicate that they have been recorded and will be processed by the system. * @throws {string} Exception: Sender wallet address is required * @throws {string} Exception: Bulk transfer should include at least one transfer * @throws {string} Exception: Receiver wallet address is required * @throws {string} Exception: Only MINTABLE_ERC-721 tokens are valid for this type of bulk transfer * @throws {string} Exception: Token address is required * @throws {string} Error code 409 - Request-ID/Group-Request-ID is already exists * @throws {string} Http error code 400 - User wallet (sender or receiver) is not registered * @throws {string} Http error code 400 - Vault ID does not have enough funds * @throws {string} Http error code 400 - Signature is invalid * @example <caption>Sample code on Testnet (Staging) env</caption> * const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const YOUR_NFT_CONTRACT_ADDRESS = "0xA06116D9...."; const RECEIVER_WALLET_ADDRESS = '0xd0D8A467E....'; // Your receiver/users wallet address const SENDER_WALLET_ADDRESS = '0x724f337bF0F....'; // Must be the owner of tokens, sender wallet address const moduleFactory = ModuleFactory.getInstance(mClient); const transactionManager = moduleFactory.getTransactionManager(); const transferredItems: ItemSignableTransferParams[] = [ { quantizedAmount: 1, // Should be 1 as always receiverWalletAddress: RECEIVER_WALLET_ADDRESS, tokenType: TokenType.MINTABLE_ERC721, tokenData: { tokenAddress: YOUR_NFT_CONTRACT_ADDRESS, tokenId: '1' // Your minted token ID }, }, { quantizedAmount: 1, receiverWalletAddress: RECEIVER_WALLET_ADDRESS, tokenType: TokenType.MINTABLE_ERC721, tokenData: { tokenAddress: YOUR_NFT_CONTRACT_ADDRESS, tokenId: '2' // Your minted token ID }, }, { quantizedAmount: 1, receiverWalletAddress: RECEIVER_WALLET_ADDRESS, tokenType: TokenType.MINTABLE_ERC721, tokenData: { tokenAddress: YOUR_NFT_CONTRACT_ADDRESS, tokenId: '3' // Your minted token ID }, }, ]; const transferTokenParams: TransferTokenParams = { senderWalletAddress: SENDER_WALLET_ADDRESS, groupRequestId: '7257d29c-c96a-4302-8eaf-368a0d62b977', // Can use random UUID to generate groupRequestID requestId: '7257d29c-c96a-4302-8eaf-368a0d62b977', // Can use random UUID to generate requestID partnerRefId: 'Project-ID', // Project-ID on Myria System description: 'Test-Test Bulk Transfer', items: transferredItems, }; const transferResult = await transactionManager.bulkTransferNfts( transferTokenParams, ); * */ async bulkTransferNfts(transferTokenParams) { if (!transferTokenParams.senderWalletAddress) { throw new Error("Sender wallet address is required"); } if (!transferTokenParams.items || transferTokenParams.items.length === 0) { throw new Error("Bulk transfer should include at least one transfer."); } const transferredItems = []; transferTokenParams.items.forEach((item) => { var _a, _b; if (!item.receiverWalletAddress) { throw new Error("Receiver wallet address is required"); } if (item.tokenType !== (TokenType.ERC721 && TokenType.MINTABLE_ERC721)) { throw new Error("Only MINTABLE_ERC-721 tokens are valid for this type of bulk transfer"); } if (!((_a = item.tokenData) === null || _a === void 0 ? void 0 : _a.tokenAddress)) { throw new Error("Token address is required"); } if (!((_b = item.tokenData) === null || _b === void 0 ? void 0 : _b.tokenId)) { throw new Error("Token ID is required for transfer ERC-721 tokens "); } const transferredItem = { quantizedAmount: item.quantizedAmount, receiverWalletAddress: item.receiverWalletAddress, tokenType: item.tokenType, tokenData: item.tokenData, }; transferredItems.push(transferredItem); }); // Call signable API endpoint const signableBulkTransferredParams = { senderWalletAddress: transferTokenParams.senderWalletAddress, items: transferredItems, }; const fullPayloadTransferData = await this.transactionAPI.signableBulkTransfer(signableBulkTransferredParams); if (!fullPayloadTransferData) { throw new Error("Transfer payload data is required"); } // Create generateFullPayloadForBulkTransfer list of signature const fullPayloadTransferred = await this.commonModule.generateFullPayloadForBulkTransfer(transferTokenParams.senderWalletAddress, TokenType.MINTABLE_ERC721, fullPayloadTransferData, transferTokenParams.myriaPrivateKey); console.log("Full transfer payload -> ", fullPayloadTransferred); // Trigger transfer const bulkTransferRequestApi = { senderWalletAddress: transferTokenParams.senderWalletAddress, requestId: transferTokenParams.requestId, groupRequestId: transferTokenParams.groupRequestId, partnerRefId: transferTokenParams.partnerRefId, description: transferTokenParams.description, items: fullPayloadTransferred, isWaitingForValidation: transferTokenParams.isWaitingForValidation }; const bulkTransferResult = await this.assetMarketplaceAPI.bulkTransferERC721Token(bulkTransferRequestApi); console.log("Bulk Transfer Response Nft API -> ", JSON.stringify(bulkTransferResult)); return bulkTransferResult; } async bulkTransferNftsV2(transferTokenParams) { if (!transferTokenParams.senderWalletAddress) { throw new Error("Sender wallet address is required"); } if (!transferTokenParams.items || transferTokenParams.items.length === 0) { throw new Error("Bulk transfer should include at least one transfer."); } const transferredItems = []; transferTokenParams.items.forEach((item) => { var _a, _b; if (!item.receiverWalletAddress) { throw new Error("Receiver wallet address is required"); } if (item.tokenType !== (TokenType.ERC721 && TokenType.MINTABLE_ERC721)) { throw new Error("Only MINTABLE_ERC-721 tokens are valid for this type of bulk transfer"); } if (!((_a = item.tokenData) === null || _a === void 0 ? void 0 : _a.tokenAddress)) { throw new Error("Token address is required"); } if (!((_b = item.tokenData) === null || _b === void 0 ? void 0 : _b.tokenId)) { throw new Error("Token ID is required for transfer ERC-721 tokens "); } const transferredItem = { quantizedAmount: item.quantizedAmount, receiverWalletAddress: item.receiverWalletAddress, tokenType: item.tokenType, tokenData: item.tokenData, }; transferredItems.push(transferredItem); }); // Call signable API endpoint const signableBulkTransferredParams = { senderWalletAddress: transferTokenParams.senderWalletAddress, items: transferredItems, }; const fullPayloadTransferData = await this.transactionAPI.signableBulkTransfer(signableBulkTransferredParams); if (!fullPayloadTransferData) { throw new Error("Transfer payload data is required"); } // Create generateFullPayloadForBulkTransfer list of signature const fullPayloadTransferred = await this.commonModule.generateFullPayloadForBulkTransfer(transferTokenParams.senderWalletAddress, TokenType.MINTABLE_ERC721, fullPayloadTransferData, transferTokenParams.myriaPrivateKey); console.log("Full transfer payload -> ", fullPayloadTransferred); // Trigger transfer const bulkTransferRequestApi = { senderWalletAddress: transferTokenParams.senderWalletAddress, requestId: transferTokenParams.requestId, groupRequestId: transferTokenParams.groupRequestId, partnerRefId: transferTokenParams.partnerRefId, description: transferTokenParams.description, items: fullPayloadTransferred, isWaitingForValidation: transferTokenParams.isWaitingForValidation }; const bulkTransferResult = await this.assetMarketplaceAPI.bulkTransferNfts(bulkTransferRequestApi); console.log("Bulk Transfer Response Nft API -> ", JSON.stringify(bulkTransferResult)); return bulkTransferResult; } async getSignableDetailsTransferERC20(transferTokenParams) { if (!transferTokenParams.senderWalletAddress) { throw new Error("Sender wallet address is required"); } if (!transferTokenParams.items || transferTokenParams.items.length === 0) { throw new Error("Bulk transfer should include at least one transfer"); } const transferredItems = []; transferTokenParams.items.forEach((item) => { var _a; if (!item.receiverWalletAddress) { throw new Error("Receiver wallet address is required"); } if (item.tokenType !== TokenType.ERC20 && item.tokenType !== TokenType.MINTABLE_ERC20) { throw new Error("Only ERC20 Tokens are valid for this type of bulk transfer"); } if (!((_a = item.tokenData) === null || _a === void 0 ? void 0 : _a.tokenAddress)) { throw new Error("Token address is required"); } const transferredItem = { quantizedAmount: item.quantizedAmount, receiverWalletAddress: item.receiverWalletAddress, tokenType: item.tokenType, tokenData: item.tokenData, }; transferredItems.push(transferredItem); }); // Call signable API endpoint const signableBulkTransferredParams = { senderWalletAddress: transferTokenParams.senderWalletAddress, items: transferredItems, }; console.time("Signable_Transfer_ERC20"); const fullPayloadTransferData = await this.transactionAPI.signableBulkTransfer(signableBulkTransferredParams); console.timeEnd("Signable_Transfer_ERC20"); // console.log("Full signable transfer payload", fullPayloadTransferData); if (!fullPayloadTransferData) { throw new Error("Transfer payload data is required"); } // Create generateFullPayloadForBulkTransfer list of signature console.time("Generate_FullPayloadTransfer_ERC20"); const fullPayloadTransferred = await this.commonModule.generateFullPayloadForBulkTransfer(transferTokenParams.senderWalletAddress, TokenType.ERC20, fullPayloadTransferData, transferTokenParams.myriaPrivateKey); console.timeEnd("Generate_FullPayloadTransfer_ERC20"); // Trigger transfer const payloadTransferDetails = { senderWalletAddress: transferTokenParams.senderWalletAddress, requestId: transferTokenParams.requestId, groupRequestId: transferTokenParams.groupRequestId, partnerRefId: transferTokenParams.partnerRefId, description: transferTokenParams.description, items: fullPayloadTransferred, isWaitingForValidation: transferTokenParams.isWaitingForValidation, }; return payloadTransferDetails; } /** * @summary Async bulk transfer for ERC-20 Tokens (such as: Myria Tokens, ...) * - Function only supports ERC20 and Myria Tokens (ERC20) which are registered in Myria System already (i.e. via a deposit). * @param {TransferTokenParams} transferTokenParams Data regarding sender and receivers relevant for the transfer. * @description After bulk transfer was triggered, we can query the status of the batch with the following functions: * + getTransactionsByGroupRequestIDAndPartnerRefID(groupRequestID: string, partnerRefID: string) * + getTransactionsByPartnerRefID(partnerRefID: string) * @example <caption>Sample code on Testnet (Staging) env</caption> const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const MYR_TOKEN_ADDRESS_EXAMPLE = "0xA06116D9...."; // ERC-20 token address - and make sure it is registered in Myria System already const RECEIVER_WALLET_ADDRESS = '0xd0D8A467E....'; // Your receiver/users wallet address const SENDER_WALLET_ADDRESS = '0x724f337bF0F....'; // Must be the owner of tokens, sender wallet address const moduleFactory = ModuleFactory.getInstance(mClient); const transactionManager = moduleFactory.getTransactionManager(); const transferredItems: ItemSignableTransferParams[] = [ { quantizedAmount: String(convertAmountToQuantizedAmount(1)), receiverWalletAddress: RECEIVER_WALLET_ADDRESS, tokenType: TokenType.ERC20, tokenData: { tokenAddress: MYR_TOKEN_ADDRESS_EXAMPLE, }, }, { quantizedAmount: String(convertAmountToQuantizedAmount(2)), receiverWalletAddress: RECEIVER_WALLET_ADDRESS, tokenType: TokenType.ERC20, tokenData: { tokenAddress: MYR_TOKEN_ADDRESS_EXAMPLE, }, }, { quantizedAmount: String(convertAmountToQuantizedAmount(3)), receiverWalletAddress: RECEIVER_WALLET_ADDRESS, tokenType: TokenType.ERC20, tokenData: { tokenAddress: MYR_TOKEN_ADDRESS_EXAMPLE, }, }, ]; const transferTokenParams: TransferTokenParams = { senderWalletAddress: SENDER_WALLET_ADDRESS, groupRequestId: '7257d29c-c96a-4302-8eaf-368a0d62b977', // Can use random UUID to generate groupRequestID requestId: '7257d29c-c96a-4302-8eaf-368a0d62b977', // Can use random UUID to generate requestID partnerRefId: 'Project-ID', // Partner project ID description: 'Test-Test Bulk Transfer', items: transferredItems, }; const transferResult = await transactionManager.bulkTransferERC20Token( transferTokenParams, ); * @returns {BulkTransferTokenResponse} Transaction data list which have been captured and validated * - Response structure consist of 3 group of transactions: failed[] / success[] / validationFailed[] * - All transactions in failed[], relate to failures due to not enough funds or other internal server errors. These transactions cannot be processed. * - All transactions in validationFailed[], relate to failures due to validation such as L2's signature. These can be retried with amended data. * - All transactions in success[], indicate that they have been recorded and will be processed by the system. * @throws {string} Exception: Sender wallet address is required * @throws {string} Exception: Bulk transfer should include at least one transfer * @throws {string} Exception: Receiver wallet address is required * @throws {string} Exception: Only ERC20 Tokens are valid for this type of bulk transfer * @throws {string} Exception: Token address is required * @throws {string} Http error code 409 - Request-ID/Group-Request-ID already exists * @throws {string} Http error code 400 - User wallet (sender or receiver) is not registered * @throws {string} Http error code 400 - Vault ID does not have enough funds * @throws {string} Http error code 400 - Signature is invalid */ async bulkTransferERC20Token(transferTokenParams) { if (!transferTokenParams.senderWalletAddress) { throw new Error("Sender wallet address is required"); } if (!transferTokenParams.items || transferTokenParams.items.length === 0) { throw new Error("Bulk transfer should include at least one transfer"); } const transferredItems = []; transferTokenParams.items.forEach((item) => { var _a, _b; if (!item.receiverWalletAddress) { throw new Error("Receiver wallet address is required"); } if (item.tokenType !== TokenType.ERC20 && item.tokenType !== TokenType.MINTABLE_ERC20) { throw new Error("Only ERC20 Tokens are valid for this type of bulk transfer"); } if (!((_a = item.tokenData) === null || _a === void 0 ? void 0 : _a.tokenAddress)) { throw new Error("Token address is required"); } if (((_b = item.tokenData) === null || _b === void 0 ? void 0 : _b.tokenAddress) === TOKEN_ADDRESS_USDT) { item.tokenData.quantum = QUANTUM_USDT; } const transferredItem = { quantizedAmount: item.quantizedAmount, receiverWalletAddress: item.receiverWalletAddress, tokenType: item.tokenType, tokenData: item.tokenData, }; transferredItems.push(transferredItem); }); // Call signable API endpoint const signableBulkTransferredParams = { senderWalletAddress: transferTokenParams.senderWalletAddress, items: transferredItems, }; console.time("Signable_Transfer_ERC20"); const fullPayloadTransferData = await this.transactionAPI.signableBulkTransfer(signableBulkTransferredParams); console.timeEnd("Signable_Transfer_ERC20"); // console.log("Full signable transfer payload", fullPayloadTransferData); if (!fullPayloadTransferData) { throw new Error("Transfer payload data is required"); } // Create generateFullPayloadForBulkTransfer list of signature console.time("Generate_FullPayloadTransfer_ERC20"); const fullPayloadTransferred = await this.commonModule.generateFullPayloadForBulkTransfer(transferTokenParams.senderWalletAddress, TokenType.ERC20, fullPayloadTransferData, transferTokenParams.myriaPrivateKey); console.timeEnd("Generate_FullPayloadTransfer_ERC20"); // Trigger transfer const bulkTransferRequestApi = { senderWalletAddress: transferTokenParams.senderWalletAddress, requestId: transferTokenParams.requestId, groupRequestId: transferTokenParams.groupRequestId, partnerRefId: transferTokenParams.partnerRefId, description: transferTokenParams.description, items: fullPayloadTransferred, isWaitingForValidation: transferTokenParams.isWaitingForValidation, }; console.time("BulkTransfer_ERC20"); const bulkTransferResult = await this.transactionAPI.bulkTransferERC20Token(bulkTransferRequestApi); console.timeEnd("BulkTransfer_ERC20"); console.log("Bulk Transfer Response ERC-20 API -> ", JSON.stringify(bulkTransferResult)); return bulkTransferResult; } // /** // * @description Query the list of the transaction based on request ID // * @param {string} requestID The unique request ID of the transaction // * @throws {string} Exception: RequestID is required // * @returns {TransactionData[]} Transaction data list (such as transactionID, transactionStatus...) // * @example <caption>Sample code on Testnet (Staging) env</caption> // const mClient: IMyriaClient = { // networkId: Network.SEPOLIA, // provider: web3Instance.currentProvider, // web3: web3Instance, // env: EnvTypes.STAGING, // }; // const requestID = "923a78a2-49a2-4eb9-8163-20dddd524a8c"; // const moduleFactory = ModuleFactory.getInstance(mClient); // const transactionManager = moduleFactory.getTransactionManager(); // const result = await transactionManager.getTransactionsByRequestID(requestID); // console.log('Transaction result -> ', result); // */ async getTransactionsByRequestID(requestID) { if (!requestID) { throw new Error("RequestID is required"); } return this.transactionAPI.getTransactionsByRequestID(requestID); } /** * @description Query a list of transactions based on group request ID and Partner Ref ID * @param {string} groupReqID The unique group request ID of the transaction batch * @param {string} partnerRefID The unique partner reference ID as Project ID * @param {TransactionPagingDetails=} transactionPaging The pagination params (which included starkKey, limit, createdAt, transactionCategory for query next page) * @throws {string} Exception: Partner Reference ID is required * @throws {string} Exception: Group Request ID is required * @returns {TransactionData[]} List of transactions data which indicate the status of batch, transaction results, transaction details information * (such as transactionID, transactionStatus...) * @example <caption>Sample code on Testnet (Staging) env</caption> const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const groupRequestID = "e2fb1ef6-680b-4515-9ca6-0c46bc026ecd"; const partnerRefId = "10"; // Unique ItemSignableTransferParamsProject ID in Myria System const moduleFactory = ModuleFactory.getInstance(mClient); const transactionManager = moduleFactory.getTransactionManager(); const result = await transactionManager.getTransactionsByGroupRequestIDAndPartnerRefID( groupRequestID, partnerRefId ); console.log('Transaction result -> ', result); */ async getTransactionsByGroupRequestIDAndPartnerRefID(groupReqID, partnerRefID, transactionPaging) { if (!partnerRefID) { throw new Error("Partner Reference ID is required"); } if (!groupReqID) { throw new Error("Group Request ID is required"); } return this.transactionAPI.getTransactionsByGroupReqIDAndPartnerRefID(groupReqID, partnerRefID, transactionPaging); } /** * @description Query a list of transactions based on partner reference ID (which should be project ID) * @param partnerRefID The unique partner reference ID (Project ID) * @throws {string} Exception: Partner reference ID is required * @returns {TransactionData[]} List of transactions data which indicate the status of batch, transaction results, transaction details information * (such as transactionID, transactionStatus...) * @example <caption>Sample code on Testnet (Staging) env</caption> const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const partnerRefId = "Project-ID"; // Unique ID of the project on Myria System const moduleFactory = ModuleFactory.getInstance(mClient); const transactionManager = moduleFactory.getTransactionManager(); const result = await transactionManager.getTransactionsByPartnerRefID( partnerRefId ); console.log('Transaction result -> ', result); */ async getTransactionsByPartnerRefID(partnerRefID) { if (!partnerRefID) { throw new Error("Partner reference ID"); } return this.transactionAPI.getTransactionsByPartnerRefID(partnerRefID); } async signableBulkTransferDetail(params) { if (!params) { throw new Error("Transfer params is required"); } if (!(params === null || params === void 0 ? void 0 : params.senderWalletAddress)) { throw new Error("Sender wallet address is required"); } if (params.senderWalletAddress && !Web3.utils.isAddress(params.senderWalletAddress)) { throw new Error("Sender Wallet Address is Invalid"); } if ((params === null || params === void 0 ? void 0 : params.items.length) === 0) { throw new Error("Transfer params items is required"); } else { params.items.forEach((paramItemValue) => { var _a, _b, _c; if (!paramItemValue.receiverWalletAddress) { throw new Error("Receive wallet address is required"); } if (paramItemValue.receiverWalletAddress && !Web3.utils.isAddress(paramItemValue.receiverWalletAddress)) { throw new Error("Receive wallet address is Invalid"); } if (!paramItemValue.tokenType) { throw new Error("Token type is required"); } if (!paramItemValue.quantizedAmount) { throw new Error("Quantized amount is required"); } if (paramItemValue.tokenType) { switch (paramItemValue.tokenType) { case TokenType.MINTABLE_ERC20: case TokenType.ERC20: if (!((_a = paramItemValue.tokenData) === null || _a === void 0 ? void 0 : _a.tokenAddress)) { throw new Error("Token Address is required"); } break; case TokenType.MINTABLE_ERC721: case TokenType.ERC721: if (!((_b = paramItemValue.tokenData) === null || _b === void 0 ? void 0 : _b.tokenAddress) || !((_c = paramItemValue.tokenData) === null || _c === void 0 ? void 0 : _c.tokenId)) { throw new Error("Token Address and Token Id are required"); } break; default: break; } } }); } try { const result = await this.transactionAPI.signableBulkTransfer(params); return result; } catch (err) { throw new Error(`Bulk Transfer failed: ${err}`); } } /** * @summary Single Transfer ERC-20 Token * @param {TransferERC20Params} transferParams Transfer ERC-20 Tokens params (include information for Sender/Receiver) * @description After transfer was triggered, we can query the status of the transaction with the following functions: * + getTransactionDetails(transactionId: number) {return TransactionData} * + getTransactionsByPartnerRefID(partnerRefID: string) {return []TransactionData} * @returns {TransferResponse} Transactions data which indicate the status of batch, transaction results, transaction details information * (such as transactionID, transactionStatus...) * @throws {string} Exception: Sender vault is not found * @throws {string} Exception: Receiver vault is not found * @throws {string} Http Status 400 - Sender/Receiver vaults do not exist * @throws {string} Http Status 400 - Signature is invalid * @throws {string} Http Status 400 - Vault IDs does not have enough funds */ async transferERC20Token(transferParams) { var _a, _b, _c; const isUsdtToken = transferParams.tokenAddress === TOKEN_ADDRESS_USDT; const quantum = transferParams.quantum || QUANTUM_ERC20; if (isUsdtToken && quantum !== QUANTUM_USDT) { throw new Error('Need to input correct quantum for USDT token address! Quantum for USDT token address must be 1000000'); } const requestSenderVaultERC20 = { starkKey: transferParams.senderPublicKey, tokenAddress: transferParams.tokenAddress, quantum, }; const requestReceiverVaultERC20 = { starkKey: transferParams.receiverPublicKey, tokenAddress: transferParams.tokenAddress, quantum, }; console.log('requestSenderVaultERC20', requestSenderVaultERC20); console.log('requestReceiverVaultERC20', requestReceiverVaultERC20); const senderVault = await this.commonAPI.retrieveERC20Vault(requestSenderVaultERC20); if (senderVault.status !== "success") { throw Error('"Request sender vault failed => ' + JSON.stringify(requestSenderVaultERC20)); } const receiverVault = await this.commonAPI.retrieveERC20Vault(requestReceiverVaultERC20); if (receiverVault.status !== "success") { throw Error('"Request receiver vault failed => ' + JSON.stringify(receiverVault)); } const transferCommonParams = { senderVaultId: (_a = senderVault.data) === null || _a === void 0 ? void 0 : _a.vaultId, senderPublicKey: transferParams.senderPublicKey, senderWalletAddress: transferParams.senderWalletAddress, receiverVaultId: (_b = receiverVault.data) === null || _b === void 0 ? void 0 : _b.vaultId, receiverPublicKey: transferParams.receiverPublicKey, assetId: (_c = senderVault.data) === null || _c === void 0 ? void 0 : _c.assetId, quantizedAmount: transferParams.quantizedAmount, tokenType: TokenType.ERC20, groupRequestId: transferParams.groupRequestId, partnerRefId: transferParams.partnerRefId, myriaPrivateStarkKey: transferParams.myriaPrivateKey, path: "/v1/transactions/transfer", }; const transferResult = await this.transferTokenCommon(transferCommonParams); return transferResult; } /** * @summary Single Transfer ERC-20 Token * @param {TransferERC20Params} transferParams Transfer ERC-20 Tokens params (include information for Sender/Receiver) * @description After transfer was triggered, we can query the status of the transaction with the following functions: * + getTransactionDetails(transactionId: number) {return TransactionData} * + getTransactionsByPartnerRefID(partnerRefID: string) {return []TransactionData} * @returns {TransferResponse} Transactions data which indicate the status of batch, transaction results, transaction details information * (such as transactionID, transactionStatus...) * @throws {string} Exception: Sender vault is not found * @throws {string} Exception: Receiver vault is not found * @throws {string} Http Status 400 - Sender/Receiver vaults do not exist * @throws {string} Http Status 400 - Signature is invalid * @throws {string} Http Status 400 - Vault IDs does not have enough funds */ async transferETHToken(transferParams) { var _a, _b, _c; const requestSenderVaultERC20 = { starkKey: transferParams.senderPublicKey, }; const requestReceiverVaultERC20 = { starkKey: transferParams.receiverPublicKey, }; const senderVault = await this.commonAPI.retrieveETHVault(requestSenderVaultERC20); if (senderVault.status !== "success") { throw Error('"Request sender vault failed => ' + JSON.stringify(requestSenderVaultERC20)); } const receiverVault = await this.commonAPI.retrieveETHVault(requestReceiverVaultERC20); if (receiverVault.status !== "success") { throw Error('"Request receiver vault failed => ' + JSON.stringify(receiverVault)); } const transferCommonParams = { senderVaultId: (_a = senderVault.data) === null || _a === void 0 ? void 0 : _a.vaultId, senderPublicKey: transferParams.senderPublicKey, senderWalletAddress: transferParams.senderWalletAddress, receiverVaultId: (_b = receiverVault.data) === null || _b === void 0 ? void 0 : _b.vaultId, receiverPublicKey: transferParams.receiverPublicKey, assetId: (_c = senderVault.data) === null || _c === void 0 ? void 0 : _c.assetId, quantizedAmount: transferParams.quantizedAmount, tokenType: TokenType.ETH, groupRequestId: transferParams.groupRequestId, partnerRefId: transferParams.partnerRefId, myriaPrivateStarkKey: transferParams.myriaPrivateKey, path: '/v1/transactions/transfer' }; const transferResult = await this.transferTokenCommon(transferCommonParams); return transferResult; } async transferTokenCommon(transferParams) { if (!transferParams.senderPublicKey) { throw new Error("Sender public key is required."); } if (!transferParams.senderVaultId) { throw new Error("Sender vault Id is required."); } if (!transferParams.senderWalletAddress) { throw new Error("Sender wallet address is required!"); } if (!transferParams.receiverPublicKey) { throw new Error("Receiver public key is required!"); } if (!transferParams.receiverVaultId) { throw new Error("Receiver vault Id is required!"); } if (!transferParams.assetId) { throw new Error("Asset id is required!"); } if (!transferParams.quantizedAmount) { throw new Error("Amount is required!"); } // if (!transferParams.myriaPrivateStarkKey) { // throw new Error('Myria private stark key is required!'); // } const currentTimestamp = new Date(); // const nonce = transferParams.nonce || currentTimestamp.getDate() * Math.floor(Math.random() * 10); let nonce; try { const result = await this.commonAPI.getNonceByStarkKey(transferParams.senderPublicKey); if (result.status === "success") { nonce = result.data; } else { throw new Error("Getting nonce value failed!"); } } catch (err) { throw new Error(`Getting nonce value failed: ${err}`); } const expirationTimestamp = transferParams.expirationTimestamp || Math.floor(currentTimestamp.setFullYear(currentTimestamp.getFullYear() + 20) / 3600 / 1000); if (!transferParams.nonce) { transferParams = { ...transferParams, nonce, }; } if (!transferParams.expirationTimestamp) { transferParams = { ...transferParams, expirationTimestamp, }; } const starkSignature = await this.commonModule.generateStarkSignatureForTransfer(transferParams); if (!starkSignature) { throw new Error("Signing message failed!"); } const currentTimestampSignatureHeader = Math.floor(Date.now() / 1000); let generateSignatureHeader = undefined; if (transferParams.myriaPrivateStarkKey) { generateSignatureHeader = await this.commonModule.generateHeaderSignatureWithSecretKey(transferParams.myriaPrivateStarkKey || "", transferParams.senderPublicKey, currentTimestampSignatureHeader, transferParams.path || ""); } const transferRequest = { senderVaultId: transferParams.senderVaultId, senderPublicKey: transferParams.senderPublicKey, receiverVaultId: transferParams.receiverVaultId, receiverPublicKey: transferParams.receiverPublicKey, token: transferParams.assetId, quantizedAmount: transferParams.quantizedAmount, nonce: Number(transferParams.nonce), expirationTimestamp: Number(transferParams.expirationTimestamp), signature: starkSignature, signatureHeader: generateSignatureHeader, }; let transferResult; try { let transferResponse = undefined; if (transferParams.tokenType === TokenType.ERC20 || transferParams.tokenType === TokenType.MINTABLE_ERC20 || transferParams.tokenType === TokenType.ETH) { transferResponse = await this.transactionAPI.transferToken(transferRequest); } else if (transferParams.tokenType === TokenType.ERC721 || transferParams.tokenType === TokenType.MINTABLE_ERC721) { transferResponse = await this.assetMarketplaceAPI.transferERC721Token(transferRequest); } if (!transferResponse) { throw Error("Please check token type, the transfer response is undefined"); } if ((transferResponse === null || transferResponse === void 0 ? void 0 : transferResponse.status) === "success") { transferResult = transferResponse === null || transferResponse === void 0 ? void 0 : transferResponse.data; } else { const error = { message: "Transfer failed with server error", code: "", error: undefined }; throw new Error(JSON.stringify(error)); } } catch (err) { const error = { message: "Transfer failed with server error", code: "", error: err }; throw new Error(JSON.stringify(error)); } return transferResult; } async updateTransactionComplete(payload) { if (!payload.starkKey) { throw new Error("Stark key is required"); } if (!payload.transactionHash) { throw new Error("Transaction hash is required"); } if (!payload.transactionId) { throw new Error("Transaction id is required"); } let response; try { response = await this.transactionAPI.updateTransactionComplete(payload); } catch (err) { throw new Error(`Error api called ${err}`); } return response; } /** * @summary Asynchronous burn for NFT Tokens (such as: ERC-721 Tokens, Marketplace NFTs) * - Function only supports MINTABLE_ERC721 and NFTs which are minted on Myria System * @param {BurnTokenParams} burnTokenParams Data regarding sender and receivers relevant for the burn transfer. * @description After burn transfer was triggered, we can query the status of the batch with the following functions: * + getTransactionsByGroupRequestIDAndPartnerRefID(groupRequestID: string, partnerRefID: string) * + getTransactionsByPartnerRefID(partnerRefID: string) * @returns {BurnTokenResponse} Transaction data list which have been captured and validated * - Response structure consist of 3 group of transactions failed[] / success[] / validationFailed[] * - All transactions in failed[], relate to failures due to not enough funds or other internal server errors. These transactions cannot be processed. * - All transactions in validationFailed[], relate to failures due to validation such as L2's signature. These can be retried with amended data. * - All transactions in success[], indicate that they have been recorded and will be processed by the system. * @throws {string} Exception: Sender wallet address is required * @throws {string} Exception: Burn transfer should include at least one transfer * @throws {string} Exception: Only MINTABLE_ERC-721 tokens are valid for this type of bulk transfer * @throws {string} Exception: Token address is required * @throws {string} Error code 409 - Request-ID/Group-Request-ID is already exists * @throws {string} Http error code 400 - User wallet (sender or receiver) is not registered * @throws {string} Http error code 400 - Vault ID does not have enough funds * @throws {string} Http error code 400 - Signature is invalid * @example <caption>Sample code on Testnet (Staging) env</caption> * const mClient: IMyriaClient = { networkId: Network.SEPOLIA, provider: web3Instance.currentProvider, web3: web3Instance, env: EnvTypes.STAGING, }; const YOUR_NFT_CONTRACT_ADDRESS = "0xA06116D9...."; const SENDER_WALLET_ADDRESS = '0x724f337bF0F....'; // Must be the owner of tokens, sender wallet address const moduleFactory = ModuleFactory.getInstance(mClient); const transactionManager = moduleFactory.getTransactionManager(); const burnTransferredItems: ItemSignableBurnTransferParams[] = [ { quantizedAmount: 1, // Should be 1 as always tokenType: TokenType.MINTABLE_ERC721, tokenData: { tokenAddress: YOUR_NFT_CONTRACT_ADDRESS, tokenId: '1' // Your minted token ID, quantum: '1' }, }, { quantizedAmount: 1, tokenType: TokenType.MINTABLE_ERC721, tokenData: { tokenAddress: YOUR_NFT_CONTRACT_ADDRESS, tokenId: '2' // Your minted token ID, }, }, { quantizedAmount: 1, tokenType: TokenType.MINTABLE_ERC721, tokenData: { tokenAddress: YOUR_NFT_CONTRACT_ADDRESS, tokenId: '3' // Your minted token ID }, }, ]; const burnTransferTokenParams: BurnTokenParams = {