Skip to content

Commit 1724184

Browse files
authored
Merge pull request #193 from lightninglabs/failure-reasons
loop+history: display failure reason on History Page
2 parents 45d559e + bb40620 commit 1724184

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

app/src/__tests__/components/history/HistoryRow.spec.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import * as LOOP from 'types/generated/loop_pb';
23
import { renderWithProviders } from 'util/tests';
34
import { loopListSwaps } from 'util/tests/sampleData';
45
import { Swap } from 'store/models';
@@ -8,7 +9,7 @@ describe('HistoryRow component', () => {
89
let swap: Swap;
910

1011
beforeEach(async () => {
11-
swap = new Swap(loopListSwaps.swapsList[0]);
12+
swap = new Swap(loopListSwaps.swapsList[1]);
1213
});
1314

1415
const render = () => {
@@ -20,6 +21,22 @@ describe('HistoryRow component', () => {
2021
expect(getByText(swap.stateLabel)).toBeInTheDocument();
2122
});
2223

24+
it.each<[number, string]>([
25+
[LOOP.FailureReason.FAILURE_REASON_NONE, 'Failed'],
26+
[LOOP.FailureReason.FAILURE_REASON_OFFCHAIN, 'Off-chain Failure'],
27+
[LOOP.FailureReason.FAILURE_REASON_TIMEOUT, 'On-chain Timeout'],
28+
[LOOP.FailureReason.FAILURE_REASON_SWEEP_TIMEOUT, 'Sweep Timeout'],
29+
[LOOP.FailureReason.FAILURE_REASON_INSUFFICIENT_VALUE, 'Insufficient Value'],
30+
[LOOP.FailureReason.FAILURE_REASON_TEMPORARY, 'Temporary Failure'],
31+
[LOOP.FailureReason.FAILURE_REASON_INCORRECT_AMOUNT, 'Incorrect Amount'],
32+
])('should display correct dot icon for a "(%s) %s"', (reason, label) => {
33+
swap.state = LOOP.SwapState.FAILED;
34+
swap.failureReason = reason;
35+
36+
const { getByText } = render();
37+
expect(getByText(label)).toBeInTheDocument();
38+
});
39+
2340
it('should display the type', () => {
2441
const { getByText } = render();
2542
expect(getByText(swap.typeName)).toBeInTheDocument();

app/src/components/history/HistoryRow.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { observer } from 'mobx-react-lite';
33
import { usePrefixedTranslation } from 'hooks';
44
import { useStore } from 'store';
55
import { Swap } from 'store/models';
6-
import { Column, Row } from 'components/base';
6+
import { Column, HelpCircle, Row } from 'components/base';
77
import SortableHeader from 'components/common/SortableHeader';
8+
import Tip from 'components/common/Tip';
89
import Unit from 'components/common/Unit';
910
import SwapDot from 'components/loop/SwapDot';
1011
import { styled } from 'components/theme';
@@ -110,13 +111,31 @@ interface Props {
110111
}
111112

112113
const HistoryRow: React.FC<Props> = ({ swap, style }) => {
114+
const { l } = usePrefixedTranslation('cmps.history.HistoryRow');
113115
const { Row, Column, ActionColumn } = Styled;
114116
return (
115117
<Row style={style}>
116118
<ActionColumn>
117119
<SwapDot swap={swap} />
118120
</ActionColumn>
119-
<Column cols={3}>{swap.stateLabel}</Column>
121+
<Column cols={3}>
122+
{swap.stateLabel !== 'Failed' ? (
123+
swap.stateLabel
124+
) : (
125+
<>
126+
{swap.failureLabel}
127+
<Tip
128+
overlay={l(`failure-${swap.failureReason}`)}
129+
capitalize={false}
130+
maxWidth={300}
131+
>
132+
<span>
133+
<HelpCircle />
134+
</span>
135+
</Tip>
136+
</>
137+
)}
138+
</Column>
120139
<Column>{swap.typeName}</Column>
121140
<Column right>
122141
<Unit sats={swap.amount} suffix={false} />

app/src/i18n/locales/en-US.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
"cmps.history.HistoryRowHeader.type": "Type",
3434
"cmps.history.HistoryRowHeader.created": "Created",
3535
"cmps.history.HistoryRowHeader.updated": "Updated",
36+
"cmps.history.HistoryRow.failure-0": "The swap has failed to complete successfully. Check the logs for additional information.",
37+
"cmps.history.HistoryRow.failure-1": "Unable to find a route for one or both off chain payments that met the fee and timelock limits required.",
38+
"cmps.history.HistoryRow.failure-2": "The on-chain HTLC did not confirm before its expiry, or it confirmed too late for us to reveal our preimage and claim.",
39+
"cmps.history.HistoryRow.failure-3": "The on-chain HTLC wasn't swept before the server revoked the HTLC.",
40+
"cmps.history.HistoryRow.failure-4": "The on-chain HTLC has a lower value than requested.",
41+
"cmps.history.HistoryRow.failure-5": "The swap cannot continue due to an internal error. Manual intervention such as a restart is required.",
42+
"cmps.history.HistoryRow.failure-6": "The amount extended by an external Loop In HTLC is insufficient.",
3643
"cmps.loop.ChannelIcon.processing.in": "Loop In currently in progress",
3744
"cmps.loop.ChannelIcon.processing.out": "Loop Out currently in progress",
3845
"cmps.loop.ChannelIcon.processing.both": "Loop In and Loop Out currently in progress",

app/src/store/models/swap.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export default class Swap {
1515
initiationTime = 0;
1616
lastUpdateTime = 0;
1717
state = 0;
18+
failureReason = 0;
1819

1920
constructor(loopSwap: LOOP.SwapStatus.AsObject) {
2021
makeAutoObservable(this, {}, { deep: false, autoBind: true });
@@ -81,6 +82,29 @@ export default class Swap {
8182
return 'Unknown';
8283
}
8384

85+
/**
86+
* The numeric swap `failureReason` as a user friendly string
87+
*/
88+
get failureLabel() {
89+
switch (this.failureReason) {
90+
case LOOP.FailureReason.FAILURE_REASON_OFFCHAIN:
91+
return 'Off-chain Failure';
92+
case LOOP.FailureReason.FAILURE_REASON_TIMEOUT:
93+
return 'On-chain Timeout';
94+
case LOOP.FailureReason.FAILURE_REASON_SWEEP_TIMEOUT:
95+
return 'Sweep Timeout';
96+
case LOOP.FailureReason.FAILURE_REASON_INSUFFICIENT_VALUE:
97+
return 'Insufficient Value';
98+
case LOOP.FailureReason.FAILURE_REASON_TEMPORARY:
99+
return 'Temporary Failure';
100+
case LOOP.FailureReason.FAILURE_REASON_INCORRECT_AMOUNT:
101+
return 'Incorrect Amount';
102+
case LOOP.FailureReason.FAILURE_REASON_NONE:
103+
default:
104+
return 'Failed';
105+
}
106+
}
107+
84108
/** The date this swap was created as a JS Date object */
85109
get createdOn() {
86110
return new Date(this.initiationTime / 1000 / 1000);
@@ -112,6 +136,7 @@ export default class Swap {
112136
this.initiationTime = loopSwap.initiationTime;
113137
this.lastUpdateTime = loopSwap.lastUpdateTime;
114138
this.state = loopSwap.state;
139+
this.failureReason = loopSwap.failureReason;
115140
}
116141

117142
/**

0 commit comments

Comments
 (0)