rubic-sdk
Version:
Simplify dApp creation
478 lines • 22.6 kB
JavaScript
"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