Skip to content

Commit 875bdf7

Browse files
authored
Merge pull request #11 from opendexnetwork/feat/connext-deposit
feat: implement deposit to a connext address
2 parents 5cc6aad + b911905 commit 875bdf7

File tree

7 files changed

+317
-215
lines changed

7 files changed

+317
-215
lines changed

src/common/currencyUtil.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,6 @@ export const getCurrencyFullName = (currency: string): string => {
4444
const fullName = CURRENCY_DICTIONARY[currency.toUpperCase()]?.fullName;
4545
return fullName || currency;
4646
};
47+
48+
export const isLnd = (currency: string): boolean =>
49+
["BTC", "LTC"].includes(currency);

src/dashboard/Dashboard.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,15 @@ const Dashboard = (): ReactElement => {
129129
.pipe(takeUntil(lndsReady$))
130130
.subscribe({
131131
next: (status: SetupStatusResponse | null) => {
132-
if (status) {
132+
if (status && status.details) {
133133
setSyncInProgress(true);
134-
setMenuItemTooltipMsg(
135-
status.details
136-
? [
137-
"Waiting for initial sync...",
138-
`Bitcoin: ${status.details["lndbtc"]}`,
139-
`Litecoin: ${status.details["lndltc"]}`,
140-
]
141-
: [status.status]
142-
);
134+
setMenuItemTooltipMsg([
135+
"Waiting for initial sync...",
136+
`Bitcoin: ${status.details["lndbtc"]}`,
137+
`Litecoin: ${status.details["lndltc"]}`,
138+
]);
139+
} else {
140+
setSyncInProgress(false);
143141
}
144142
},
145143
error: () => {
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import {
2+
createStyles,
3+
Grid,
4+
makeStyles,
5+
Theme,
6+
Typography,
7+
} from "@material-ui/core";
8+
import React, { ReactElement, useEffect, useState } from "react";
9+
import { combineLatest, Observable, Subject } from "rxjs";
10+
import api from "../../api";
11+
import { satsToCoinsStr } from "../../common/currencyUtil";
12+
import { getErrorMsg } from "../../common/errorUtil";
13+
import QrCode from "../../common/QrCode";
14+
import WarningMessage from "../../common/WarningMessage";
15+
import { DepositResponse } from "../../models/DepositResponse";
16+
import { GetServiceInfoResponse } from "../../models/GetServiceInfoResponse";
17+
import { Info } from "../../models/Info";
18+
import Address from "./Address";
19+
import BoltzFeeInfo from "./BoltzFeeInfo";
20+
import CheckBoltzTransactionStatus from "./CheckBoltzTransactionStatus";
21+
22+
type BoltzDepositProps = {
23+
currency: string;
24+
refreshSubject: Subject<void>;
25+
getInfo$: Observable<Info>;
26+
setError: (value: string) => void;
27+
setFetchingData: (value: boolean) => void;
28+
};
29+
30+
const useStyles = makeStyles((theme: Theme) =>
31+
createStyles({
32+
row: {
33+
paddingTop: theme.spacing(2),
34+
},
35+
})
36+
);
37+
38+
const getAvgMinutesBetweenBlocks = (
39+
start: number,
40+
end: number,
41+
currency: string
42+
): number | null => {
43+
const diff = end - start;
44+
if (currency === "BTC") {
45+
return diff * 10;
46+
}
47+
if (currency === "LTC") {
48+
return Math.round(diff * 2.5);
49+
}
50+
return null;
51+
};
52+
53+
const getTimeString = (minutes: number): string => {
54+
const hours = Math.floor(minutes / 60);
55+
const remainingMins = minutes % 60;
56+
const hoursStr = hours ? `${hours} hours` : "";
57+
const minutesStr = remainingMins ? `${remainingMins} minutes` : "";
58+
return [hoursStr, minutesStr].join(" ");
59+
};
60+
61+
const BoltzDeposit = (props: BoltzDepositProps): ReactElement => {
62+
const {
63+
currency,
64+
refreshSubject,
65+
getInfo$,
66+
setError,
67+
setFetchingData,
68+
} = props;
69+
const classes = useStyles();
70+
71+
const [depositData, setDepositData] = useState<DepositResponse | undefined>(
72+
undefined
73+
);
74+
const [serviceInfo, setServiceInfo] = useState<
75+
GetServiceInfoResponse | undefined
76+
>(undefined);
77+
const [currentBlockHeight, setCurrentBlockHeight] = useState<
78+
number | undefined
79+
>(undefined);
80+
const [qrOpen, setQrOpen] = useState(false);
81+
const [addressAutoUpdated, setAddressAutoUpdated] = useState(false);
82+
83+
useEffect(() => {
84+
const fetchDepositData = (): void => {
85+
combineLatest([
86+
api.boltzDeposit$(currency),
87+
api.boltzServiceInfo$(currency),
88+
]).subscribe({
89+
next: (resp) => {
90+
setDepositData(resp[0]);
91+
setServiceInfo(resp[1]);
92+
setFetchingData(false);
93+
},
94+
error: (err) => {
95+
setError(getErrorMsg(err));
96+
setFetchingData(false);
97+
},
98+
});
99+
};
100+
101+
fetchDepositData();
102+
const subscription = refreshSubject.subscribe(() => fetchDepositData());
103+
104+
return () => subscription.unsubscribe();
105+
}, [currency, refreshSubject, setError, setFetchingData]);
106+
107+
useEffect(() => {
108+
const subscription = getInfo$.subscribe({
109+
next: (resp) => {
110+
const currentHeight = Number.parseInt(resp.lnd[currency].blockheight);
111+
setCurrentBlockHeight(currentHeight);
112+
if (depositData && currentHeight >= depositData.timeoutBlockHeight) {
113+
refreshSubject.next();
114+
setAddressAutoUpdated(true);
115+
}
116+
},
117+
error: (err) => setError(getErrorMsg(err)),
118+
});
119+
120+
return () => subscription.unsubscribe();
121+
}, [getInfo$, currency, refreshSubject, depositData, setError]);
122+
123+
return (
124+
<>
125+
{!!depositData && !!serviceInfo && !!currentBlockHeight && (
126+
<>
127+
{addressAutoUpdated && (
128+
<WarningMessage message="Address updated due to timeout" />
129+
)}
130+
{!qrOpen ? (
131+
<Grid item>
132+
<Typography variant="body2" align="center">
133+
Deposit between {satsToCoinsStr(serviceInfo.limits.minimal)} and{" "}
134+
{satsToCoinsStr(serviceInfo.limits.maximal, currency)} in the
135+
following address in the next ~
136+
{getTimeString(
137+
getAvgMinutesBetweenBlocks(
138+
currentBlockHeight,
139+
depositData.timeoutBlockHeight,
140+
currency
141+
)!
142+
)}{" "}
143+
(block height {depositData.timeoutBlockHeight}).
144+
</Typography>
145+
<Address
146+
address={depositData.address}
147+
openQr={() => setQrOpen(true)}
148+
readOnly={true}
149+
/>
150+
<BoltzFeeInfo fees={serviceInfo.fees} currency={currency} />
151+
<div className={classes.row}>
152+
<CheckBoltzTransactionStatus
153+
currency={currency}
154+
id={depositData.id}
155+
/>
156+
</div>
157+
</Grid>
158+
) : (
159+
<Grid item>
160+
<QrCode
161+
value={depositData.address}
162+
handleClose={() => setQrOpen(false)}
163+
/>
164+
</Grid>
165+
)}
166+
</>
167+
)}
168+
</>
169+
);
170+
};
171+
172+
export default BoltzDeposit;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Grid, Typography } from "@material-ui/core";
2+
import React, { ReactElement, useEffect, useState } from "react";
3+
import { Observable } from "rxjs";
4+
import { getErrorMsg } from "../../common/errorUtil";
5+
import QrCode from "../../common/QrCode";
6+
import { Info } from "../../models/Info";
7+
import Address from "./Address";
8+
9+
type ConnextDepositProps = {
10+
currency: string;
11+
getInfo$: Observable<Info>;
12+
setError: (value: string) => void;
13+
setFetchingData: (value: boolean) => void;
14+
};
15+
16+
const ConnextDeposit = (props: ConnextDepositProps): ReactElement => {
17+
const { currency, getInfo$, setError, setFetchingData } = props;
18+
const [qrOpen, setQrOpen] = useState(false);
19+
const [address, setAddress] = useState("");
20+
21+
useEffect(() => {
22+
getInfo$.subscribe({
23+
next: (resp) => {
24+
const connextAddress = resp.connext?.address;
25+
if (connextAddress) {
26+
setAddress(resp.connext.address);
27+
} else {
28+
setError("Cannot find address");
29+
}
30+
setFetchingData(false);
31+
},
32+
error: (err) => {
33+
setError(getErrorMsg(err));
34+
setFetchingData(false);
35+
},
36+
});
37+
}, [getInfo$, setError, setFetchingData]);
38+
39+
return (
40+
<>
41+
{!qrOpen ? (
42+
<Grid item>
43+
<Typography variant="body2" align="center">
44+
Deposit {currency} in the following address from your wallet
45+
</Typography>
46+
<Address
47+
address={address}
48+
openQr={() => setQrOpen(true)}
49+
readOnly={true}
50+
/>
51+
</Grid>
52+
) : (
53+
<Grid item>
54+
<QrCode value={address} handleClose={() => setQrOpen(false)} />
55+
</Grid>
56+
)}
57+
</>
58+
);
59+
};
60+
61+
export default ConnextDeposit;

0 commit comments

Comments
 (0)