UNPKG

rubic-sdk

Version:
478 lines • 22.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CrossChainStatusManager = void 0; const router_sdk_1 = require("@viaprotocol/router-sdk"); const rango_sdk_basic_1 = require("rango-sdk-basic"); const errors_1 = require("../../../common/errors"); const blockchain_name_1 = require("../../../core/blockchain/models/blockchain-name"); const blockchain_id_1 = require("../../../core/blockchain/utils/blockchains-info/constants/blockchain-id"); const tx_status_1 = require("../../../core/blockchain/web3-public-service/web3-public/models/tx-status"); const injector_1 = require("../../../core/injector/injector"); const changenow_api_key_1 = require("../../common/providers/changenow/constants/changenow-api-key"); const get_bridgers_trade_status_1 = require("../../common/status-manager/utils/get-bridgers-trade-status"); const get_src_tx_status_1 = require("../../common/status-manager/utils/get-src-tx-status"); const cross_chain_trade_type_1 = require("../calculation-manager/models/cross-chain-trade-type"); const cbridge_cross_chain_api_service_1 = require("../calculation-manager/providers/cbridge/cbridge-cross-chain-api-service"); const cbridge_status_response_1 = require("../calculation-manager/providers/cbridge/models/cbridge-status-response"); const lifi_swap_status_1 = require("../calculation-manager/providers/lifi-provider/models/lifi-swap-status"); const rango_api_key_1 = require("../calculation-manager/providers/rango-provider/constants/rango-api-key"); const symbiosis_swap_status_1 = require("../calculation-manager/providers/symbiosis-provider/models/symbiosis-swap-status"); const via_default_api_key_1 = require("../calculation-manager/providers/via-provider/constants/via-default-api-key"); const via_swap_status_1 = require("../calculation-manager/providers/via-provider/models/via-swap-status"); const xy_cross_chain_provider_1 = require("../calculation-manager/providers/xy-provider/xy-cross-chain-provider"); const cross_chain_cbridge_manager_1 = require("../cbridge-manager/cross-chain-cbridge-manager"); const multichain_status_mapping_1 = require("./constants/multichain-status-mapping"); const celer_transfer_status_enum_1 = require("./models/celer-transfer-status.enum"); const changenow_api_response_1 = require("./models/changenow-api-response"); /** * Contains methods for getting cross-chain trade statuses. */ class CrossChainStatusManager { constructor() { this.httpClient = injector_1.Injector.httpClient; this.getDstTxStatusFnMap = { [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.CELER]: this.getCelerDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.LIFI]: this.getLifiDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.SYMBIOSIS]: this.getSymbiosisDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.DEBRIDGE]: this.getDebridgeDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.VIA]: this.getViaDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.RANGO]: this.getRangoDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.BRIDGERS]: this.getBridgersDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.MULTICHAIN]: this.getMultichainDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.XY]: this.getXyDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.CELER_BRIDGE]: this.getCelerBridgeDstSwapStatus, [cross_chain_trade_type_1.CROSS_CHAIN_TRADE_TYPE.CHANGENOW]: this.getChangenowDstSwapStatus }; } /** * Returns cross-chain trade statuses on the source and target networks. * The result consists of statuses of the source and target transactions and destination tx hash. * @example * ```ts * const tradeData = { * fromBlockchain: BLOCKCHAIN_NAME.FANTOM, * toBlockchain: BLOCKCHAIN_NAME.BSC, * txTimestamp: 1658241570024, * srxTxHash: '0xd2263ca82ac0fce606cb75df27d7f0dc94909d41a58c37563bd6772496cb8924' * }; * const tradeType = CROSS_CHAIN_TRADE_TYPE.VIA; * const crossChainStatus = await sdk.crossChainStatusManager.getCrossChainStatus(tradeData, tradeType); * console.log('Source transaction status', crossChainStatus.srcTxStatus); * console.log('Destination transaction status', crossChainStatus.dstTxStatus); * console.log('Destination transaction hash', crossChainStatus.dstTxHash); * ``` * @param data Data needed to calculate statuses. * @param tradeType Cross-chain trade type. * @returns Object with transaction statuses and hash. */ async getCrossChainStatus(data, tradeType) { const { fromBlockchain, srcTxHash } = data; let srcTxStatus = await (0, get_src_tx_status_1.getSrcTxStatus)(fromBlockchain, srcTxHash); const dstTxData = await this.getDstTxData(srcTxStatus, data, tradeType); if (dstTxData.status === tx_status_1.TxStatus.FAIL && srcTxStatus === tx_status_1.TxStatus.PENDING) { srcTxStatus = tx_status_1.TxStatus.FAIL; } return { srcTxStatus, dstTxStatus: dstTxData.status, dstTxHash: dstTxData.hash }; } /** * Get destination transaction status and hash based on source transaction status, * source transaction receipt, trade data and type. * @param srcTxStatus Source transaction status. * @param tradeData Trade data. * @param tradeType Cross-chain trade type. * @returns Cross-chain transaction status and hash. */ async getDstTxData(srcTxStatus, tradeData, tradeType) { if (srcTxStatus === tx_status_1.TxStatus.FAIL) { return { hash: null, status: tx_status_1.TxStatus.FAIL }; } if (srcTxStatus === tx_status_1.TxStatus.PENDING) { return { hash: null, status: tx_status_1.TxStatus.PENDING }; } const getDstTxStatusFn = this.getDstTxStatusFnMap[tradeType]; if (!getDstTxStatusFn) { throw new errors_1.RubicSdkError('Unsupported cross chain provider'); } return getDstTxStatusFn.call(this, tradeData); } /** * Get Rango trade dst transaction status and hash. * @param data Trade data. * @returns Cross-chain transaction status and hash. */ async getRangoDstSwapStatus(data) { try { const { rangoRequestId: requestId } = data; const rangoTradeStatusResponse = await injector_1.Injector.httpClient.get('https://api.rango.exchange/basic/status', { params: { apiKey: rango_api_key_1.RANGO_API_KEY, requestId: requestId, txId: data.srcTxHash } }); const dstTxData = { status: tx_status_1.TxStatus.UNKNOWN, hash: rangoTradeStatusResponse.bridgeData?.destTxHash || null }; if (rangoTradeStatusResponse.status === rango_sdk_basic_1.TransactionStatus.SUCCESS) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } if (rangoTradeStatusResponse.status === rango_sdk_basic_1.TransactionStatus.FAILED) { dstTxData.status = tx_status_1.TxStatus.FAIL; const type = rangoTradeStatusResponse?.output?.type; if (type === 'MIDDLE_ASSET_IN_SRC' || type === 'MIDDLE_ASSET_IN_DEST') { dstTxData.status = tx_status_1.TxStatus.FALLBACK; } if (type === 'REVERTED_TO_INPUT') { dstTxData.status = tx_status_1.TxStatus.REVERT; } } if (rangoTradeStatusResponse.status === rango_sdk_basic_1.TransactionStatus.RUNNING || rangoTradeStatusResponse.status === null) { dstTxData.status = tx_status_1.TxStatus.PENDING; } return dstTxData; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } /** * Get Symbiosis trade dst transaction status and hash. * @param data Trade data. * @returns Cross-chain transaction status and hash. */ async getSymbiosisDstSwapStatus(data) { const symbiosisTxIndexingTimeSpent = Date.now() > data.txTimestamp + 30000; if (symbiosisTxIndexingTimeSpent) { try { const srcChainId = blockchain_id_1.blockchainId[data.fromBlockchain]; const { status: { text: dstTxStatus }, tx } = await injector_1.Injector.httpClient.get(`https://api-v2.symbiosis.finance/crosschain/v1/tx/${srcChainId}/${data.srcTxHash}`); let dstTxData = { status: tx_status_1.TxStatus.PENDING, hash: tx?.hash || null }; if (dstTxStatus === symbiosis_swap_status_1.SymbiosisSwapStatus.PENDING || dstTxStatus === symbiosis_swap_status_1.SymbiosisSwapStatus.NOT_FOUND) { dstTxData.status = tx_status_1.TxStatus.PENDING; } if (dstTxStatus === symbiosis_swap_status_1.SymbiosisSwapStatus.STUCKED) { dstTxData.status = tx_status_1.TxStatus.REVERT; } if (dstTxStatus === symbiosis_swap_status_1.SymbiosisSwapStatus.REVERTED) { dstTxData.status = tx_status_1.TxStatus.FALLBACK; } if (dstTxStatus === symbiosis_swap_status_1.SymbiosisSwapStatus.SUCCESS) { if (data.toBlockchain !== blockchain_name_1.BLOCKCHAIN_NAME.BITCOIN) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } else { dstTxData = await this.getBitcoinStatus(tx.hash); } } return dstTxData; } catch (error) { console.debug('[Symbiosis Trade] Error retrieving dst tx status', error); return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } return { status: tx_status_1.TxStatus.PENDING, hash: null }; } /** * Get Li-fi trade dst transaction status and hash. * @param data Trade data. * @returns Cross-chain transaction status and hash. */ async getLifiDstSwapStatus(data) { if (!data.lifiBridgeType) { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } try { const params = { bridge: data.lifiBridgeType, fromChain: blockchain_id_1.blockchainId[data.fromBlockchain], toChain: blockchain_id_1.blockchainId[data.toBlockchain], txHash: data.srcTxHash }; const { status, receiving } = await injector_1.Injector.httpClient.get('https://li.quest/v1/status', { params }); const dstTxData = { status: tx_status_1.TxStatus.UNKNOWN, hash: receiving?.txHash || null }; if (status === lifi_swap_status_1.LifiSwapStatus.DONE) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } if (status === lifi_swap_status_1.LifiSwapStatus.FAILED) { dstTxData.status = tx_status_1.TxStatus.FAIL; } if (status === lifi_swap_status_1.LifiSwapStatus.INVALID) { dstTxData.status = tx_status_1.TxStatus.UNKNOWN; } if (status === lifi_swap_status_1.LifiSwapStatus.NOT_FOUND || status === lifi_swap_status_1.LifiSwapStatus.PENDING) { dstTxData.status = tx_status_1.TxStatus.PENDING; } return dstTxData; } catch (error) { console.debug('[Li-fi Trade] error retrieving tx status', error); return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } /** * Get Celer trade dst transaction status. * @param data Trade data. * @returns Cross-chain transaction status. */ async getCelerDstSwapStatus(data) { try { const dstTxData = { status: tx_status_1.TxStatus.PENDING, hash: null }; const txSearchResult = await injector_1.Injector.httpClient.get('https://api.celerscan.com/scan/searchByTxHash', { params: { tx: data.srcTxHash } }); if (txSearchResult.txSearchInfo.length === 0) { return dstTxData; } const trade = txSearchResult.txSearchInfo[0].transfer[0]; if ([ celer_transfer_status_enum_1.CelerTransferStatus.XS_UNKNOWN, celer_transfer_status_enum_1.CelerTransferStatus.XS_WAITING_FOR_SGN_CONFIRMATION, celer_transfer_status_enum_1.CelerTransferStatus.XS_WAITING_FOR_FUND_RELEASE ].includes(trade.xfer_status)) { dstTxData.status = tx_status_1.TxStatus.PENDING; } if (trade.xfer_status === celer_transfer_status_enum_1.CelerTransferStatus.XS_COMPLETED) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } if ([ celer_transfer_status_enum_1.CelerTransferStatus.XS_REFUNDED, celer_transfer_status_enum_1.CelerTransferStatus.XS_TO_BE_REFUND, celer_transfer_status_enum_1.CelerTransferStatus.XS_REFUND_TO_BE_CONFIRMED ].includes(trade.xfer_status)) { dstTxData.status = tx_status_1.TxStatus.FALLBACK; } return dstTxData; } catch (error) { console.debug('[Celer Trade] error retrieving tx status', error); return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } /** * Get DeBridge trade dst transaction status. * @param data Trade data. * @returns Cross-chain transaction status and hash. */ async getDebridgeDstSwapStatus(data) { try { const params = { filter: data.srcTxHash, filterType: 1 }; const { send = null, claim = null } = await this.httpClient.get('https://api.debridge.finance/api/Transactions/GetFullSubmissionInfo', { params }); const dstTxData = { status: tx_status_1.TxStatus.FAIL, hash: claim?.transactionHash || null }; if (!send || !claim) { dstTxData.status = tx_status_1.TxStatus.PENDING; } if (claim?.transactionHash) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } return dstTxData; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } /** * Get Via trade dst transaction status and hash. * @param data Trade data. * @returns Cross-chain transaction status and hash. */ async getViaDstSwapStatus(data) { try { const txStatusResponse = await new router_sdk_1.Via(via_default_api_key_1.VIA_DEFAULT_CONFIG).checkTx({ actionUuid: data.viaUuid }); const status = txStatusResponse.event; const dstTxData = { status: tx_status_1.TxStatus.PENDING, hash: txStatusResponse.data?.txHash || null }; if (status === via_swap_status_1.ViaSwapStatus.SUCCESS) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } if (status === via_swap_status_1.ViaSwapStatus.FAIL) { dstTxData.status = tx_status_1.TxStatus.FAIL; } return dstTxData; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } /** * Get Bridgers trade dst transaction status. * @param data Trade data. * @returns Cross-chain transaction status. */ getBridgersDstSwapStatus(data) { if (!data.amountOutMin) { throw new errors_1.RubicSdkError('field amountOutMin is not set.'); } return (0, get_bridgers_trade_status_1.getBridgersTradeStatus)(data.srcTxHash, data.fromBlockchain, 'rubic', data.amountOutMin); } /** * @internal * Get transaction status in bitcoin network; * @param hash Bitcoin transaction hash. */ async getBitcoinStatus(hash) { let bitcoinTransactionStatus; const dstTxData = { status: tx_status_1.TxStatus.PENDING, hash: null }; try { const btcStatusApi = 'https://blockchain.info/rawtx/'; bitcoinTransactionStatus = await this.httpClient.get(`${btcStatusApi}${hash}`); dstTxData.hash = bitcoinTransactionStatus?.hash || null; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } const isCompleted = bitcoinTransactionStatus?.block_index !== undefined; if (isCompleted) { dstTxData.status = tx_status_1.TxStatus.SUCCESS; } return dstTxData; } async getMultichainDstSwapStatus(data) { try { const { info: { status, swaptx } } = await this.httpClient.get(`https://bridgeapi.anyswap.exchange/v2/history/details?params=${data.srcTxHash}`); return { status: multichain_status_mapping_1.MultichainStatusMapping?.[status] || tx_status_1.TxStatus.PENDING, hash: swaptx || null }; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } async getXyDstSwapStatus(data) { try { const { isSuccess, status, txHash } = await this.httpClient.get(`${xy_cross_chain_provider_1.XyCrossChainProvider.apiEndpoint}/crossChainStatus?srcChainId=${blockchain_id_1.blockchainId[data.fromBlockchain]}&transactionHash=${data.srcTxHash}`); if (isSuccess && status === 'Done') { return { status: tx_status_1.TxStatus.SUCCESS, hash: txHash }; } if (!isSuccess) { return { status: tx_status_1.TxStatus.FAIL, hash: null }; } return { status: tx_status_1.TxStatus.PENDING, hash: null }; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } async getCelerBridgeDstSwapStatus(data) { try { const transferId = await cross_chain_cbridge_manager_1.CrossChainCbridgeManager.getTransferId(data.srcTxHash, data.fromBlockchain); const swapData = await cbridge_cross_chain_api_service_1.CbridgeCrossChainApiService.fetchTradeStatus(transferId); switch (swapData.status) { case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_UNKNOWN: case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_SUBMITTING: case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_WAITING_FOR_SGN_CONFIRMATION: case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_REQUESTING_REFUND: case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_CONFIRMING_YOUR_REFUND: default: return { status: tx_status_1.TxStatus.PENDING, hash: null }; case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_REFUNDED: case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_COMPLETED: return { status: tx_status_1.TxStatus.SUCCESS, hash: swapData.dst_block_tx_link.split('/').at(-1) }; case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_FAILED: return { status: tx_status_1.TxStatus.FAIL, hash: null }; case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_WAITING_FOR_FUND_RELEASE: case cbridge_status_response_1.TransferHistoryStatus.TRANSFER_TO_BE_REFUNDED: return swapData.refund_reason === cbridge_status_response_1.XferStatus.OK_TO_RELAY ? { status: tx_status_1.TxStatus.PENDING, hash: null } : { status: tx_status_1.TxStatus.REVERT, hash: null }; } } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } async getChangenowDstSwapStatus(data) { if (!data.changenowId) { throw new errors_1.RubicSdkError('Must provide changenow trade id'); } try { const { status, payoutHash } = await this.httpClient.get('https://api.changenow.io/v2/exchange/by-id', { params: { id: data.changenowId }, headers: { 'x-changenow-api-key': changenow_api_key_1.changenowApiKey } }); if (status === changenow_api_response_1.ChangenowApiStatus.FINISHED || status === changenow_api_response_1.ChangenowApiStatus.REFUNDED) { return { status: tx_status_1.TxStatus.SUCCESS, hash: payoutHash }; } if (status === changenow_api_response_1.ChangenowApiStatus.FAILED) { return { status: tx_status_1.TxStatus.FAIL, hash: null }; } return { status: tx_status_1.TxStatus.PENDING, hash: null }; } catch { return { status: tx_status_1.TxStatus.PENDING, hash: null }; } } } exports.CrossChainStatusManager = CrossChainStatusManager; //# sourceMappingURL=cross-chain-status-manager.js.map