mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-22 03:48:01 -05:00
feat(gui): Monero wallet (#442)
* 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
* 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
* refactor(gui): Remove isLoading from wallet slice
* feat(gui): Add success dialog after send transaction was approved
* fix(gui): Floor piconero amount in sendMoneroTransaction
* feat(gui): Allow view on explorer button on send success modal
* feat(backend): save the wallet state on events
* fix(structure): move throttle into its own crate
* fix(log): remove spammy logs
* fix(logs): log folder in confid
* remove "sync progress: " log
* small refactors
* save wallet at most every 60s
* remove useless logs
* underscore unused variables
* feat(gui): Add timestamp of the tx
* feat(gui): Add the legacy wallet init option
* legac ybutton
* Fix(gui, asb): reverse the log config
remove log in bridge.h
cleanup
* use none for .store(..)
* display dot for running swap
---------
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
eb0dc10489
commit
a7823d7489
118 changed files with 7857 additions and 3456 deletions
|
|
@ -31,16 +31,31 @@ import {
|
|||
RedactResponse,
|
||||
GetCurrentSwapResponse,
|
||||
LabeledMoneroAddress,
|
||||
GetPendingApprovalsArgs,
|
||||
GetMoneroHistoryResponse,
|
||||
GetMoneroMainAddressResponse,
|
||||
GetMoneroBalanceResponse,
|
||||
SendMoneroArgs,
|
||||
SendMoneroResponse,
|
||||
GetMoneroSyncProgressResponse,
|
||||
GetPendingApprovalsResponse,
|
||||
RejectApprovalArgs,
|
||||
RejectApprovalResponse,
|
||||
SetRestoreHeightArgs,
|
||||
SetRestoreHeightResponse,
|
||||
GetRestoreHeightResponse,
|
||||
} from "models/tauriModel";
|
||||
import {
|
||||
rpcSetBalance,
|
||||
rpcSetSwapInfo,
|
||||
approvalRequestsReplaced,
|
||||
} from "store/features/rpcSlice";
|
||||
import {
|
||||
setMainAddress,
|
||||
setBalance,
|
||||
setSyncProgress,
|
||||
setHistory,
|
||||
} from "store/features/walletSlice";
|
||||
import { store } from "./store/storeRenderer";
|
||||
import { Maker } from "models/apiModel";
|
||||
import { providerToConcatenatedMultiAddr } from "utils/multiAddrUtils";
|
||||
import { MoneroRecoveryResponse } from "models/rpcModel";
|
||||
import { ListSellersResponse } from "../models/tauriModel";
|
||||
|
|
@ -417,6 +432,129 @@ export async function getMoneroAddresses(): Promise<GetMoneroAddressesResponse>
|
|||
return await invokeNoArgs<GetMoneroAddressesResponse>("get_monero_addresses");
|
||||
}
|
||||
|
||||
export async function getRestoreHeight(): Promise<GetRestoreHeightResponse> {
|
||||
return await invokeNoArgs<GetRestoreHeightResponse>("get_restore_height");
|
||||
}
|
||||
|
||||
export async function setMoneroRestoreHeight(
|
||||
height: number | Date,
|
||||
): Promise<SetRestoreHeightResponse> {
|
||||
const args: SetRestoreHeightArgs =
|
||||
typeof height === "number"
|
||||
? { type: "Height", height: height }
|
||||
: {
|
||||
type: "Date",
|
||||
height: {
|
||||
year: height.getFullYear(),
|
||||
month: height.getMonth() + 1, // JavaScript months are 0-indexed, but we want 1-indexed
|
||||
day: height.getDate(),
|
||||
},
|
||||
};
|
||||
|
||||
return await invoke<SetRestoreHeightArgs, SetRestoreHeightResponse>(
|
||||
"set_monero_restore_height",
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMoneroHistory(): Promise<GetMoneroHistoryResponse> {
|
||||
return await invokeNoArgs<GetMoneroHistoryResponse>("get_monero_history");
|
||||
}
|
||||
|
||||
export async function getMoneroMainAddress(): Promise<GetMoneroMainAddressResponse> {
|
||||
return await invokeNoArgs<GetMoneroMainAddressResponse>(
|
||||
"get_monero_main_address",
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMoneroBalance(): Promise<GetMoneroBalanceResponse> {
|
||||
return await invokeNoArgs<GetMoneroBalanceResponse>("get_monero_balance");
|
||||
}
|
||||
|
||||
export async function sendMonero(
|
||||
args: SendMoneroArgs,
|
||||
): Promise<SendMoneroResponse> {
|
||||
return await invoke<SendMoneroArgs, SendMoneroResponse>("send_monero", args);
|
||||
}
|
||||
|
||||
export async function getMoneroSyncProgress(): Promise<GetMoneroSyncProgressResponse> {
|
||||
return await invokeNoArgs<GetMoneroSyncProgressResponse>(
|
||||
"get_monero_sync_progress",
|
||||
);
|
||||
}
|
||||
|
||||
// Wallet management functions that handle Redux dispatching
|
||||
export async function initializeMoneroWallet() {
|
||||
try {
|
||||
const [
|
||||
addressResponse,
|
||||
balanceResponse,
|
||||
syncProgressResponse,
|
||||
historyResponse,
|
||||
] = await Promise.all([
|
||||
getMoneroMainAddress(),
|
||||
getMoneroBalance(),
|
||||
getMoneroSyncProgress(),
|
||||
getMoneroHistory(),
|
||||
]);
|
||||
|
||||
store.dispatch(setMainAddress(addressResponse.address));
|
||||
store.dispatch(setBalance(balanceResponse));
|
||||
store.dispatch(setSyncProgress(syncProgressResponse));
|
||||
store.dispatch(setHistory(historyResponse));
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch Monero wallet data:", err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendMoneroTransaction(
|
||||
args: SendMoneroArgs,
|
||||
): Promise<SendMoneroResponse> {
|
||||
try {
|
||||
const response = await sendMonero(args);
|
||||
|
||||
// Refresh balance and history after sending - but don't let this block the response
|
||||
Promise.all([
|
||||
getMoneroBalance(),
|
||||
getMoneroHistory(),
|
||||
]).then(([newBalance, newHistory]) => {
|
||||
store.dispatch(setBalance(newBalance));
|
||||
store.dispatch(setHistory(newHistory));
|
||||
}).catch(refreshErr => {
|
||||
console.error("Failed to refresh wallet data after send:", refreshErr);
|
||||
// Could emit a toast notification here
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
console.error("Failed to send Monero:", err);
|
||||
throw err; // ✅ Re-throw so caller can handle appropriately
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshWalletDataAfterTransaction() {
|
||||
try {
|
||||
const [newBalance, newHistory] = await Promise.all([
|
||||
getMoneroBalance(),
|
||||
getMoneroHistory(),
|
||||
]);
|
||||
store.dispatch(setBalance(newBalance));
|
||||
store.dispatch(setHistory(newHistory));
|
||||
} catch (err) {
|
||||
console.error("Failed to refresh wallet data after transaction:", err);
|
||||
// Maybe show a non-blocking notification to user
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateMoneroSyncProgress() {
|
||||
try {
|
||||
const response = await getMoneroSyncProgress();
|
||||
store.dispatch(setSyncProgress(response));
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch sync progress:", err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDataDir(): Promise<string> {
|
||||
const testnet = isTestnet();
|
||||
return await invoke<GetDataDirArgs, string>("get_data_dir", {
|
||||
|
|
@ -424,22 +562,37 @@ export async function getDataDir(): Promise<string> {
|
|||
});
|
||||
}
|
||||
|
||||
export async function resolveApproval(
|
||||
export async function resolveApproval<T>(
|
||||
requestId: string,
|
||||
accept: object,
|
||||
accept: T,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await invoke<ResolveApprovalArgs, ResolveApprovalResponse>(
|
||||
"resolve_approval_request",
|
||||
{ request_id: requestId, accept },
|
||||
{ request_id: requestId, accept: accept as object },
|
||||
);
|
||||
} catch (error) {
|
||||
// Refresh approval list when resolve fails to keep UI in sync
|
||||
} finally {
|
||||
// Always refresh the approval list
|
||||
await refreshApprovals();
|
||||
throw error;
|
||||
|
||||
// Refresh the approval list a few miliseconds later to again
|
||||
// Just to make sure :)
|
||||
setTimeout(() => {
|
||||
refreshApprovals();
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
export async function rejectApproval<T>(
|
||||
requestId: string,
|
||||
reject: T,
|
||||
): Promise<void> {
|
||||
await invoke<RejectApprovalArgs, RejectApprovalResponse>(
|
||||
"reject_approval_request",
|
||||
{ request_id: requestId },
|
||||
);
|
||||
}
|
||||
|
||||
export async function refreshApprovals(): Promise<void> {
|
||||
const response = await invokeNoArgs<GetPendingApprovalsResponse>(
|
||||
"get_pending_approvals",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue