From 0ac9276fe760d1586bebc530424659a6e6b017ad Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Wed, 19 Mar 2025 08:48:31 -0500 Subject: [PATCH 1/8] https://github.com/xch-dev/sage/issues/331 https://github.com/xch-dev/sage/issues/140 --- src/hooks/useInitialization.ts | 15 ++++++++++++++- src/pages/Login.tsx | 10 ++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/hooks/useInitialization.ts b/src/hooks/useInitialization.ts index 221a885c..e57c794c 100644 --- a/src/hooks/useInitialization.ts +++ b/src/hooks/useInitialization.ts @@ -1,6 +1,7 @@ import { commands } from '@/bindings'; import { useCallback, useEffect, useState } from 'react'; import { useErrors } from './useErrors'; +import { logoutAndUpdateState } from '@/state'; export default function useInitialization() { const { addError } = useErrors(); @@ -11,7 +12,19 @@ export default function useInitialization() { commands .initialize() .then(() => setInitialized(true)) - .catch(addError); + .catch(async (error) => { + // When there's an error (like DB migration issues), log out and show error + addError(error); + try { + // Log out to ensure we go to login screen + await logoutAndUpdateState(); + } catch (logoutError) { + // Even if logout fails, we still want to proceed + console.error('Error during logout:', logoutError); + } + // Mark as initialized so the app can proceed + setInitialized(true); + }); }, [addError]); useEffect(() => { diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 42598252..f52874e6 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -66,8 +66,14 @@ export default function Login() { useEffect(() => { commands .getKey({}) - .then((data) => data.key !== null && navigate('/wallet')) - .catch(addError); + .then((data) => { + if (data.key !== null) { + navigate('/wallet'); + } + }) + .catch((error) => { + addError(error); + }); }, [navigate, addError]); return ( From 1b342099ca462520d763938193ebe25a549600f8 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Sat, 22 Mar 2025 15:07:26 -0500 Subject: [PATCH 2/8] allow recovery from migration error --- crates/sage-api/src/types/error_kind.rs | 1 + crates/sage-rpc/src/lib.rs | 4 +++- crates/sage/src/error.rs | 2 +- crates/sage/src/sage.rs | 2 +- src/bindings.ts | 2 +- src/hooks/useInitialization.ts | 27 ++++++++++++++++--------- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/crates/sage-api/src/types/error_kind.rs b/crates/sage-api/src/types/error_kind.rs index a44fee9b..86da4651 100644 --- a/crates/sage-api/src/types/error_kind.rs +++ b/crates/sage-api/src/types/error_kind.rs @@ -9,4 +9,5 @@ pub enum ErrorKind { NotFound, Unauthorized, Internal, + DatabaseMigration, } diff --git a/crates/sage-rpc/src/lib.rs b/crates/sage-rpc/src/lib.rs index 9125b71c..ca5a272f 100644 --- a/crates/sage-rpc/src/lib.rs +++ b/crates/sage-rpc/src/lib.rs @@ -45,7 +45,9 @@ where ErrorKind::Api => StatusCode::BAD_REQUEST, ErrorKind::NotFound => StatusCode::NOT_FOUND, ErrorKind::Unauthorized => StatusCode::UNAUTHORIZED, - ErrorKind::Wallet | ErrorKind::Internal => StatusCode::INTERNAL_SERVER_ERROR, + ErrorKind::DatabaseMigration | ErrorKind::Wallet | ErrorKind::Internal => { + StatusCode::INTERNAL_SERVER_ERROR + } }; (status, error.to_string()).into_response() } diff --git a/crates/sage/src/error.rs b/crates/sage/src/error.rs index b07ccb78..a2d48134 100644 --- a/crates/sage/src/error.rs +++ b/crates/sage/src/error.rs @@ -227,11 +227,11 @@ impl Error { | KeychainError::Bip39(..) | KeychainError::Argon2(..) => ErrorKind::Internal, }, + Self::SqlxMigration(..) => ErrorKind::DatabaseMigration, Self::Send(..) | Self::Io(..) | Self::Client(..) | Self::Sqlx(..) - | Self::SqlxMigration(..) | Self::Bip39(..) | Self::TomlDe(..) | Self::TomlSer(..) diff --git a/crates/sage/src/sage.rs b/crates/sage/src/sage.rs index c033a957..a1528da7 100644 --- a/crates/sage/src/sage.rs +++ b/crates/sage/src/sage.rs @@ -68,8 +68,8 @@ impl Sage { self.setup_logging()?; let receiver = self.setup_sync_manager()?; - self.switch_wallet().await?; self.setup_peers().await?; + self.switch_wallet().await?; info!("Sage wallet initialized"); diff --git a/src/bindings.ts b/src/bindings.ts index d622a265..7da3f1a2 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -322,7 +322,7 @@ export type DeleteOfferResponse = Record export type DerivationRecord = { index: number; public_key: string; address: string } export type DidRecord = { launcher_id: string; name: string | null; visible: boolean; coin_id: string; address: string; amount: Amount; recovery_hash: string | null; created_height: number | null; create_transaction_id: string | null } export type Error = { kind: ErrorKind; reason: string } -export type ErrorKind = "wallet" | "api" | "not_found" | "unauthorized" | "internal" +export type ErrorKind = "wallet" | "api" | "not_found" | "unauthorized" | "internal" | "database_migration" export type FilterUnlockedCoins = { coin_ids: string[] } export type FilterUnlockedCoinsResponse = { coin_ids: string[] } export type GenerateMnemonic = { use_24_words: boolean } diff --git a/src/hooks/useInitialization.ts b/src/hooks/useInitialization.ts index e57c794c..ed43550f 100644 --- a/src/hooks/useInitialization.ts +++ b/src/hooks/useInitialization.ts @@ -13,17 +13,26 @@ export default function useInitialization() { .initialize() .then(() => setInitialized(true)) .catch(async (error) => { - // When there's an error (like DB migration issues), log out and show error + // Always add the error to be displayed addError(error); - try { - // Log out to ensure we go to login screen - await logoutAndUpdateState(); - } catch (logoutError) { - // Even if logout fails, we still want to proceed - console.error('Error during logout:', logoutError); + + // Check if this is a database migration error using the specific error kind + if (error.kind === 'database_migration') { + try { + // Only log out for database migration errors + await logoutAndUpdateState(); + console.log('Logged out due to database migration error'); + // Mark as initialized so the app can proceed + setInitialized(true); + } catch (logoutError) { + console.error('Error during logout:', logoutError); + } + } else { + console.error( + 'Initialization error (not a DB migration issue):', + error, + ); } - // Mark as initialized so the app can proceed - setInitialized(true); }); }, [addError]); From 38592e3eb1f232a3e55edbe5bca80a62349033be Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Sat, 22 Mar 2025 15:19:43 -0500 Subject: [PATCH 3/8] show login errors --- src/hooks/useInitialization.ts | 10 +++------- src/pages/Login.tsx | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/hooks/useInitialization.ts b/src/hooks/useInitialization.ts index ed43550f..a87df1c4 100644 --- a/src/hooks/useInitialization.ts +++ b/src/hooks/useInitialization.ts @@ -16,22 +16,18 @@ export default function useInitialization() { // Always add the error to be displayed addError(error); - // Check if this is a database migration error using the specific error kind + // Check if this is a database migration, which is recoverable if (error.kind === 'database_migration') { try { - // Only log out for database migration errors await logoutAndUpdateState(); console.log('Logged out due to database migration error'); - // Mark as initialized so the app can proceed + // Now mark as initialized so the app can proceed setInitialized(true); } catch (logoutError) { console.error('Error during logout:', logoutError); } } else { - console.error( - 'Initialization error (not a DB migration issue):', - error, - ); + console.error('Unrecoverable initialization error', error); } }); }, [addError]); diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index f52874e6..7a48b11e 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -216,13 +216,18 @@ function WalletItem({ network, info, keys, setKeys }: WalletItemProps) { const loginSelf = (explicit: boolean) => { if (isMenuOpen && !explicit) return; - loginAndUpdateState(info.fingerprint).then(() => { - commands - .getKey({}) - .then((data) => setWallet(data.key)) - .then(() => navigate('/wallet')) - .catch(addError); - }); + loginAndUpdateState(info.fingerprint) + .then(() => { + commands + .getKey({}) + .then((data) => setWallet(data.key)) + .then(() => navigate('/wallet')) + .catch(addError); + }) + .catch(async (error) => { + // Always add the error to be displayed + addError(error); + }); }; useEffect(() => { From 80f53bf73594342a74cea4a8d68d26811a4c7db3 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Sat, 22 Mar 2025 15:20:18 -0500 Subject: [PATCH 4/8] log login errors --- src/pages/Login.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 7a48b11e..b059ff57 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -225,8 +225,7 @@ function WalletItem({ network, info, keys, setKeys }: WalletItemProps) { .catch(addError); }) .catch(async (error) => { - // Always add the error to be displayed - addError(error); + console.error('Login error', error); }); }; From 874532c78c91fdd4bbc24a66912b3479d7853f97 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Mon, 24 Mar 2025 16:54:26 -0500 Subject: [PATCH 5/8] take switch_wallet out of sage initialization and make it part of the app init flow --- crates/sage/src/sage.rs | 1 - src-tauri/src/commands.rs | 7 +++++++ src-tauri/src/lib.rs | 1 + src/bindings.ts | 3 +++ src/hooks/useInitialization.ts | 36 ++++++++++++++++------------------ 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/crates/sage/src/sage.rs b/crates/sage/src/sage.rs index a1528da7..a3d76aa3 100644 --- a/crates/sage/src/sage.rs +++ b/crates/sage/src/sage.rs @@ -69,7 +69,6 @@ impl Sage { let receiver = self.setup_sync_manager()?; self.setup_peers().await?; - self.switch_wallet().await?; info!("Sage wallet initialized"); diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 6a8f4bda..21b98e32 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -135,3 +135,10 @@ pub async fn set_rpc_run_on_startup( state.lock().await.save_config()?; Ok(()) } + +#[command] +#[specta] +pub async fn switch_wallet(state: State<'_, AppState>) -> Result<()> { + state.lock().await.switch_wallet().await?; + Ok(()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2ab4101e..367fffa3 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -111,6 +111,7 @@ pub fn run() { commands::stop_rpc_server, commands::get_rpc_run_on_startup, commands::set_rpc_run_on_startup, + commands::switch_wallet, ]) .events(collect_events![SyncEvent]); diff --git a/src/bindings.ts b/src/bindings.ts index 7da3f1a2..c6826df6 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -268,6 +268,9 @@ async getRpcRunOnStartup() : Promise { }, async setRpcRunOnStartup(runOnStartup: boolean) : Promise { return await TAURI_INVOKE("set_rpc_run_on_startup", { runOnStartup }); +}, +async switchWallet() : Promise { + return await TAURI_INVOKE("switch_wallet"); } } diff --git a/src/hooks/useInitialization.ts b/src/hooks/useInitialization.ts index a87df1c4..8a776218 100644 --- a/src/hooks/useInitialization.ts +++ b/src/hooks/useInitialization.ts @@ -9,27 +9,25 @@ export default function useInitialization() { const [initialized, setInitialized] = useState(false); const onInitialize = useCallback(async () => { - commands - .initialize() - .then(() => setInitialized(true)) - .catch(async (error) => { - // Always add the error to be displayed - addError(error); + try { + await commands.initialize(); + setInitialized(true); + await commands.switchWallet(); + } catch (error: any) { + // Always add the error to be displayed + addError(error); - // Check if this is a database migration, which is recoverable - if (error.kind === 'database_migration') { - try { - await logoutAndUpdateState(); - console.log('Logged out due to database migration error'); - // Now mark as initialized so the app can proceed - setInitialized(true); - } catch (logoutError) { - console.error('Error during logout:', logoutError); - } - } else { - console.error('Unrecoverable initialization error', error); + // Check if this is a database migration, which is recoverable + if (error.kind === 'database_migration') { + try { + await logoutAndUpdateState(); + } catch (logoutError) { + console.error('Error during logout:', logoutError); } - }); + } else { + console.error('Unrecoverable initialization error', error); + } + } }, [addError]); useEffect(() => { From 00b6696d51e8a72a0199ca62cdb36d8e97a28a5a Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Wed, 26 Mar 2025 14:42:30 -0500 Subject: [PATCH 6/8] call switch_wallet when starting the rpc server --- crates/sage-cli/src/rpc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/sage-cli/src/rpc.rs b/crates/sage-cli/src/rpc.rs index 843553d5..20dfcdca 100644 --- a/crates/sage-cli/src/rpc.rs +++ b/crates/sage-cli/src/rpc.rs @@ -23,6 +23,7 @@ impl_endpoints! { Self::Start => { let mut sage = Sage::new(&path); let mut receiver = sage.initialize().await?; + sage.switch_wallet().await?; tokio::spawn(async move { while let Some(_message) = receiver.recv().await {} }); start_rpc(Arc::new(Mutex::new(sage))).await }, From 76416c38a2c041434b3a2447413d5a29e437764e Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Tue, 29 Apr 2025 10:26:54 -0500 Subject: [PATCH 7/8] capture errors during login, display the actual error (not the auth error) and abort login process --- src/contexts/ErrorContext.tsx | 2 +- src/pages/Login.tsx | 6 +++--- src/state.ts | 19 ++++++++++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/contexts/ErrorContext.tsx b/src/contexts/ErrorContext.tsx index c0927f87..3c5d0feb 100644 --- a/src/contexts/ErrorContext.tsx +++ b/src/contexts/ErrorContext.tsx @@ -106,7 +106,7 @@ export default function ErrorDialog({ error, setError }: ErrorDialogProps) { {kind ? `${kind} ` : ''}Error - + {error?.reason} diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index d7fbfb06..eebbf959 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -318,7 +318,7 @@ function WalletItem({ const loginSelf = (explicit: boolean) => { if (isMenuOpen && !explicit) return; - loginAndUpdateState(info.fingerprint) + loginAndUpdateState(info.fingerprint, addError) .then(() => { commands .getKey({}) @@ -326,8 +326,8 @@ function WalletItem({ .then(() => navigate('/wallet')) .catch(addError); }) - .catch(async (error) => { - console.error('Login error', error); + .catch((error) => { + addError(error); }); }; diff --git a/src/state.ts b/src/state.ts index 256ce1d9..a3fc80a1 100644 --- a/src/state.ts +++ b/src/state.ts @@ -6,6 +6,7 @@ import { GetSyncStatusResponse, KeyInfo, } from './bindings'; +import { CustomError } from './contexts/ErrorContext'; export interface WalletState { sync: GetSyncStatusResponse; @@ -85,9 +86,21 @@ events.syncEvent.listen((event) => { } }); -export async function loginAndUpdateState(fingerprint: number): Promise { - await commands.login({ fingerprint }); - await fetchState(); +export async function loginAndUpdateState( + fingerprint: number, + onError?: (error: CustomError) => void, +): Promise { + try { + await commands.login({ fingerprint }); + await fetchState(); + } catch (error) { + if (onError) { + onError(error as CustomError); + } else { + console.error(error); + } + throw error; + } } // Create a separate function to handle wallet state updates From d5962ec580ef1022ea170db71ae263c6f2289fa7 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Sun, 11 May 2025 17:01:44 -0500 Subject: [PATCH 8/8] prettier --- src/bindings.ts | 664 ++++++++++++++++++++++++------------------------ 1 file changed, 332 insertions(+), 332 deletions(-) diff --git a/src/bindings.ts b/src/bindings.ts index 01d5a4db..a7443794 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -5,297 +5,297 @@ export const commands = { - async initialize(): Promise { - return await TAURI_INVOKE("initialize"); - }, - async login(req: Login): Promise { - return await TAURI_INVOKE("login", { req }); - }, - async logout(req: Logout): Promise { - return await TAURI_INVOKE("logout", { req }); - }, - async resync(req: Resync): Promise { - return await TAURI_INVOKE("resync", { req }); - }, - async generateMnemonic(req: GenerateMnemonic): Promise { - return await TAURI_INVOKE("generate_mnemonic", { req }); - }, - async importKey(req: ImportKey): Promise { - return await TAURI_INVOKE("import_key", { req }); - }, - async deleteKey(req: DeleteKey): Promise { - return await TAURI_INVOKE("delete_key", { req }); - }, - async renameKey(req: RenameKey): Promise { - return await TAURI_INVOKE("rename_key", { req }); - }, - async getKeys(req: GetKeys): Promise { - return await TAURI_INVOKE("get_keys", { req }); - }, - async getKey(req: GetKey): Promise { - return await TAURI_INVOKE("get_key", { req }); - }, - async getSecretKey(req: GetSecretKey): Promise { - return await TAURI_INVOKE("get_secret_key", { req }); - }, - async sendXch(req: SendXch): Promise { - return await TAURI_INVOKE("send_xch", { req }); - }, - async bulkSendXch(req: BulkSendXch): Promise { - return await TAURI_INVOKE("bulk_send_xch", { req }); - }, - async combineXch(req: CombineXch): Promise { - return await TAURI_INVOKE("combine_xch", { req }); - }, - async autoCombineXch(req: AutoCombineXch): Promise { - return await TAURI_INVOKE("auto_combine_xch", { req }); - }, - async splitXch(req: SplitXch): Promise { - return await TAURI_INVOKE("split_xch", { req }); - }, - async sendCat(req: SendCat): Promise { - return await TAURI_INVOKE("send_cat", { req }); - }, - async bulkSendCat(req: BulkSendCat): Promise { - return await TAURI_INVOKE("bulk_send_cat", { req }); - }, - async combineCat(req: CombineCat): Promise { - return await TAURI_INVOKE("combine_cat", { req }); - }, - async autoCombineCat(req: AutoCombineCat): Promise { - return await TAURI_INVOKE("auto_combine_cat", { req }); - }, - async splitCat(req: SplitCat): Promise { - return await TAURI_INVOKE("split_cat", { req }); - }, - async issueCat(req: IssueCat): Promise { - return await TAURI_INVOKE("issue_cat", { req }); - }, - async createDid(req: CreateDid): Promise { - return await TAURI_INVOKE("create_did", { req }); - }, - async bulkMintNfts(req: BulkMintNfts): Promise { - return await TAURI_INVOKE("bulk_mint_nfts", { req }); - }, - async transferNfts(req: TransferNfts): Promise { - return await TAURI_INVOKE("transfer_nfts", { req }); - }, - async transferDids(req: TransferDids): Promise { - return await TAURI_INVOKE("transfer_dids", { req }); - }, - async normalizeDids(req: NormalizeDids): Promise { - return await TAURI_INVOKE("normalize_dids", { req }); - }, - async addNftUri(req: AddNftUri): Promise { - return await TAURI_INVOKE("add_nft_uri", { req }); - }, - async assignNftsToDid(req: AssignNftsToDid): Promise { - return await TAURI_INVOKE("assign_nfts_to_did", { req }); - }, - async signCoinSpends(req: SignCoinSpends): Promise { - return await TAURI_INVOKE("sign_coin_spends", { req }); - }, - async viewCoinSpends(req: ViewCoinSpends): Promise { - return await TAURI_INVOKE("view_coin_spends", { req }); - }, - async submitTransaction(req: SubmitTransaction): Promise { - return await TAURI_INVOKE("submit_transaction", { req }); - }, - async getSyncStatus(req: GetSyncStatus): Promise { - return await TAURI_INVOKE("get_sync_status", { req }); - }, - async checkAddress(req: CheckAddress): Promise { - return await TAURI_INVOKE("check_address", { req }); - }, - async getDerivations(req: GetDerivations): Promise { - return await TAURI_INVOKE("get_derivations", { req }); - }, - async getAreCoinsSpendable(req: GetAreCoinsSpendable): Promise { - return await TAURI_INVOKE("get_are_coins_spendable", { req }); - }, - async getSpendableCoinCount(req: GetSpendableCoinCount): Promise { - return await TAURI_INVOKE("get_spendable_coin_count", { req }); - }, - async getCoinsByIds(req: GetCoinsByIds): Promise { - return await TAURI_INVOKE("get_coins_by_ids", { req }); - }, - async getXchCoins(req: GetXchCoins): Promise { - return await TAURI_INVOKE("get_xch_coins", { req }); - }, - async getCatCoins(req: GetCatCoins): Promise { - return await TAURI_INVOKE("get_cat_coins", { req }); - }, - async getCats(req: GetCats): Promise { - return await TAURI_INVOKE("get_cats", { req }); - }, - async getCat(req: GetCat): Promise { - return await TAURI_INVOKE("get_cat", { req }); - }, - async getDids(req: GetDids): Promise { - return await TAURI_INVOKE("get_dids", { req }); - }, - async getMinterDidIds(req: GetMinterDidIds): Promise { - return await TAURI_INVOKE("get_minter_did_ids", { req }); - }, - async getNftCollections(req: GetNftCollections): Promise { - return await TAURI_INVOKE("get_nft_collections", { req }); - }, - async getNftCollection(req: GetNftCollection): Promise { - return await TAURI_INVOKE("get_nft_collection", { req }); - }, - async getNfts(req: GetNfts): Promise { - return await TAURI_INVOKE("get_nfts", { req }); - }, - async getNft(req: GetNft): Promise { - return await TAURI_INVOKE("get_nft", { req }); - }, - async getNftData(req: GetNftData): Promise { - return await TAURI_INVOKE("get_nft_data", { req }); - }, - async getNftIcon(req: GetNftIcon): Promise { - return await TAURI_INVOKE("get_nft_icon", { req }); - }, - async getNftThumbnail(req: GetNftThumbnail): Promise { - return await TAURI_INVOKE("get_nft_thumbnail", { req }); - }, - async getPendingTransactions(req: GetPendingTransactions): Promise { - return await TAURI_INVOKE("get_pending_transactions", { req }); - }, - async getTransactions(req: GetTransactions): Promise { - return await TAURI_INVOKE("get_transactions", { req }); - }, - async validateAddress(address: string): Promise { - return await TAURI_INVOKE("validate_address", { address }); - }, - async makeOffer(req: MakeOffer): Promise { - return await TAURI_INVOKE("make_offer", { req }); - }, - async takeOffer(req: TakeOffer): Promise { - return await TAURI_INVOKE("take_offer", { req }); - }, - async combineOffers(req: CombineOffers): Promise { - return await TAURI_INVOKE("combine_offers", { req }); - }, - async viewOffer(req: ViewOffer): Promise { - return await TAURI_INVOKE("view_offer", { req }); - }, - async importOffer(req: ImportOffer): Promise { - return await TAURI_INVOKE("import_offer", { req }); - }, - async getOffers(req: GetOffers): Promise { - return await TAURI_INVOKE("get_offers", { req }); - }, - async getOffer(req: GetOffer): Promise { - return await TAURI_INVOKE("get_offer", { req }); - }, - async deleteOffer(req: DeleteOffer): Promise { - return await TAURI_INVOKE("delete_offer", { req }); - }, - async cancelOffer(req: CancelOffer): Promise { - return await TAURI_INVOKE("cancel_offer", { req }); - }, - async networkConfig(): Promise { - return await TAURI_INVOKE("network_config"); - }, - async setDiscoverPeers(req: SetDiscoverPeers): Promise { - return await TAURI_INVOKE("set_discover_peers", { req }); - }, - async setTargetPeers(req: SetTargetPeers): Promise { - return await TAURI_INVOKE("set_target_peers", { req }); - }, - async setNetwork(req: SetNetwork): Promise { - return await TAURI_INVOKE("set_network", { req }); - }, - async setNetworkOverride(req: SetNetworkOverride): Promise { - return await TAURI_INVOKE("set_network_override", { req }); - }, - async walletConfig(fingerprint: number): Promise { - return await TAURI_INVOKE("wallet_config", { fingerprint }); - }, - async getNetworks(req: GetNetworks): Promise { - return await TAURI_INVOKE("get_networks", { req }); - }, - async getNetwork(req: GetNetwork): Promise { - return await TAURI_INVOKE("get_network", { req }); - }, - async updateCat(req: UpdateCat): Promise { - return await TAURI_INVOKE("update_cat", { req }); - }, - async removeCat(req: RemoveCat): Promise { - return await TAURI_INVOKE("remove_cat", { req }); - }, - async updateDid(req: UpdateDid): Promise { - return await TAURI_INVOKE("update_did", { req }); - }, - async updateNft(req: UpdateNft): Promise { - return await TAURI_INVOKE("update_nft", { req }); - }, - async updateNftCollection(req: UpdateNftCollection): Promise { - return await TAURI_INVOKE("update_nft_collection", { req }); - }, - async redownloadNft(req: RedownloadNft): Promise { - return await TAURI_INVOKE("redownload_nft", { req }); - }, - async increaseDerivationIndex(req: IncreaseDerivationIndex): Promise { - return await TAURI_INVOKE("increase_derivation_index", { req }); - }, - async getPeers(req: GetPeers): Promise { - return await TAURI_INVOKE("get_peers", { req }); - }, - async addPeer(req: AddPeer): Promise { - return await TAURI_INVOKE("add_peer", { req }); - }, - async removePeer(req: RemovePeer): Promise { - return await TAURI_INVOKE("remove_peer", { req }); - }, - async filterUnlockedCoins(req: FilterUnlockedCoins): Promise { - return await TAURI_INVOKE("filter_unlocked_coins", { req }); - }, - async getAssetCoins(req: GetAssetCoins): Promise { - return await TAURI_INVOKE("get_asset_coins", { req }); - }, - async signMessageWithPublicKey(req: SignMessageWithPublicKey): Promise { - return await TAURI_INVOKE("sign_message_with_public_key", { req }); - }, - async signMessageByAddress(req: SignMessageByAddress): Promise { - return await TAURI_INVOKE("sign_message_by_address", { req }); - }, - async sendTransactionImmediately(req: SendTransactionImmediately): Promise { - return await TAURI_INVOKE("send_transaction_immediately", { req }); - }, - async isRpcRunning(): Promise { - return await TAURI_INVOKE("is_rpc_running"); - }, - async startRpcServer(): Promise { - return await TAURI_INVOKE("start_rpc_server"); - }, - async stopRpcServer(): Promise { - return await TAURI_INVOKE("stop_rpc_server"); - }, - async getRpcRunOnStartup(): Promise { - return await TAURI_INVOKE("get_rpc_run_on_startup"); - }, - async setRpcRunOnStartup(runOnStartup: boolean): Promise { - return await TAURI_INVOKE("set_rpc_run_on_startup", { runOnStartup }); - }, - async switchWallet(): Promise { - return await TAURI_INVOKE("switch_wallet"); - }, - async moveKey(fingerprint: number, index: number): Promise { - return await TAURI_INVOKE("move_key", { fingerprint, index }); - }, - async downloadCniOffercode(code: string): Promise { - return await TAURI_INVOKE("download_cni_offercode", { code }); - } +async initialize() : Promise { + return await TAURI_INVOKE("initialize"); +}, +async login(req: Login) : Promise { + return await TAURI_INVOKE("login", { req }); +}, +async logout(req: Logout) : Promise { + return await TAURI_INVOKE("logout", { req }); +}, +async resync(req: Resync) : Promise { + return await TAURI_INVOKE("resync", { req }); +}, +async generateMnemonic(req: GenerateMnemonic) : Promise { + return await TAURI_INVOKE("generate_mnemonic", { req }); +}, +async importKey(req: ImportKey) : Promise { + return await TAURI_INVOKE("import_key", { req }); +}, +async deleteKey(req: DeleteKey) : Promise { + return await TAURI_INVOKE("delete_key", { req }); +}, +async renameKey(req: RenameKey) : Promise { + return await TAURI_INVOKE("rename_key", { req }); +}, +async getKeys(req: GetKeys) : Promise { + return await TAURI_INVOKE("get_keys", { req }); +}, +async getKey(req: GetKey) : Promise { + return await TAURI_INVOKE("get_key", { req }); +}, +async getSecretKey(req: GetSecretKey) : Promise { + return await TAURI_INVOKE("get_secret_key", { req }); +}, +async sendXch(req: SendXch) : Promise { + return await TAURI_INVOKE("send_xch", { req }); +}, +async bulkSendXch(req: BulkSendXch) : Promise { + return await TAURI_INVOKE("bulk_send_xch", { req }); +}, +async combineXch(req: CombineXch) : Promise { + return await TAURI_INVOKE("combine_xch", { req }); +}, +async autoCombineXch(req: AutoCombineXch) : Promise { + return await TAURI_INVOKE("auto_combine_xch", { req }); +}, +async splitXch(req: SplitXch) : Promise { + return await TAURI_INVOKE("split_xch", { req }); +}, +async sendCat(req: SendCat) : Promise { + return await TAURI_INVOKE("send_cat", { req }); +}, +async bulkSendCat(req: BulkSendCat) : Promise { + return await TAURI_INVOKE("bulk_send_cat", { req }); +}, +async combineCat(req: CombineCat) : Promise { + return await TAURI_INVOKE("combine_cat", { req }); +}, +async autoCombineCat(req: AutoCombineCat) : Promise { + return await TAURI_INVOKE("auto_combine_cat", { req }); +}, +async splitCat(req: SplitCat) : Promise { + return await TAURI_INVOKE("split_cat", { req }); +}, +async issueCat(req: IssueCat) : Promise { + return await TAURI_INVOKE("issue_cat", { req }); +}, +async createDid(req: CreateDid) : Promise { + return await TAURI_INVOKE("create_did", { req }); +}, +async bulkMintNfts(req: BulkMintNfts) : Promise { + return await TAURI_INVOKE("bulk_mint_nfts", { req }); +}, +async transferNfts(req: TransferNfts) : Promise { + return await TAURI_INVOKE("transfer_nfts", { req }); +}, +async transferDids(req: TransferDids) : Promise { + return await TAURI_INVOKE("transfer_dids", { req }); +}, +async normalizeDids(req: NormalizeDids) : Promise { + return await TAURI_INVOKE("normalize_dids", { req }); +}, +async addNftUri(req: AddNftUri) : Promise { + return await TAURI_INVOKE("add_nft_uri", { req }); +}, +async assignNftsToDid(req: AssignNftsToDid) : Promise { + return await TAURI_INVOKE("assign_nfts_to_did", { req }); +}, +async signCoinSpends(req: SignCoinSpends) : Promise { + return await TAURI_INVOKE("sign_coin_spends", { req }); +}, +async viewCoinSpends(req: ViewCoinSpends) : Promise { + return await TAURI_INVOKE("view_coin_spends", { req }); +}, +async submitTransaction(req: SubmitTransaction) : Promise { + return await TAURI_INVOKE("submit_transaction", { req }); +}, +async getSyncStatus(req: GetSyncStatus) : Promise { + return await TAURI_INVOKE("get_sync_status", { req }); +}, +async checkAddress(req: CheckAddress) : Promise { + return await TAURI_INVOKE("check_address", { req }); +}, +async getDerivations(req: GetDerivations) : Promise { + return await TAURI_INVOKE("get_derivations", { req }); +}, +async getAreCoinsSpendable(req: GetAreCoinsSpendable) : Promise { + return await TAURI_INVOKE("get_are_coins_spendable", { req }); +}, +async getSpendableCoinCount(req: GetSpendableCoinCount) : Promise { + return await TAURI_INVOKE("get_spendable_coin_count", { req }); +}, +async getCoinsByIds(req: GetCoinsByIds) : Promise { + return await TAURI_INVOKE("get_coins_by_ids", { req }); +}, +async getXchCoins(req: GetXchCoins) : Promise { + return await TAURI_INVOKE("get_xch_coins", { req }); +}, +async getCatCoins(req: GetCatCoins) : Promise { + return await TAURI_INVOKE("get_cat_coins", { req }); +}, +async getCats(req: GetCats) : Promise { + return await TAURI_INVOKE("get_cats", { req }); +}, +async getCat(req: GetCat) : Promise { + return await TAURI_INVOKE("get_cat", { req }); +}, +async getDids(req: GetDids) : Promise { + return await TAURI_INVOKE("get_dids", { req }); +}, +async getMinterDidIds(req: GetMinterDidIds) : Promise { + return await TAURI_INVOKE("get_minter_did_ids", { req }); +}, +async getNftCollections(req: GetNftCollections) : Promise { + return await TAURI_INVOKE("get_nft_collections", { req }); +}, +async getNftCollection(req: GetNftCollection) : Promise { + return await TAURI_INVOKE("get_nft_collection", { req }); +}, +async getNfts(req: GetNfts) : Promise { + return await TAURI_INVOKE("get_nfts", { req }); +}, +async getNft(req: GetNft) : Promise { + return await TAURI_INVOKE("get_nft", { req }); +}, +async getNftData(req: GetNftData) : Promise { + return await TAURI_INVOKE("get_nft_data", { req }); +}, +async getNftIcon(req: GetNftIcon) : Promise { + return await TAURI_INVOKE("get_nft_icon", { req }); +}, +async getNftThumbnail(req: GetNftThumbnail) : Promise { + return await TAURI_INVOKE("get_nft_thumbnail", { req }); +}, +async getPendingTransactions(req: GetPendingTransactions) : Promise { + return await TAURI_INVOKE("get_pending_transactions", { req }); +}, +async getTransactions(req: GetTransactions) : Promise { + return await TAURI_INVOKE("get_transactions", { req }); +}, +async validateAddress(address: string) : Promise { + return await TAURI_INVOKE("validate_address", { address }); +}, +async makeOffer(req: MakeOffer) : Promise { + return await TAURI_INVOKE("make_offer", { req }); +}, +async takeOffer(req: TakeOffer) : Promise { + return await TAURI_INVOKE("take_offer", { req }); +}, +async combineOffers(req: CombineOffers) : Promise { + return await TAURI_INVOKE("combine_offers", { req }); +}, +async viewOffer(req: ViewOffer) : Promise { + return await TAURI_INVOKE("view_offer", { req }); +}, +async importOffer(req: ImportOffer) : Promise { + return await TAURI_INVOKE("import_offer", { req }); +}, +async getOffers(req: GetOffers) : Promise { + return await TAURI_INVOKE("get_offers", { req }); +}, +async getOffer(req: GetOffer) : Promise { + return await TAURI_INVOKE("get_offer", { req }); +}, +async deleteOffer(req: DeleteOffer) : Promise { + return await TAURI_INVOKE("delete_offer", { req }); +}, +async cancelOffer(req: CancelOffer) : Promise { + return await TAURI_INVOKE("cancel_offer", { req }); +}, +async networkConfig() : Promise { + return await TAURI_INVOKE("network_config"); +}, +async setDiscoverPeers(req: SetDiscoverPeers) : Promise { + return await TAURI_INVOKE("set_discover_peers", { req }); +}, +async setTargetPeers(req: SetTargetPeers) : Promise { + return await TAURI_INVOKE("set_target_peers", { req }); +}, +async setNetwork(req: SetNetwork) : Promise { + return await TAURI_INVOKE("set_network", { req }); +}, +async setNetworkOverride(req: SetNetworkOverride) : Promise { + return await TAURI_INVOKE("set_network_override", { req }); +}, +async walletConfig(fingerprint: number) : Promise { + return await TAURI_INVOKE("wallet_config", { fingerprint }); +}, +async getNetworks(req: GetNetworks) : Promise { + return await TAURI_INVOKE("get_networks", { req }); +}, +async getNetwork(req: GetNetwork) : Promise { + return await TAURI_INVOKE("get_network", { req }); +}, +async updateCat(req: UpdateCat) : Promise { + return await TAURI_INVOKE("update_cat", { req }); +}, +async removeCat(req: RemoveCat) : Promise { + return await TAURI_INVOKE("remove_cat", { req }); +}, +async updateDid(req: UpdateDid) : Promise { + return await TAURI_INVOKE("update_did", { req }); +}, +async updateNft(req: UpdateNft) : Promise { + return await TAURI_INVOKE("update_nft", { req }); +}, +async updateNftCollection(req: UpdateNftCollection) : Promise { + return await TAURI_INVOKE("update_nft_collection", { req }); +}, +async redownloadNft(req: RedownloadNft) : Promise { + return await TAURI_INVOKE("redownload_nft", { req }); +}, +async increaseDerivationIndex(req: IncreaseDerivationIndex) : Promise { + return await TAURI_INVOKE("increase_derivation_index", { req }); +}, +async getPeers(req: GetPeers) : Promise { + return await TAURI_INVOKE("get_peers", { req }); +}, +async addPeer(req: AddPeer) : Promise { + return await TAURI_INVOKE("add_peer", { req }); +}, +async removePeer(req: RemovePeer) : Promise { + return await TAURI_INVOKE("remove_peer", { req }); +}, +async filterUnlockedCoins(req: FilterUnlockedCoins) : Promise { + return await TAURI_INVOKE("filter_unlocked_coins", { req }); +}, +async getAssetCoins(req: GetAssetCoins) : Promise { + return await TAURI_INVOKE("get_asset_coins", { req }); +}, +async signMessageWithPublicKey(req: SignMessageWithPublicKey) : Promise { + return await TAURI_INVOKE("sign_message_with_public_key", { req }); +}, +async signMessageByAddress(req: SignMessageByAddress) : Promise { + return await TAURI_INVOKE("sign_message_by_address", { req }); +}, +async sendTransactionImmediately(req: SendTransactionImmediately) : Promise { + return await TAURI_INVOKE("send_transaction_immediately", { req }); +}, +async isRpcRunning() : Promise { + return await TAURI_INVOKE("is_rpc_running"); +}, +async startRpcServer() : Promise { + return await TAURI_INVOKE("start_rpc_server"); +}, +async stopRpcServer() : Promise { + return await TAURI_INVOKE("stop_rpc_server"); +}, +async getRpcRunOnStartup() : Promise { + return await TAURI_INVOKE("get_rpc_run_on_startup"); +}, +async setRpcRunOnStartup(runOnStartup: boolean) : Promise { + return await TAURI_INVOKE("set_rpc_run_on_startup", { runOnStartup }); +}, +async switchWallet() : Promise { + return await TAURI_INVOKE("switch_wallet"); +}, +async moveKey(fingerprint: number, index: number) : Promise { + return await TAURI_INVOKE("move_key", { fingerprint, index }); +}, +async downloadCniOffercode(code: string) : Promise { + return await TAURI_INVOKE("download_cni_offercode", { code }); +} } /** user-defined events **/ export const events = __makeEvents__<{ - syncEvent: SyncEvent +syncEvent: SyncEvent }>({ - syncEvent: "sync-event" +syncEvent: "sync-event" }) /** user-defined constants **/ @@ -323,13 +323,13 @@ export type BulkSendXch = { addresses: string[]; amount: Amount; fee: Amount; me export type CancelOffer = { offer_id: string; fee: Amount; auto_submit?: boolean } export type CatAmount = { asset_id: string; amount: Amount } export type CatRecord = { asset_id: string; name: string | null; ticker: string | null; description: string | null; icon_url: string | null; visible: boolean; balance: Amount } -export type ChangeMode = { mode: "default" } | +export type ChangeMode = { mode: "default" } | /** * Reuse the first address of coins involved in the transaction * as the change address for the output. This improves compatibility * with wallets which do not support multiple addresses. */ -{ mode: "same" } | +{ mode: "same" } | /** * Use an address that has not been used before as the change address * for the output. This is beneficial for privacy, but results in more @@ -353,12 +353,12 @@ export type DeleteKey = { fingerprint: number } export type DeleteKeyResponse = Record export type DeleteOffer = { offer_id: string } export type DeleteOfferResponse = Record -export type DerivationMode = { mode: "default" } | +export type DerivationMode = { mode: "default" } | /** * Automatically generate new addresses if there aren't enough that * haven't been used yet. */ -{ mode: "auto"; derivation_batch_size: number } | +{ mode: "auto"; derivation_batch_size: number } | /** * Don't generate any new addresses, only use existing ones. */ @@ -430,7 +430,7 @@ export type GetXchCoinsResponse = { coins: CoinRecord[]; total: number } export type ImportKey = { name: string; key: string; derivation_index?: number; save_secrets?: boolean; login?: boolean } export type ImportKeyResponse = { fingerprint: number } export type ImportOffer = { offer: string } -export type ImportOfferResponse = Record +export type ImportOfferResponse = { offer_id: string } export type IncreaseDerivationIndex = { hardened?: boolean | null; index: number } export type IncreaseDerivationIndexResponse = Record export type InheritedNetwork = "mainnet" | "testnet11" @@ -529,59 +529,59 @@ export type Wallet = { name?: string; fingerprint: number; change: ChangeMode; d /** tauri-specta globals **/ import { - invoke as TAURI_INVOKE, - Channel as TAURI_CHANNEL, + invoke as TAURI_INVOKE, + Channel as TAURI_CHANNEL, } from "@tauri-apps/api/core"; import * as TAURI_API_EVENT from "@tauri-apps/api/event"; import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow"; type __EventObj__ = { - listen: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - once: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - emit: null extends T - ? (payload?: T) => ReturnType - : (payload: T) => ReturnType; + listen: ( + cb: TAURI_API_EVENT.EventCallback, + ) => ReturnType>; + once: ( + cb: TAURI_API_EVENT.EventCallback, + ) => ReturnType>; + emit: null extends T + ? (payload?: T) => ReturnType + : (payload: T) => ReturnType; }; export type Result = - | { status: "ok"; data: T } - | { status: "error"; error: E }; + | { status: "ok"; data: T } + | { status: "error"; error: E }; function __makeEvents__>( - mappings: Record, + mappings: Record, ) { - return new Proxy( - {} as unknown as { - [K in keyof T]: __EventObj__ & { - (handle: __WebviewWindow__): __EventObj__; - }; - }, - { - get: (_, event) => { - const name = mappings[event as keyof T]; + return new Proxy( + {} as unknown as { + [K in keyof T]: __EventObj__ & { + (handle: __WebviewWindow__): __EventObj__; + }; + }, + { + get: (_, event) => { + const name = mappings[event as keyof T]; - return new Proxy((() => { }) as any, { - apply: (_, __, [window]: [__WebviewWindow__]) => ({ - listen: (arg: any) => window.listen(name, arg), - once: (arg: any) => window.once(name, arg), - emit: (arg: any) => window.emit(name, arg), - }), - get: (_, command: keyof __EventObj__) => { - switch (command) { - case "listen": - return (arg: any) => TAURI_API_EVENT.listen(name, arg); - case "once": - return (arg: any) => TAURI_API_EVENT.once(name, arg); - case "emit": - return (arg: any) => TAURI_API_EVENT.emit(name, arg); - } - }, - }); - }, - }, - ); + return new Proxy((() => {}) as any, { + apply: (_, __, [window]: [__WebviewWindow__]) => ({ + listen: (arg: any) => window.listen(name, arg), + once: (arg: any) => window.once(name, arg), + emit: (arg: any) => window.emit(name, arg), + }), + get: (_, command: keyof __EventObj__) => { + switch (command) { + case "listen": + return (arg: any) => TAURI_API_EVENT.listen(name, arg); + case "once": + return (arg: any) => TAURI_API_EVENT.once(name, arg); + case "emit": + return (arg: any) => TAURI_API_EVENT.emit(name, arg); + } + }, + }); + }, + }, + ); }