mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-10 23:40:23 -04:00
feat(gui): Allow to select from recently used monero addresses (#139)
* feat(gui): Allow user to select from recently used monero addresses in textfield
This commit is contained in:
parent
4867d2713f
commit
bd3fca7e41
11 changed files with 271 additions and 78 deletions
|
@ -10,6 +10,14 @@ import SwapPage from "./pages/swap/SwapPage";
|
||||||
import WalletPage from "./pages/wallet/WalletPage";
|
import WalletPage from "./pages/wallet/WalletPage";
|
||||||
import GlobalSnackbarProvider from "./snackbar/GlobalSnackbarProvider";
|
import GlobalSnackbarProvider from "./snackbar/GlobalSnackbarProvider";
|
||||||
import UpdaterDialog from "./modal/updater/UpdaterDialog";
|
import UpdaterDialog from "./modal/updater/UpdaterDialog";
|
||||||
|
import { initEventListeners } from "renderer/rpc";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { fetchProvidersViaHttp, fetchAlertsViaHttp, fetchXmrPrice, fetchBtcPrice, fetchXmrBtcRate } from "renderer/api";
|
||||||
|
import { store } from "renderer/store/storeRenderer";
|
||||||
|
import { setAlerts } from "store/features/alertsSlice";
|
||||||
|
import { setRegistryProviders, registryConnectionFailed } from "store/features/providersSlice";
|
||||||
|
import { setXmrPrice, setBtcPrice, setXmrBtcRate } from "store/features/ratesSlice";
|
||||||
|
import logger from "utils/logger";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
innerContent: {
|
innerContent: {
|
||||||
|
@ -52,6 +60,11 @@ function InnerContent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
useEffect(() => {
|
||||||
|
fetchInitialData();
|
||||||
|
initEventListeners();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<GlobalSnackbarProvider>
|
<GlobalSnackbarProvider>
|
||||||
|
@ -65,3 +78,46 @@ export default function App() {
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchInitialData() {
|
||||||
|
try {
|
||||||
|
const providerList = await fetchProvidersViaHttp();
|
||||||
|
store.dispatch(setRegistryProviders(providerList));
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
{ providerList },
|
||||||
|
"Fetched providers via UnstoppableSwap HTTP API",
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
store.dispatch(registryConnectionFailed());
|
||||||
|
logger.error(e, "Failed to fetch providers via UnstoppableSwap HTTP API");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const alerts = await fetchAlertsViaHttp();
|
||||||
|
store.dispatch(setAlerts(alerts));
|
||||||
|
logger.info({ alerts }, "Fetched alerts via UnstoppableSwap HTTP API");
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e, "Failed to fetch alerts via UnstoppableSwap HTTP API");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const xmrPrice = await fetchXmrPrice();
|
||||||
|
store.dispatch(setXmrPrice(xmrPrice));
|
||||||
|
logger.info({ xmrPrice }, "Fetched XMR price");
|
||||||
|
|
||||||
|
const btcPrice = await fetchBtcPrice();
|
||||||
|
store.dispatch(setBtcPrice(btcPrice));
|
||||||
|
logger.info({ btcPrice }, "Fetched BTC price");
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e, "Error retrieving fiat prices");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const xmrBtcRate = await fetchXmrBtcRate();
|
||||||
|
store.dispatch(setXmrBtcRate(xmrBtcRate));
|
||||||
|
logger.info({ xmrBtcRate }, "Fetched XMR/BTC rate");
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e, "Error retrieving XMR/BTC rate");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,18 @@
|
||||||
import { TextField } from "@material-ui/core";
|
import { Box, Button, Dialog, DialogActions, DialogContent, IconButton, List, ListItem, ListItemText, TextField } from "@material-ui/core";
|
||||||
import { TextFieldProps } from "@material-ui/core/TextField/TextField";
|
import { TextFieldProps } from "@material-ui/core/TextField/TextField";
|
||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { getMoneroAddresses } from "renderer/rpc";
|
||||||
import { isTestnet } from "store/config";
|
import { isTestnet } from "store/config";
|
||||||
import { isXmrAddressValid } from "utils/conversionUtils";
|
import { isXmrAddressValid } from "utils/conversionUtils";
|
||||||
|
import ImportContactsIcon from '@material-ui/icons/ImportContacts';
|
||||||
|
import TruncatedText from "../other/TruncatedText";
|
||||||
|
|
||||||
|
type MoneroAddressTextFieldProps = TextFieldProps & {
|
||||||
|
address: string;
|
||||||
|
onAddressChange: (address: string) => void;
|
||||||
|
onAddressValidityChange: (valid: boolean) => void;
|
||||||
|
helperText: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function MoneroAddressTextField({
|
export default function MoneroAddressTextField({
|
||||||
address,
|
address,
|
||||||
|
@ -10,30 +20,116 @@ export default function MoneroAddressTextField({
|
||||||
onAddressValidityChange,
|
onAddressValidityChange,
|
||||||
helperText,
|
helperText,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: MoneroAddressTextFieldProps) {
|
||||||
address: string;
|
const [addresses, setAddresses] = useState<string[]>([]);
|
||||||
onAddressChange: (address: string) => void;
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
onAddressValidityChange: (valid: boolean) => void;
|
|
||||||
helperText: string;
|
// Validation
|
||||||
} & TextFieldProps) {
|
|
||||||
const placeholder = isTestnet() ? "59McWTPGc745..." : "888tNkZrPN6J...";
|
const placeholder = isTestnet() ? "59McWTPGc745..." : "888tNkZrPN6J...";
|
||||||
const errorText = isXmrAddressValid(address, isTestnet())
|
const errorText = isXmrAddressValid(address, isTestnet())
|
||||||
? null
|
? null
|
||||||
: "Not a valid Monero address";
|
: "Not a valid Monero address";
|
||||||
|
|
||||||
|
// Effects
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onAddressValidityChange(!errorText);
|
onAddressValidityChange(!errorText);
|
||||||
}, [address, onAddressValidityChange, errorText]);
|
}, [address, onAddressValidityChange, errorText]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAddresses = async () => {
|
||||||
|
const response = await getMoneroAddresses();
|
||||||
|
setAddresses(response.addresses);
|
||||||
|
};
|
||||||
|
fetchAddresses();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
const handleClose = () => setShowDialog(false);
|
||||||
|
const handleAddressSelect = (selectedAddress: string) => {
|
||||||
|
onAddressChange(selectedAddress);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<Box>
|
||||||
value={address}
|
<TextField
|
||||||
onChange={(e) => onAddressChange(e.target.value)}
|
value={address}
|
||||||
error={!!errorText && address.length > 0}
|
onChange={(e) => onAddressChange(e.target.value)}
|
||||||
helperText={address.length > 0 ? errorText || helperText : helperText}
|
error={!!errorText && address.length > 0}
|
||||||
placeholder={placeholder}
|
helperText={address.length > 0 ? errorText || helperText : helperText}
|
||||||
variant="outlined"
|
placeholder={placeholder}
|
||||||
{...props}
|
variant="outlined"
|
||||||
/>
|
InputProps={{
|
||||||
|
endAdornment: addresses?.length > 0 && (
|
||||||
|
<IconButton onClick={() => setShowDialog(true)} size="small">
|
||||||
|
<ImportContactsIcon />
|
||||||
|
</IconButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RecentlyUsedAddressesDialog
|
||||||
|
open={showDialog}
|
||||||
|
onClose={handleClose}
|
||||||
|
addresses={addresses}
|
||||||
|
onAddressSelect={handleAddressSelect}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RecentlyUsedAddressesDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
addresses: string[];
|
||||||
|
onAddressSelect: (address: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RecentlyUsedAddressesDialog({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
addresses,
|
||||||
|
onAddressSelect
|
||||||
|
}: RecentlyUsedAddressesDialogProps) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<List>
|
||||||
|
{addresses.map((addr) => (
|
||||||
|
<ListItem
|
||||||
|
button
|
||||||
|
key={addr}
|
||||||
|
onClick={() => onAddressSelect(addr)}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
primary={
|
||||||
|
<Box fontFamily="monospace">
|
||||||
|
<TruncatedText limit={40} truncateMiddle>
|
||||||
|
{addr}
|
||||||
|
</TruncatedText>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
secondary="Recently used as a redeem address"
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={onClose}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,20 @@ export default function TruncatedText({
|
||||||
children,
|
children,
|
||||||
limit = 6,
|
limit = 6,
|
||||||
ellipsis = "...",
|
ellipsis = "...",
|
||||||
|
truncateMiddle = false,
|
||||||
}: {
|
}: {
|
||||||
children: string;
|
children: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
ellipsis?: string;
|
ellipsis?: string;
|
||||||
|
truncateMiddle?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const truncatedText =
|
const truncatedText = children.length > limit
|
||||||
children.length > limit ? children.slice(0, limit) + ellipsis : children;
|
? truncateMiddle
|
||||||
|
? children.slice(0, Math.floor(limit/2)) +
|
||||||
|
ellipsis +
|
||||||
|
children.slice(children.length - Math.floor(limit/2))
|
||||||
|
: children.slice(0, limit) + ellipsis
|
||||||
|
: children;
|
||||||
|
|
||||||
return truncatedText;
|
return <span>{truncatedText}</span>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,49 +29,3 @@ root.render(
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
async function fetchInitialData() {
|
|
||||||
try {
|
|
||||||
const providerList = await fetchProvidersViaHttp();
|
|
||||||
store.dispatch(setRegistryProviders(providerList));
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
{ providerList },
|
|
||||||
"Fetched providers via UnstoppableSwap HTTP API",
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
store.dispatch(registryConnectionFailed());
|
|
||||||
logger.error(e, "Failed to fetch providers via UnstoppableSwap HTTP API");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const alerts = await fetchAlertsViaHttp();
|
|
||||||
store.dispatch(setAlerts(alerts));
|
|
||||||
logger.info({ alerts }, "Fetched alerts via UnstoppableSwap HTTP API");
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(e, "Failed to fetch alerts via UnstoppableSwap HTTP API");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const xmrPrice = await fetchXmrPrice();
|
|
||||||
store.dispatch(setXmrPrice(xmrPrice));
|
|
||||||
logger.info({ xmrPrice }, "Fetched XMR price");
|
|
||||||
|
|
||||||
const btcPrice = await fetchBtcPrice();
|
|
||||||
store.dispatch(setBtcPrice(btcPrice));
|
|
||||||
logger.info({ btcPrice }, "Fetched BTC price");
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(e, "Error retrieving fiat prices");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const xmrBtcRate = await fetchXmrBtcRate();
|
|
||||||
store.dispatch(setXmrBtcRate(xmrBtcRate));
|
|
||||||
logger.info({ xmrBtcRate }, "Fetched XMR/BTC rate");
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(e, "Error retrieving XMR/BTC rate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchInitialData();
|
|
||||||
initEventListeners();
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
TauriTimelockChangeEvent,
|
TauriTimelockChangeEvent,
|
||||||
GetSwapInfoArgs,
|
GetSwapInfoArgs,
|
||||||
ExportBitcoinWalletResponse,
|
ExportBitcoinWalletResponse,
|
||||||
|
GetMoneroAddressesResponse,
|
||||||
} from "models/tauriModel";
|
} from "models/tauriModel";
|
||||||
import {
|
import {
|
||||||
contextStatusEventReceived,
|
contextStatusEventReceived,
|
||||||
|
@ -219,3 +220,7 @@ export async function initializeContext() {
|
||||||
export async function getWalletDescriptor() {
|
export async function getWalletDescriptor() {
|
||||||
return await invokeNoArgs<ExportBitcoinWalletResponse>("get_wallet_descriptor");
|
return await invokeNoArgs<ExportBitcoinWalletResponse>("get_wallet_descriptor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMoneroAddresses(): Promise<GetMoneroAddressesResponse> {
|
||||||
|
return await invokeNoArgs<GetMoneroAddressesResponse>("get_monero_addresses");
|
||||||
|
}
|
|
@ -5,8 +5,9 @@ use swap::cli::{
|
||||||
api::{
|
api::{
|
||||||
request::{
|
request::{
|
||||||
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ExportBitcoinWalletArgs, GetHistoryArgs,
|
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ExportBitcoinWalletArgs, GetHistoryArgs,
|
||||||
GetLogsArgs, GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs,
|
GetLogsArgs, GetMoneroAddressesArgs, GetSwapInfoArgs, GetSwapInfosAllArgs,
|
||||||
ResumeSwapArgs, SuspendCurrentSwapArgs, WithdrawBtcArgs,
|
ListSellersArgs, MoneroRecoveryArgs, ResumeSwapArgs, SuspendCurrentSwapArgs,
|
||||||
|
WithdrawBtcArgs,
|
||||||
},
|
},
|
||||||
tauri_bindings::{TauriContextStatusEvent, TauriEmitter, TauriHandle, TauriSettings},
|
tauri_bindings::{TauriContextStatusEvent, TauriEmitter, TauriHandle, TauriSettings},
|
||||||
Context, ContextBuilder,
|
Context, ContextBuilder,
|
||||||
|
@ -146,6 +147,7 @@ pub fn run() {
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
get_balance,
|
get_balance,
|
||||||
|
get_monero_addresses,
|
||||||
get_swap_info,
|
get_swap_info,
|
||||||
get_swap_infos_all,
|
get_swap_infos_all,
|
||||||
withdraw_btc,
|
withdraw_btc,
|
||||||
|
@ -200,12 +202,14 @@ tauri_command!(monero_recovery, MoneroRecoveryArgs);
|
||||||
tauri_command!(get_logs, GetLogsArgs);
|
tauri_command!(get_logs, GetLogsArgs);
|
||||||
tauri_command!(list_sellers, ListSellersArgs);
|
tauri_command!(list_sellers, ListSellersArgs);
|
||||||
tauri_command!(cancel_and_refund, CancelAndRefundArgs);
|
tauri_command!(cancel_and_refund, CancelAndRefundArgs);
|
||||||
|
|
||||||
// These commands require no arguments
|
// These commands require no arguments
|
||||||
tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args);
|
tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args);
|
||||||
tauri_command!(suspend_current_swap, SuspendCurrentSwapArgs, no_args);
|
tauri_command!(suspend_current_swap, SuspendCurrentSwapArgs, no_args);
|
||||||
tauri_command!(get_swap_info, GetSwapInfoArgs);
|
tauri_command!(get_swap_info, GetSwapInfoArgs);
|
||||||
tauri_command!(get_swap_infos_all, GetSwapInfosAllArgs, no_args);
|
tauri_command!(get_swap_infos_all, GetSwapInfosAllArgs, no_args);
|
||||||
tauri_command!(get_history, GetHistoryArgs, no_args);
|
tauri_command!(get_history, GetHistoryArgs, no_args);
|
||||||
|
tauri_command!(get_monero_addresses, GetMoneroAddressesArgs, no_args);
|
||||||
|
|
||||||
/// Here we define Tauri commands whose implementation is not delegated to the Request trait
|
/// Here we define Tauri commands whose implementation is not delegated to the Request trait
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false],
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
|
@ -35,7 +37,9 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [true],
|
"nullable": [
|
||||||
|
true
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
|
@ -56,7 +60,10 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false, false],
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 0
|
"Right": 0
|
||||||
}
|
}
|
||||||
|
@ -92,13 +99,33 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false],
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n "
|
"query": "\n SELECT state\n FROM swap_states\n WHERE swap_id = ?\n ORDER BY id desc\n LIMIT 1;\n\n "
|
||||||
},
|
},
|
||||||
|
"98a8b7f4971e0eb4ab8f5aa688aa22e7fdc6b925de211f7784782f051c2dcd8c": {
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "address",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": "SELECT DISTINCT address FROM monero_addresses"
|
||||||
|
},
|
||||||
"b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": {
|
"b703032b4ddc627a1124817477e7a8e5014bdc694c36a14053ef3bb2fc0c69b0": {
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [],
|
"columns": [],
|
||||||
|
@ -118,7 +145,9 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false],
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
|
@ -134,7 +163,9 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false],
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
|
@ -150,7 +181,9 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false],
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
|
@ -176,7 +209,9 @@
|
||||||
"type_info": "Text"
|
"type_info": "Text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nullable": [false],
|
"nullable": [
|
||||||
|
false
|
||||||
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Right": 1
|
"Right": 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -416,6 +416,26 @@ impl Request for GetLogsArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct GetMoneroAddressesArgs;
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct GetMoneroAddressesResponse {
|
||||||
|
#[typeshare(serialized_as = "Vec<String>")]
|
||||||
|
pub addresses: Vec<monero::Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request for GetMoneroAddressesArgs {
|
||||||
|
type Response = GetMoneroAddressesResponse;
|
||||||
|
|
||||||
|
async fn request(self, ctx: Arc<Context>) -> Result<Self::Response> {
|
||||||
|
let addresses = ctx.db.get_monero_addresses().await?;
|
||||||
|
Ok(GetMoneroAddressesResponse { addresses })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(fields(method = "suspend_current_swap"), skip(context))]
|
#[tracing::instrument(fields(method = "suspend_current_swap"), skip(context))]
|
||||||
pub async fn suspend_current_swap(context: Arc<Context>) -> Result<SuspendCurrentSwapResponse> {
|
pub async fn suspend_current_swap(context: Arc<Context>) -> Result<SuspendCurrentSwapResponse> {
|
||||||
let swap_id = context.swap_lock.get_current_swap_id().await;
|
let swap_id = context.swap_lock.get_current_swap_id().await;
|
||||||
|
|
|
@ -143,6 +143,21 @@ impl Database for SqliteDatabase {
|
||||||
Ok(address)
|
Ok(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_monero_addresses(&self) -> Result<Vec<monero::Address>> {
|
||||||
|
let mut conn = self.pool.acquire().await?;
|
||||||
|
|
||||||
|
let rows = sqlx::query!("SELECT DISTINCT address FROM monero_addresses")
|
||||||
|
.fetch_all(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let addresses = rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| row.address.parse())
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok(addresses)
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()> {
|
async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()> {
|
||||||
let mut conn = self.pool.acquire().await?;
|
let mut conn = self.pool.acquire().await?;
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ pub trait Database {
|
||||||
async fn get_peer_id(&self, swap_id: Uuid) -> Result<PeerId>;
|
async fn get_peer_id(&self, swap_id: Uuid) -> Result<PeerId>;
|
||||||
async fn insert_monero_address(&self, swap_id: Uuid, address: monero::Address) -> Result<()>;
|
async fn insert_monero_address(&self, swap_id: Uuid, address: monero::Address) -> Result<()>;
|
||||||
async fn get_monero_address(&self, swap_id: Uuid) -> Result<monero::Address>;
|
async fn get_monero_address(&self, swap_id: Uuid) -> Result<monero::Address>;
|
||||||
|
async fn get_monero_addresses(&self) -> Result<Vec<monero::Address>>;
|
||||||
async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()>;
|
async fn insert_address(&self, peer_id: PeerId, address: Multiaddr) -> Result<()>;
|
||||||
async fn get_addresses(&self, peer_id: PeerId) -> Result<Vec<Multiaddr>>;
|
async fn get_addresses(&self, peer_id: PeerId) -> Result<Vec<Multiaddr>>;
|
||||||
async fn get_swap_start_date(&self, swap_id: Uuid) -> Result<String>;
|
async fn get_swap_start_date(&self, swap_id: Uuid) -> Result<String>;
|
||||||
|
|
BIN
swap/tempdb
BIN
swap/tempdb
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue