mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-11-26 10:46:23 -05:00
feat(gui): DFX.swiss integration (#451)
* feat(gui): Monero wallet
* progress
* refactor
* progress, dont delete wallet, re-fetch approvals and background periodically
* show transaction history correctly
* Enable fetching tx hashes
* Try add the wallet listener event callbacks, not working
* fix: Redeem XMR to internal main wallet, not temp wallet
* feat(monero-sys): Support signing messages
* feat(gui): DFX.swiss integration
* refactor: format, slight refactorings
* progress
* type safety
* refactoring of callback system
* make free floating functions generic
* refactor: Format files
* refactor(gui): Split wallet components and redesign balanceOverview component
* refactor(gui): Add action buttons and transaction section
* wrapper event listener
* progress, compiles
* works!
* WORKS! Event received on balance change
* refactor: format and slight refactorings and comments
* refactor(gui): Start with implementation of send dialog
- new number input
- new button variant and size
* add @tauri-apps/plugin-dialog
* feat(gui): Add permissions for file dialog
* fix(monero-harness): Compile issue
* feat(gui): Extract seed from Monero wallet and use for derivation, allow opening existing wallet file
* feat(gui): Always refresh the approval list from frontend when resolving
* fix(monero-rpc-pool): Implement Into<String> for ServerInfo
* fix(monero-sys): Use oneshot channel for all wallets
* feat(gui, monero-sys): Display recently opened wallets
* small refactors
* fix(gui): Enable background_sync, display temp "Loading..." if values are null
* feat(gui): Remove headers from pages, show selected navigation item
* feat(gui): Explicitly tell user if no swaps have been made yet
* feat(gui): send sync and history updates
* feat(gui): Fetch monero wallet details when context becomes availiable
* feat(gui): Display Monero primary address without modal
* feat(gui): Make "swap" button on wallet page take you to "/swap"
* feat(gui): Rework send modal, adjust number input, added send to field
* feat(gui): set block restore height, not working
* refactor(gui): Optimize number input and add support for switching between currency
* feat(gui): Display real fiat currency prices in send modal
* feat(gui): Add error message for too high send amount
* feat(gui): Modern UI for SeedSelectionDialog
* feat(gui): Wrap MoneroWalletActions
* wip
* refactoring approval callback
* feat(gui): Send Direction of Transaction in History to Frontend
* feat(gui): Let user approve transaction before publishing
* feat: Display 8 digits for Monero amounts by default
* feat(monero-sys): Store pending (non published) transactions in Mutex map inside wallet thread
This allows seperating signing and publishing transactions cleanly
* dprint fmt
* fix(gui): Refresh Monero wallet history C++ struct before serializing
* feat(monero-rpc-pool): Fail after three JSON-RPC errors
* feat(monero-sys): Add wrapper around verify_wallet_password
* feat(gui): Allow opening password-protected Wallets
* refactor: fmt, remove receive button
* fix(gui): Convert to XMR before converting into Fiat
* feat(gui): Add dialog for setting restore height
* feat(gui): block height can be changed, blocks when too low
* refactor(monero-sys): Remove old WalletListener code
* feat(gui): Continually ask for user to select wallet and enter password, if user rejects, offer to select different wallet
* refactor(swap): Extract "select Monero wallet" into own function
* refactor(tauri): Dont kill monero-wallet-rpc
* refactor(tauri): Avoid multiple concurrent Contexts starting
* refactor: Change "Cancel" to "Change wallet" on PasswordEntryDialog
* feat(gui): show curent block height, fix blockage
* Cargo.lock update
* refactor(monero-sys): Use match instead of is_err() and expect(...)
* refactor: better context for WalletHandle constructor method errors handling
* refactor(monero-sys): Common open_with<F>(path: String, daemon: Daemon, wallet_op: F) function
* feat: check empty password before requeston password for wallet
* feat: Remove "Checking for available remote nodes" from frontend
* feat(gui): Allow sweeping entire Monero balance
* feat(monero-rpc-pool): Keep alive TCP connections, do not record JSON-RPC errors as failure if >=3 nodes failed
If >=3 nodes failed we assume it was an actual issue on our side, not an issue with the node
* refactor(swap): Remove dead code
* add comment to WalletHandleListener::on_refreshed{...}
* feat(gui): show current block height in the field
* refactor: remove unused UserCancelledError;
* refactor: No Arc<Mutex<_>> for Pending TXs map
* refactor: remove redundant } catch (error) {
* feat: add our new crates to `OUR_CRATES` in tracing util
* fix(gui): Add math.ceil to piconero conversion to ensure integer
* fix(gui): Close menu when option is clicked
* review and improve/reduce uses of unsafe, also remove unique_ptr wrapper around TransactionHistory to avoid double free
* fix(gui): Use monero amount from units.tsx
* fix(gui): Use PromiseInvokeButton for simplification for approving of send transaction
* update comment, rename function
* refactor(gui): Fix alignment of amounts
* refactor(gui): Remove sending and refreshing states from wallet
* fix(cli, gui): use old seed flow on no tauri, fix minor issues in gui
* fix: use the new named function
* refactor(gui): Add skeletons for monero wallet when still loading
* fix
* get working
* feat(gui): Add tooltip to buy monero button
* refactor: Format files
* refactor(gui): Do not store logs in redux-persist
---------
Co-authored-by: Maksim Kirillov <maksim.kirillov@staticlabs.de>
Co-authored-by: b-enedict <benedict.seuss@gmail.com>
Co-authored-by: einliterflasche <einliterflasche@pm.me>
This commit is contained in:
parent
591d0b8e20
commit
69ddd2486d
15 changed files with 658 additions and 81 deletions
24
src-gui/src/assets/dfx-logo.svg
Normal file
24
src-gui/src/assets/dfx-logo.svg
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<svg width="544" height="170" viewBox="0 0 544 170" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4704_494)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M61.5031 0H124.245C170.646 0 208.267 36.5427 208.267 84.0393C208.267 131.536 169.767 170.018 122.288 170.018H61.5031V135.504H114.046C141.825 135.504 164.541 112.789 164.541 85.009C164.541 57.2293 141.825 34.5136 114.046 34.5136H61.5031V0ZM266.25 31.5686V76.4973H338.294V108.066H266.25V170H226.906V0H355.389V31.5686H266.25ZM495.76 170L454.71 110.975L414.396 170H369.216L432.12 83.5365L372.395 0H417.072L456.183 55.1283L494.557 0H537.061L477.803 82.082L541.191 170H495.778H495.76Z" fill="#072440"/>
|
||||
<path d="M86.1582 126.274C109.821 126.274 129.004 107.092 129.004 83.4287C129.004 59.7657 109.821 40.583 86.1582 40.583C62.4952 40.583 43.3126 59.7657 43.3126 83.4287C43.3126 107.092 62.4952 126.274 86.1582 126.274Z" fill="url(#paint0_linear_4704_494)"/>
|
||||
<path d="M47.1374 132.146C73.1707 132.146 94.2748 111.042 94.2748 85.009C94.2748 58.9757 73.1707 37.8716 47.1374 37.8716C21.1041 37.8716 0 58.9757 0 85.009C0 111.042 21.1041 132.146 47.1374 132.146Z" fill="url(#paint1_linear_4704_494)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_4704_494" x1="122.111" y1="64.6777" x2="45.9618" y2="103.949" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.04" stop-color="#F5516C"/>
|
||||
<stop offset="0.14" stop-color="#C74863"/>
|
||||
<stop offset="0.31" stop-color="#853B57"/>
|
||||
<stop offset="0.44" stop-color="#55324E"/>
|
||||
<stop offset="0.55" stop-color="#382D49"/>
|
||||
<stop offset="0.61" stop-color="#2D2B47"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_4704_494" x1="75.8868" y1="50.7468" x2="15.2815" y2="122.952" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.2" stop-color="#F5516C"/>
|
||||
<stop offset="1" stop-color="#6B3753"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_4704_494">
|
||||
<rect width="541.174" height="170" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,105 @@
|
|||
import {
|
||||
Box,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
Button,
|
||||
DialogContent,
|
||||
Chip,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import { EuroSymbol as EuroIcon } from "@mui/icons-material";
|
||||
import DFXSwissLogo from "assets/dfx-logo.svg";
|
||||
import { useState } from "react";
|
||||
import { dfxAuthenticate } from "renderer/rpc";
|
||||
|
||||
function DFXLogo({ height = 24 }: { height?: number }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
borderRadius: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: 1,
|
||||
height,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={DFXSwissLogo}
|
||||
alt="DFX Swiss"
|
||||
style={{ height: "100%", flex: 1 }}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// Component for DFX button and modal
|
||||
export default function DfxButton() {
|
||||
const [dfxUrl, setDfxUrl] = useState<string | null>(null);
|
||||
|
||||
const handleOpenDfx = async () => {
|
||||
try {
|
||||
// Get authentication token and URL (this will initialize DFX if needed)
|
||||
const response = await dfxAuthenticate();
|
||||
setDfxUrl(response.kyc_url);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("DFX authentication failed:", error);
|
||||
// TODO: Show error snackbar if needed
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setDfxUrl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="Buy Monero with fiat using DFX" enterDelay={500}>
|
||||
<Chip
|
||||
variant="button"
|
||||
icon={<EuroIcon />}
|
||||
label="Buy Monero"
|
||||
clickable
|
||||
onClick={handleOpenDfx}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Dialog
|
||||
open={dfxUrl != null}
|
||||
onClose={handleCloseModal}
|
||||
maxWidth="lg"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<DFXLogo />
|
||||
<Button onClick={handleCloseModal} variant="outlined">
|
||||
Close
|
||||
</Button>
|
||||
</Box>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0, height: "min(40rem, 80vh)" }}>
|
||||
{dfxUrl && (
|
||||
<iframe
|
||||
src={dfxUrl}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
border: "none",
|
||||
}}
|
||||
title="DFX Swiss"
|
||||
/>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -60,7 +60,6 @@ export default function SendAmountInput({
|
|||
|
||||
const handleMaxAmount = () => {
|
||||
if (disabled) return;
|
||||
|
||||
if (onMaxToggled) {
|
||||
onMaxToggled();
|
||||
} else if (onMaxClicked) {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ export default function SendTransactionContent({
|
|||
|
||||
const handleMaxToggled = () => {
|
||||
if (isSending) return;
|
||||
|
||||
if (isMaxSelected) {
|
||||
// Disable MAX mode - restore previous amount
|
||||
setIsMaxSelected(false);
|
||||
|
|
@ -76,7 +75,6 @@ export default function SendTransactionContent({
|
|||
|
||||
const handleAmountChange = (newAmount: string) => {
|
||||
if (isSending) return;
|
||||
|
||||
if (newAmount !== "<MAX>") {
|
||||
setIsMaxSelected(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import SendTransactionModal from "../SendTransactionModal";
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
|
||||
import SetRestoreHeightModal from "../SetRestoreHeightModal";
|
||||
import DfxButton from "./DFXWidget";
|
||||
|
||||
interface WalletActionButtonsProps {
|
||||
balance: {
|
||||
|
|
@ -32,45 +33,6 @@ interface WalletActionButtonsProps {
|
|||
};
|
||||
}
|
||||
|
||||
function RestoreHeightDialog({
|
||||
open,
|
||||
onClose,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const [restoreHeight, setRestoreHeight] = useState(0);
|
||||
|
||||
const handleRestoreHeight = async () => {
|
||||
await setMoneroRestoreHeight(restoreHeight);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose}>
|
||||
<DialogTitle>Restore Height</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
label="Restore Height"
|
||||
type="number"
|
||||
value={restoreHeight}
|
||||
onChange={(e) => setRestoreHeight(Number(e.target.value))}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose}>Cancel</Button>
|
||||
<PromiseInvokeButton
|
||||
onInvoke={handleRestoreHeight}
|
||||
displayErrorSnackbar={true}
|
||||
variant="contained"
|
||||
>
|
||||
Restore
|
||||
</PromiseInvokeButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WalletActionButtons({
|
||||
balance,
|
||||
}: WalletActionButtonsProps) {
|
||||
|
|
@ -121,6 +83,7 @@ export default function WalletActionButtons({
|
|||
variant="button"
|
||||
clickable
|
||||
/>
|
||||
<DfxButton />
|
||||
|
||||
<IconButton onClick={handleMenuClick}>
|
||||
<MoreHorizIcon />
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
SendMoneroResponse,
|
||||
GetMoneroSyncProgressResponse,
|
||||
GetPendingApprovalsResponse,
|
||||
DfxAuthenticateResponse,
|
||||
RejectApprovalArgs,
|
||||
RejectApprovalResponse,
|
||||
SetRestoreHeightArgs,
|
||||
|
|
@ -621,3 +622,7 @@ export async function saveFilesInDialog(files: Record<string, string>) {
|
|||
files,
|
||||
});
|
||||
}
|
||||
|
||||
export async function dfxAuthenticate(): Promise<DfxAuthenticateResponse> {
|
||||
return await invokeNoArgs<DfxAuthenticateResponse>("dfx_authenticate");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue