From 9400b8628e7b7cf07c7d10c390db1de95422402d Mon Sep 17 00:00:00 2001
From: binarybaron <86064887+binarybaron@users.noreply.github.com>
Date: Wed, 24 Jul 2024 18:49:19 +0200
Subject: [PATCH] feat: Allow cooperative Monero redeem after being punished
(#210)
- Allow cooperative Monero redeem after being punished
- Upgrade swap-cli to 0.13.3
- Warn if provider is running outdated version of the software
---
.erb/scripts/download-swap-binaries.ts | 4 +-
src/main/cli/dirs.ts | 2 +-
src/models/cliModel.ts | 77 +++++++++++++++----
src/models/storeModel.ts | 34 ++++++++
.../components/alert/SwapStatusAlert.tsx | 2 +-
.../components/alert/UnfinishedSwapsAlert.tsx | 4 +-
.../modal/provider/ProviderInfo.tsx | 8 ++
.../modal/swap/pages/SwapStatePage.tsx | 19 ++++-
.../swap/pages/done/BitcoinPunishedPage.tsx | 25 +++++-
.../pages/done/XmrRedeemInMempoolPage.tsx | 13 ++--
.../swap/pages/exited/ProcessExitedPage.tsx | 4 +-
.../navigation/UnfinishedSwapsCountBadge.tsx | 4 +-
.../pages/history/table/HistoryRowActions.tsx | 9 ++-
src/store/features/providersSlice.ts | 7 +-
src/store/features/swapSlice.ts | 37 +++++++++
src/store/hooks.ts | 14 +++-
src/utils/multiAddrUtils.ts | 17 ++--
src/utils/sortUtils.ts | 30 ++++----
18 files changed, 248 insertions(+), 62 deletions(-)
diff --git a/.erb/scripts/download-swap-binaries.ts b/.erb/scripts/download-swap-binaries.ts
index 3f9697d..571a558 100644
--- a/.erb/scripts/download-swap-binaries.ts
+++ b/.erb/scripts/download-swap-binaries.ts
@@ -16,9 +16,9 @@ async function makeFileExecutable(binary: Binary) {
);
}
-const CLI_VERSION = '0.13.2';
+const CLI_VERSION = '0.13.3';
// Ensure the value here matches with the one in src/main/cli/dirs.ts
-const CLI_FILE_NAME_VERSION_PREFIX = '0_13_2_';
+const CLI_FILE_NAME_VERSION_PREFIX = '0_13_3_';
const binaries = [
{
diff --git a/src/main/cli/dirs.ts b/src/main/cli/dirs.ts
index 5978218..238dcdb 100644
--- a/src/main/cli/dirs.ts
+++ b/src/main/cli/dirs.ts
@@ -14,7 +14,7 @@ import { RpcProcessStateType } from 'models/rpcModel';
// Ensure the CLI version is updated in both places when updating the CLI version.
// We need a different name for each CLI version to make sure, that the CLI is definitely updated
// electron-builder sometimes doesn't update the CLI binary
-const CLI_FILE_NAME_VERSION_PREFIX = '0_13_2_';
+const CLI_FILE_NAME_VERSION_PREFIX = '0_13_3_';
// Be consistent with the way the cli generates the
// data-dir on linux
diff --git a/src/models/cliModel.ts b/src/models/cliModel.ts
index 47e87a6..ede1239 100644
--- a/src/models/cliModel.ts
+++ b/src/models/cliModel.ts
@@ -352,31 +352,80 @@ export function isCliLogDownloadingMoneroWalletRpc(
return log.fields.message === 'Downloading monero-wallet-rpc';
}
-export interface CliLogStartedSyncingMoneroWallet extends CliLog {
+export interface CliLogGotNotificationForNewBlock extends CliLog {
fields: {
- message: 'Syncing Monero wallet';
- current_sync_height?: boolean;
+ message: 'Got notification for new block';
+ block_height: string;
};
}
-export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
+export function isCliLogGotNotificationForNewBlock(
+ log: CliLog,
+): log is CliLogGotNotificationForNewBlock {
+ return log.fields.message === 'Got notification for new block';
+}
+
+export interface CliLogAttemptingToCooperativelyRedeemXmr extends CliLog {
fields: {
- message: 'Downloading monero-wallet-rpc';
- progress: string;
- size: string;
- download_url: string;
+ message: 'Attempting to cooperatively redeem XMR after being punished';
};
}
-export interface CliLogGotNotificationForNewBlock extends CliLog {
+export function isCliLogAttemptingToCooperativelyRedeemXmr(
+ log: CliLog,
+): log is CliLogAttemptingToCooperativelyRedeemXmr {
+ return (
+ log.fields.message ===
+ 'Attempting to cooperatively redeem XMR after being punished'
+ );
+}
+
+export interface CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr
+ extends CliLog {
fields: {
- message: 'Got notification for new block';
- block_height: string;
+ message: 'Alice has accepted our request to cooperatively redeem the XMR';
};
}
-export function isCliLogGotNotificationForNewBlock(
+export function isCliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr(
log: CliLog,
-): log is CliLogGotNotificationForNewBlock {
- return log.fields.message === 'Got notification for new block';
+): log is CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr {
+ return (
+ log.fields.message ===
+ 'Alice has accepted our request to cooperatively redeem the XMR'
+ );
+}
+
+export interface CliLogAliceRejectedOurRequestForCooperativeXmrRedeem
+ extends CliLog {
+ fields: {
+ message: 'Alice rejected our request for cooperative XMR redeem';
+ reason: string;
+ };
+}
+
+export function isCliLogAliceRejectedOurRequestForCooperativeXmrRedeem(
+ log: CliLog,
+): log is CliLogAliceRejectedOurRequestForCooperativeXmrRedeem {
+ return (
+ log.fields.message ===
+ 'Alice rejected our request for cooperative XMR redeem'
+ );
+}
+
+// tracing::error!(?error, "Failed to request cooperative XMR redeem from Alice");
+export interface CliLogFailedToRequestCooperativeXmrRedeemFromAlice
+ extends CliLog {
+ fields: {
+ message: 'Failed to request cooperative XMR redeem from Alice';
+ error: string;
+ };
+}
+
+export function isCliLogFailedToRequestCooperativeXmrRedeemFromAlice(
+ log: CliLog,
+): log is CliLogFailedToRequestCooperativeXmrRedeemFromAlice {
+ return (
+ log.fields.message === 'Failed to request cooperative XMR redeem from Alice'
+ );
}
diff --git a/src/models/storeModel.ts b/src/models/storeModel.ts
index f81860b..0ccda1c 100644
--- a/src/models/storeModel.ts
+++ b/src/models/storeModel.ts
@@ -33,6 +33,9 @@ export enum SwapStateType {
BTC_CANCELLED = 'btc cancelled',
BTC_REFUNDED = 'btc refunded',
BTC_PUNISHED = 'btc punished',
+ ATTEMPTING_COOPERATIVE_REDEEM = 'attempting cooperative redeem',
+ COOPERATIVE_REDEEM_REJECTED = 'cooperative redeem rejected',
+ COOPERATIVE_REDEEM_ACCEPTED = 'cooperative redeem accepted',
}
export function isSwapState(state?: SwapState | null): state is SwapState {
@@ -138,6 +141,37 @@ export function isSwapStateBtcRedemeed(
return state?.type === SwapStateType.BTC_REDEEMED;
}
+export interface SwapStateAttemptingCooperativeRedeeem extends SwapState {
+ type: SwapStateType.ATTEMPTING_COOPERATIVE_REDEEM;
+}
+
+export function isSwapStateAttemptingCooperativeRedeeem(
+ state?: SwapState | null,
+): state is SwapStateAttemptingCooperativeRedeeem {
+ return state?.type === SwapStateType.ATTEMPTING_COOPERATIVE_REDEEM;
+}
+
+export interface SwapStateCooperativeRedeemAccepted extends SwapState {
+ type: SwapStateType.COOPERATIVE_REDEEM_ACCEPTED;
+}
+
+export function isSwapStateCooperativeRedeemAccepted(
+ state?: SwapState | null,
+): state is SwapStateCooperativeRedeemAccepted {
+ return state?.type === SwapStateType.COOPERATIVE_REDEEM_ACCEPTED;
+}
+
+export interface SwapStateCooperativeRedeemRejected extends SwapState {
+ type: SwapStateType.COOPERATIVE_REDEEM_REJECTED;
+ reason: string;
+}
+
+export function isSwapStateCooperativeRedeemRejected(
+ state?: SwapState | null,
+): state is SwapStateCooperativeRedeemRejected {
+ return state?.type === SwapStateType.COOPERATIVE_REDEEM_REJECTED;
+}
+
export interface SwapStateXmrRedeemInMempool extends SwapState {
type: SwapStateType.XMR_REDEEM_IN_MEMPOOL;
bobXmrRedeemTxId: string;
diff --git a/src/renderer/components/alert/SwapStatusAlert.tsx b/src/renderer/components/alert/SwapStatusAlert.tsx
index 91fcb06..01ef28f 100644
--- a/src/renderer/components/alert/SwapStatusAlert.tsx
+++ b/src/renderer/components/alert/SwapStatusAlert.tsx
@@ -221,7 +221,7 @@ export default function SwapStatusAlert({
}
+ action={Resume}
variant="filled"
>
diff --git a/src/renderer/components/alert/UnfinishedSwapsAlert.tsx b/src/renderer/components/alert/UnfinishedSwapsAlert.tsx
index bdeda60..5a298a6 100644
--- a/src/renderer/components/alert/UnfinishedSwapsAlert.tsx
+++ b/src/renderer/components/alert/UnfinishedSwapsAlert.tsx
@@ -1,10 +1,10 @@
import { Button } from '@material-ui/core';
import Alert from '@material-ui/lab/Alert';
import { useNavigate } from 'react-router-dom';
-import { useResumeableSwapsCount } from 'store/hooks';
+import { useResumeableSwapsCountExcludingPunished } from 'store/hooks';
export default function UnfinishedSwapsAlert() {
- const resumableSwapsCount = useResumeableSwapsCount();
+ const resumableSwapsCount = useResumeableSwapsCountExcludingPunished();
const navigate = useNavigate();
if (resumableSwapsCount > 0) {
diff --git a/src/renderer/components/modal/provider/ProviderInfo.tsx b/src/renderer/components/modal/provider/ProviderInfo.tsx
index 592bf1d..9706da5 100644
--- a/src/renderer/components/modal/provider/ProviderInfo.tsx
+++ b/src/renderer/components/modal/provider/ProviderInfo.tsx
@@ -6,6 +6,8 @@ import {
MoneroBitcoinExchangeRate,
SatsAmount,
} from 'renderer/components/other/Units';
+import { isProviderOutdated } from 'utils/multiAddrUtils';
+import WarningIcon from '@material-ui/icons/Warning';
const useStyles = makeStyles((theme) => ({
content: {
@@ -28,6 +30,7 @@ export default function ProviderInfo({
provider: ExtendedProviderStatus;
}) {
const classes = useStyles();
+ const isOutdated = isProviderOutdated(provider);
return (
@@ -69,6 +72,11 @@ export default function ProviderInfo({
} color="primary" />
)}
+ {isOutdated && (
+
+ } color="primary" />
+
+ )}
);
diff --git a/src/renderer/components/modal/swap/pages/SwapStatePage.tsx b/src/renderer/components/modal/swap/pages/SwapStatePage.tsx
index de0429a..b049d2f 100644
--- a/src/renderer/components/modal/swap/pages/SwapStatePage.tsx
+++ b/src/renderer/components/modal/swap/pages/SwapStatePage.tsx
@@ -1,11 +1,14 @@
import { Box } from '@material-ui/core';
import { useAppSelector } from 'store/hooks';
import {
+ isSwapStateAttemptingCooperativeRedeeem,
isSwapStateBtcCancelled,
isSwapStateBtcLockInMempool,
isSwapStateBtcPunished,
isSwapStateBtcRedemeed,
isSwapStateBtcRefunded,
+ isSwapStateCooperativeRedeemAccepted,
+ isSwapStateCooperativeRedeemRejected,
isSwapStateInitiated,
isSwapStateProcessExited,
isSwapStateReceivedQuote,
@@ -32,6 +35,7 @@ import BitcoinCancelledPage from './in_progress/BitcoinCancelledPage';
import BitcoinRefundedPage from './done/BitcoinRefundedPage';
import BitcoinPunishedPage from './done/BitcoinPunishedPage';
import { SyncingMoneroWalletPage } from './in_progress/SyncingMoneroWalletPage';
+import CircularProgressWithSubtitle from '../CircularProgressWithSubtitle';
export default function SwapStatePage({
swapState,
@@ -83,7 +87,20 @@ export default function SwapStatePage({
return ;
}
if (isSwapStateBtcPunished(swapState)) {
- return ;
+ return ;
+ }
+ if (isSwapStateAttemptingCooperativeRedeeem(swapState)) {
+ return (
+
+ );
+ }
+ if (isSwapStateCooperativeRedeemAccepted(swapState)) {
+ return (
+
+ );
+ }
+ if (isSwapStateCooperativeRedeemRejected(swapState)) {
+ return ;
}
if (isSwapStateProcessExited(swapState)) {
return ;
diff --git a/src/renderer/components/modal/swap/pages/done/BitcoinPunishedPage.tsx b/src/renderer/components/modal/swap/pages/done/BitcoinPunishedPage.tsx
index e98f9fc..f54d873 100644
--- a/src/renderer/components/modal/swap/pages/done/BitcoinPunishedPage.tsx
+++ b/src/renderer/components/modal/swap/pages/done/BitcoinPunishedPage.tsx
@@ -1,13 +1,30 @@
import { Box, DialogContentText } from '@material-ui/core';
import FeedbackInfoBox from '../../../../pages/help/FeedbackInfoBox';
+import {
+ SwapStateCooperativeRedeemRejected,
+ isSwapStateCooperativeRedeemRejected,
+} from 'models/storeModel';
-export default function BitcoinPunishedPage() {
+export default function BitcoinPunishedPage({
+ state,
+}: {
+ state: null | SwapStateCooperativeRedeemRejected;
+}) {
return (
- Unfortunately, the swap was not successful, and you've incurred a
- penalty because the swap was not refunded in time. Both the Bitcoin and
- Monero are irretrievable.
+ Unfortunately, the swap was unsuccessful. Since you did not refund in
+ time, the Bitcoin has been lost. However, with the cooperation of the
+ other party, you might still be able to redeem the Monero, although this
+ is not guaranteed.{' '}
+ {isSwapStateCooperativeRedeemRejected(state) && (
+ <>
+
+ We tried to redeem the Monero with the other party's help, but it
+ was unsuccessful (reason: {state.reason}). Attempting again at a
+ later time might yield success.
+ >
+ )}
diff --git a/src/renderer/components/modal/swap/pages/done/XmrRedeemInMempoolPage.tsx b/src/renderer/components/modal/swap/pages/done/XmrRedeemInMempoolPage.tsx
index 18f4959..8c839a1 100644
--- a/src/renderer/components/modal/swap/pages/done/XmrRedeemInMempoolPage.tsx
+++ b/src/renderer/components/modal/swap/pages/done/XmrRedeemInMempoolPage.tsx
@@ -4,6 +4,7 @@ import { useActiveSwapInfo } from 'store/hooks';
import { getSwapXmrAmount } from 'models/rpcModel';
import MoneroTransactionInfoBox from '../../MoneroTransactionInfoBox';
import FeedbackInfoBox from '../../../../pages/help/FeedbackInfoBox';
+import { MoneroAmount } from 'renderer/components/other/Units';
type XmrRedeemInMempoolPageProps = {
state: SwapStateXmrRedeemInMempool | null;
@@ -13,11 +14,13 @@ export default function XmrRedeemInMempoolPage({
state,
}: XmrRedeemInMempoolPageProps) {
const swap = useActiveSwapInfo();
- const additionalContent = swap
- ? `This transaction transfers ${getSwapXmrAmount(swap).toFixed(6)} XMR to ${
- state?.bobXmrRedeemAddress
- }`
- : null;
+ const additionalContent = swap ? (
+ <>
+ This transaction transfers{' '}
+ to the{' '}
+ {state?.bobXmrRedeemAddress}
+ >
+ ) : null;
return (
diff --git a/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx b/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx
index 2e78ff8..fd5acec 100644
--- a/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx
+++ b/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx
@@ -3,6 +3,7 @@ import { SwapStateName } from 'models/rpcModel';
import {
isSwapStateBtcPunished,
isSwapStateBtcRefunded,
+ isSwapStateCooperativeRedeemRejected,
isSwapStateXmrRedeemInMempool,
SwapStateProcessExited,
} from '../../../../../../models/storeModel';
@@ -24,7 +25,8 @@ export default function ProcessExitedPage({ state }: ProcessExitedPageProps) {
if (
isSwapStateXmrRedeemInMempool(state.prevState) ||
isSwapStateBtcRefunded(state.prevState) ||
- isSwapStateBtcPunished(state.prevState)
+ isSwapStateBtcPunished(state.prevState) ||
+ isSwapStateCooperativeRedeemRejected(state.prevState)
) {
return ;
}
diff --git a/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx b/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx
index 1304b77..d32dce5 100644
--- a/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx
+++ b/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx
@@ -1,12 +1,12 @@
import { Badge } from '@material-ui/core';
-import { useResumeableSwapsCount } from 'store/hooks';
+import { useResumeableSwapsCountExcludingPunished } from 'store/hooks';
export default function UnfinishedSwapsBadge({
children,
}: {
children: JSX.Element;
}) {
- const resumableSwapsCount = useResumeableSwapsCount();
+ const resumableSwapsCount = useResumeableSwapsCountExcludingPunished();
if (resumableSwapsCount > 0) {
return (
diff --git a/src/renderer/components/pages/history/table/HistoryRowActions.tsx b/src/renderer/components/pages/history/table/HistoryRowActions.tsx
index 0caf854..49b2f22 100644
--- a/src/renderer/components/pages/history/table/HistoryRowActions.tsx
+++ b/src/renderer/components/pages/history/table/HistoryRowActions.tsx
@@ -14,6 +14,7 @@ import {
export function SwapResumeButton({
swap,
+ children,
...props
}: { swap: GetSwapInfoResponse } & ButtonProps) {
return (
@@ -27,7 +28,7 @@ export function SwapResumeButton({
requiresRpc
{...props}
>
- Resume
+ {children}
);
}
@@ -80,11 +81,11 @@ export default function HistoryRowActions({
if (swap.stateName === SwapStateName.BtcPunished) {
return (
-
-
+
+ Attempt recovery
);
}
- return ;
+ return Resume;
}
diff --git a/src/store/features/providersSlice.ts b/src/store/features/providersSlice.ts
index d4af559..a3833fa 100644
--- a/src/store/features/providersSlice.ts
+++ b/src/store/features/providersSlice.ts
@@ -1,7 +1,7 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { ExtendedProviderStatus, ProviderStatus } from 'models/apiModel';
import { sortProviderList } from 'utils/sortUtils';
-import { isProviderCompatible } from 'utils/multiAddrUtils';
+import { isProviderOutdated } from 'utils/multiAddrUtils';
import { getStubTestnetProvider } from 'store/config';
const stubTestnetProvider = getStubTestnetProvider();
@@ -81,13 +81,12 @@ export const providersSlice = createSlice({
slice,
action: PayloadAction,
) {
+ // Add stub testnet provider if it exists
if (stubTestnetProvider) {
action.payload.push(stubTestnetProvider);
}
- slice.registry.providers = sortProviderList(action.payload).filter(
- isProviderCompatible,
- );
+ slice.registry.providers = sortProviderList(action.payload);
slice.selectedProvider = selectNewSelectedProvider(slice);
},
increaseFailedRegistryReconnectAttemptsSinceLastSuccess(slice) {
diff --git a/src/store/features/swapSlice.ts b/src/store/features/swapSlice.ts
index 622ad17..22ee940 100644
--- a/src/store/features/swapSlice.ts
+++ b/src/store/features/swapSlice.ts
@@ -8,11 +8,14 @@ import {
isSwapStateWaitingForBtcDeposit,
isSwapStateXmrLockInMempool,
SwapSlice,
+ SwapStateAttemptingCooperativeRedeeem,
SwapStateBtcCancelled,
SwapStateBtcLockInMempool,
SwapStateBtcPunished,
SwapStateBtcRedemeed,
SwapStateBtcRefunded,
+ SwapStateCooperativeRedeemAccepted,
+ SwapStateCooperativeRedeemRejected,
SwapStateInitiated,
SwapStateProcessExited,
SwapStateReceivedQuote,
@@ -42,6 +45,10 @@ import {
isCliLogAcquiringSwapLockLog,
isCliLogApiCallError,
isCliLogDeterminedSwapAmount,
+ isCliLogAttemptingToCooperativelyRedeemXmr,
+ isCliLogAliceRejectedOurRequestForCooperativeXmrRedeem,
+ isCliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr,
+ isCliLogFailedToRequestCooperativeXmrRedeemFromAlice,
} from '../../models/cliModel';
import logger from '../../utils/logger';
@@ -249,6 +256,36 @@ export const swapSlice = createSlice({
type: SwapStateType.BTC_PUNISHED,
};
+ slice.state = nextState;
+ } else if (isCliLogAttemptingToCooperativelyRedeemXmr(log)) {
+ const nextState: SwapStateAttemptingCooperativeRedeeem = {
+ type: SwapStateType.ATTEMPTING_COOPERATIVE_REDEEM,
+ };
+
+ slice.state = nextState;
+ } else if (
+ isCliLogAliceRejectedOurRequestForCooperativeXmrRedeem(log)
+ ) {
+ const nextState: SwapStateCooperativeRedeemRejected = {
+ type: SwapStateType.COOPERATIVE_REDEEM_REJECTED,
+ reason: log.fields.reason,
+ };
+
+ slice.state = nextState;
+ } else if (
+ isCliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr(log)
+ ) {
+ const nextState: SwapStateCooperativeRedeemAccepted = {
+ type: SwapStateType.COOPERATIVE_REDEEM_ACCEPTED,
+ };
+
+ slice.state = nextState;
+ } else if (isCliLogFailedToRequestCooperativeXmrRedeemFromAlice(log)) {
+ const nextState: SwapStateCooperativeRedeemRejected = {
+ type: SwapStateType.COOPERATIVE_REDEEM_REJECTED,
+ reason: 'Failed to connect to Alice: ' + log.fields.error,
+ };
+
slice.state = nextState;
} else if (
isCliLogReleasingSwapLockLog(log) &&
diff --git a/src/store/hooks.ts b/src/store/hooks.ts
index df19f6e..b417a7e 100644
--- a/src/store/hooks.ts
+++ b/src/store/hooks.ts
@@ -2,20 +2,30 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { AppDispatch, RootState } from 'renderer/store/storeRenderer';
import { sortBy } from 'lodash';
import { parseDateString } from 'utils/parseUtils';
+import { GetSwapInfoResponse, SwapStateName } from 'models/rpcModel';
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch();
export const useAppSelector: TypedUseSelectorHook = useSelector;
-export function useResumeableSwapsCount() {
+export function useResumeableSwapsCount(
+ filter?: (s: GetSwapInfoResponse) => boolean,
+) {
return useAppSelector(
(state) =>
Object.values(state.rpc.state.swapInfos).filter(
- (swapInfo) => !swapInfo.completed,
+ (swapInfo) =>
+ !swapInfo.completed && (filter == null || filter(swapInfo)),
).length,
);
}
+export function useResumeableSwapsCountExcludingPunished() {
+ return useResumeableSwapsCount(
+ (s) => s.stateName !== SwapStateName.BtcPunished,
+ );
+}
+
export function useIsSwapRunning() {
return useAppSelector((state) => state.swap.state !== null);
}
diff --git a/src/utils/multiAddrUtils.ts b/src/utils/multiAddrUtils.ts
index 71a3669..8536c76 100644
--- a/src/utils/multiAddrUtils.ts
+++ b/src/utils/multiAddrUtils.ts
@@ -3,7 +3,7 @@ import semver from 'semver';
import { ExtendedProviderStatus, Provider } from 'models/apiModel';
import { isTestnet } from 'store/config';
-const MIN_ASB_VERSION = '0.12.0';
+const MIN_ASB_VERSION = '0.13.3';
export function providerToConcatenatedMultiAddr(provider: Provider) {
return new Multiaddr(provider.multiAddr)
@@ -11,14 +11,19 @@ export function providerToConcatenatedMultiAddr(provider: Provider) {
.toString();
}
-export function isProviderCompatible(
- provider: ExtendedProviderStatus,
-): boolean {
+export function isProviderOutdated(provider: ExtendedProviderStatus): boolean {
if (provider.version) {
- if (!semver.satisfies(provider.version, `>=${MIN_ASB_VERSION}`))
+ if (semver.satisfies(provider.version, `>=${MIN_ASB_VERSION}`))
return false;
+ } else {
+ return false;
}
- if (provider.testnet !== isTestnet()) return false;
return true;
}
+
+export function isProviderCompatible(
+ provider: ExtendedProviderStatus,
+): boolean {
+ return provider.testnet === isTestnet();
+}
diff --git a/src/utils/sortUtils.ts b/src/utils/sortUtils.ts
index 0b6185c..f2059c1 100644
--- a/src/utils/sortUtils.ts
+++ b/src/utils/sortUtils.ts
@@ -1,19 +1,23 @@
import { ExtendedProviderStatus } from 'models/apiModel';
+import { isProviderCompatible } from './multiAddrUtils';
export function sortProviderList(list: ExtendedProviderStatus[]) {
- return list.concat().sort((firstEl, secondEl) => {
- // If neither of them have a relevancy score, sort by max swap amount
- if (firstEl.relevancy === undefined && secondEl.relevancy === undefined) {
- if (firstEl.maxSwapAmount > secondEl.maxSwapAmount) {
+ return list
+ .filter(isProviderCompatible)
+ .concat()
+ .sort((firstEl, secondEl) => {
+ // If neither of them have a relevancy score, sort by max swap amount
+ if (firstEl.relevancy === undefined && secondEl.relevancy === undefined) {
+ if (firstEl.maxSwapAmount > secondEl.maxSwapAmount) {
+ return -1;
+ }
+ }
+ // If only on of the two don't have a relevancy score, prioritize the one that does
+ if (firstEl.relevancy === undefined) return 1;
+ if (secondEl.relevancy === undefined) return -1;
+ if (firstEl.relevancy > secondEl.relevancy) {
return -1;
}
- }
- // If only on of the two don't have a relevancy score, prioritize the one that does
- if (firstEl.relevancy === undefined) return 1;
- if (secondEl.relevancy === undefined) return -1;
- if (firstEl.relevancy > secondEl.relevancy) {
- return -1;
- }
- return 1;
- });
+ return 1;
+ });
}