import initMutinyWallet, { ActivityItem, BudgetPeriod, ChannelClosure, FederationBalance, FederationBalances, FedimintSweepResult, LnUrlParams, MutinyBalance, MutinyBip21RawMaterials, MutinyChannel, MutinyInvoice, MutinyPeer, MutinyWallet, NwcProfile, PaymentParams, PendingNwcInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; import { IActivityItem } from "~/components"; import { MutinyWalletSettingStrings } from "~/logic/mutinyWalletSetup"; import { FakeDirectMessage, OnChainTx } from "~/routes"; import { DiscoveredFederation, MutinyFederationIdentity } from "~/routes/settings"; // For some reason {...invoice } doesn't bring across the paid field function destructureInvoice(invoice: MutinyInvoice): MutinyInvoice { return { amount_sats: invoice.amount_sats, bolt11: invoice.bolt11, description: invoice.description, expire: invoice.expire, expired: invoice.expired, fees_paid: invoice.fees_paid, inbound: invoice.inbound, labels: invoice.labels, last_updated: invoice.last_updated, paid: invoice.paid, payee_pubkey: invoice.payee_pubkey, payment_hash: invoice.payment_hash, potential_hodl_invoice: invoice.potential_hodl_invoice, preimage: invoice.preimage, privacy_level: invoice.privacy_level, status: invoice.status } as MutinyInvoice; } let wallet: MutinyWallet | undefined; export let wasm_initialized = false; export let wallet_initialized = false; export async function checkForWasm() { try { if ( typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function" ) { const module = new WebAssembly.Module( Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00) ); if (!(module instanceof WebAssembly.Module)) { throw new Error("Couldn't instantiate WASM Module"); } } else { throw new Error("No WebAssembly global object found"); } } catch (e) { console.error(e); } } export async function initializeWasm() { // Actually intialize the WASM, this should be the first thing that requires the WASM blob to be downloaded // If WASM is already initialized, don't init twice try { const _sats_the_standard = MutinyWallet.convert_btc_to_sats(1); console.debug("MutinyWallet WASM already initialized, skipping init"); wasm_initialized = true; return; } catch (e) { console.debug("MutinyWallet WASM about to be initialized"); await initMutinyWallet(); console.debug("MutinyWallet WASM initialized"); } } export async function setupMutinyWallet( settings: MutinyWalletSettingStrings, password?: string, safeMode?: boolean, shouldZapHodl?: boolean, nsec?: string ): Promise { console.log("Starting setup..."); // https://developer.mozilla.org/en-US/docs/Web/API/Storage_API // Ask the browser to not clear storage if (navigator.storage && navigator.storage.persist) { navigator.storage.persist().then((persistent) => { if (persistent) { console.log( "Storage will not be cleared except by explicit user action" ); } else { console.log( "Storage may be cleared by the UA under storage pressure." ); } }); } const { network, proxy, esplora, rgs, lsp, lsps_connection_string, lsps_token, auth, subscriptions, storage, scorer, primal_api, blind_auth, hermes } = settings; console.log("Initializing Mutiny Manager"); console.log("Using network", network); console.log("Using proxy", proxy); console.log("Using esplora address", esplora); console.log("Using rgs address", rgs); console.log("Using lsp address", lsp); console.log("Using lsp connection string", lsps_connection_string); console.log("Using lsp token", lsps_token); console.log("Using auth address", auth); console.log("Using subscriptions address", subscriptions); console.log("Using storage address", storage); console.log("Using scorer address", scorer); console.log("Using primal api", primal_api); console.log("Using blind auth", blind_auth); console.log("Using hermes", hermes); console.log(safeMode ? "Safe mode enabled" : "Safe mode disabled"); console.log(shouldZapHodl ? "Hodl zaps enabled" : "Hodl zaps disabled"); // Only use lsps if there's no lsp set const shouldUseLSPS = !lsp && lsps_connection_string && lsps_token; const mutinyWallet = await new MutinyWallet( // Password password ? password : undefined, // Mnemonic undefined, proxy, network, esplora, rgs, shouldUseLSPS ? undefined : lsp, shouldUseLSPS ? lsps_connection_string : undefined, shouldUseLSPS ? lsps_token : undefined, auth, subscriptions, storage, scorer, // Do not connect peers undefined, // Do not skip device lock undefined, // Safe mode safeMode || undefined, // Skip hodl invoices? (defaults to true, so if shouldZapHodl is true that's when we pass false) shouldZapHodl ? false : undefined, // Nsec override nsec, // Nip7 (not supported in web worker) undefined, // primal URL primal_api || "https://primal-cache.mutinywallet.com/api", /// blind auth url blind_auth, /// hermes url hermes ); wallet = mutinyWallet; wallet_initialized = true; return true; } /** * Gets the current balance of the wallet. * This includes both on-chain and lightning funds. * * This will not include any funds in an unconfirmed lightning channel. * @returns {Promise} */ export async function get_balance(): Promise { const balance = await wallet!.get_balance(); return { federation: balance.federation, lightning: balance.lightning, confirmed: balance.confirmed, unconfirmed: balance.unconfirmed, force_close: balance.force_close } as MutinyBalance; } /** * Lists the federation id's of the federation clients in the manager. * @returns {Promise} */ export async function list_federations(): Promise { const federations = await wallet!.list_federations(); console.log("list_federations", federations); return federations as MutinyFederationIdentity[]; } /** * Checks whether or not the user is subscribed to Mutiny+. * Submits a NWC string to keep the subscription active if not expired. * * Returns None if there's no subscription at all. * Returns Some(u64) for their unix expiration timestamp, which may be in the * past or in the future, depending on whether or not it is currently active. * @returns {Promise} */ export async function check_subscribed(): Promise { const subscribed = await wallet!.check_subscribed(); return subscribed; } /** * Stops all of the nodes and background processes. * Returns after node has been stopped. * @returns {Promise} */ export async function stop(): Promise { await wallet!.stop(); } /** * Clears storage and deletes all data. * * All data in VSS persists but the device lock is cleared. * @returns {Promise} */ export async function delete_all(): Promise { await wallet!.delete_all(); } /** * Gets the current bitcoin price in chosen Fiat. * @param {string | undefined} [fiat] * @returns {Promise} */ export async function get_bitcoin_price(fiat: string): Promise { const price = await wallet!.get_bitcoin_price(fiat); return price; } export async function get_tag_items(): Promise { const tagItems = await wallet!.get_tag_items(); return tagItems; } /** * Returns the network of the wallet. * @returns {string} */ export async function get_network(): Promise { const network = await wallet!.get_network(); return network || "signet"; } /** * Returns the user's nostr profile data * @returns {any} */ export async function get_nostr_profile(): Promise { if (wallet) { const profile = wallet.get_nostr_profile(); return { ...profile }; } else { return undefined; } } /** * Returns all the on-chain and lightning activity from the wallet. * @param {number | undefined} [limit] * @param {number | undefined} [offset] */ export async function get_activity( limit: number, offset?: number ): Promise { const activity = await wallet!.get_activity(limit, offset); return activity; } export async function get_contact_for_npub( npub: string ): Promise { const contact = await wallet!.get_contact_for_npub(npub); if (!contact) return undefined; return { ...contact?.value }; } export async function create_new_contact( name: string, npub?: string, ln_address?: string, lnurl?: string, image_url?: string ): Promise { const contactId = await wallet!.create_new_contact( name, npub, ln_address, lnurl, image_url ); return contactId; } export async function get_tag_item(id: string): Promise { const tagItem = await wallet!.get_tag_item(id); if (!tagItem) return undefined; return { ...tagItem?.value }; } /** * Returns all the on-chain and lightning activity for a given label * @param {string} label * @returns {Promise} */ export async function get_label_activity( label: string ): Promise { const activity = await wallet!.get_label_activity(label); return activity; } /** * Get dm conversation between us and given npub * Returns a vector of messages sorted by newest first * @param {string} npub * @param {bigint} limit * @param {bigint | undefined} [until] * @param {bigint | undefined} [since] * @returns {Promise} */ export async function get_dm_conversation( npub: string, limit: bigint, until?: bigint, since?: bigint ): Promise { const dms = await wallet!.get_dm_conversation(npub, limit, until, since); return [...dms]; } /** * Gets an invoice from the node manager. * This includes sent and received invoices. * @param {string} invoice * @returns {Promise} */ export async function get_invoice( bolt11: string ): Promise { const invoice = await wallet!.get_invoice(bolt11); if (!invoice) return undefined; // For some reason {...invoice } doesn't bring across the paid field return destructureInvoice(invoice); } /** * Gets all contacts sorted by last used * @returns {Promise} */ export async function get_contacts_sorted(): Promise { const contacts = await wallet!.get_contacts_sorted(); return contacts; } export async function edit_contact( id: string, name: string, npub?: string, ln_address?: string, lnurl?: string, image_url?: string ): Promise { await wallet!.edit_contact(id, name, npub, ln_address, lnurl, image_url); } export async function delete_contact(id: string): Promise { await wallet!.delete_contact(id); } /** * Follows the npub on nostr if we're not already following * @param {string} npub * @returns {Promise} */ export async function follow_npub(npub: string): Promise { await wallet!.follow_npub(npub); } /** * Unfollows the npub on nostr if we're following them * * Returns true if we were following them before * @param {string} npub * @returns {Promise} */ export async function unfollow_npub(npub: string): Promise { await wallet!.unfollow_npub(npub); } /** * Sends a DM to the given npub * @param {string} npub * @param {string} message * @returns {Promise} */ export async function send_dm( npub: string, message: string ): Promise { const result = await wallet!.send_dm(npub, message); return result; } /** * Returns the user's npub * @returns {Promise} */ export async function get_npub(): Promise { const npub = await wallet!.get_npub(); return npub; } /** * Decodes a lightning invoice into useful information. * Will return an error if the invoice is for a different network. * @param {string} invoice * @param {string | undefined} [network] * @returns {Promise} */ export async function decode_invoice( invoice: string, network?: string ): Promise { const decoded = await wallet!.decode_invoice(invoice, network); if (!decoded) return undefined; return destructureInvoice(decoded); } /** * Creates a BIP 21 invoice. This creates a new address and a lightning invoice. * The lightning invoice may return errors related to the LSP. Check the error and * fallback to `get_new_address` and warn the user that Lightning is not available. * * * Errors that might be returned include: * * - [`MutinyJsError::LspGenericError`]: This is returned for various reasons, including if a * request to the LSP server fails for any reason, or if the server returns * a status other than 500 that can't be parsed into a `ProposalResponse`. * * - [`MutinyJsError::LspFundingError`]: Returned if the LSP server returns an error with * a status of 500, indicating an "Internal Server Error", and a message * stating "Cannot fund new channel at this time". This means that the LSP cannot support * a new channel at this time. * * - [`MutinyJsError::LspAmountTooHighError`]: Returned if the LSP server returns an error with * a status of 500, indicating an "Internal Server Error", and a message stating "Invoice * amount is too high". This means that the LSP cannot support the amount that the user * requested. The user should request a smaller amount from the LSP. * * - [`MutinyJsError::LspConnectionError`]: Returned if the LSP server returns an error with * a status of 500, indicating an "Internal Server Error", and a message that starts with * "Failed to connect to peer". This means that the LSP is not connected to our node. * * If the server returns a status of 500 with a different error message, * a [`MutinyJsError::LspGenericError`] is returned. * @param {bigint | undefined} amount * @param {(string)[]} labels * @returns {Promise} */ export async function create_bip21( amount: bigint | undefined, labels: string[] ): Promise { const mbrw = await wallet!.create_bip21(amount, labels); return { ...mbrw?.value } as MutinyBip21RawMaterials; } /** * Creates a lightning invoice. The amount should be in satoshis. * If no amount is provided, the invoice will be created with no amount. * If no description is provided, the invoice will be created with no description. * * If the manager has more than one node it will create a phantom invoice. * If there is only one node it will create an invoice just for that node. * @param {bigint} amount * @param {(string)[]} labels * @returns {Promise} */ export async function create_invoice( amount: bigint, labels: string[] ): Promise { const invoice = await wallet!.create_invoice(amount, labels); if (!invoice) return undefined; return destructureInvoice(invoice); } /** * Estimates the onchain fee for a transaction sweep our on-chain balance * to the given address. * * The fee rate is in sat/vbyte. * @param {string} destination_address * @param {number | undefined} [fee_rate] * @returns {bigint} */ export async function estimate_sweep_tx_fee( address: string ): Promise { const fee = await wallet!.estimate_sweep_tx_fee(address); return fee; } /** * Estimates the onchain fee for a transaction sending to the given address. * The amount is in satoshis and the fee rate is in sat/vbyte. * @param {string} destination_address * @param {bigint} amount * @param {number | undefined} [fee_rate] * @returns {bigint} */ export async function estimate_tx_fee( address: string, amount: bigint, feeRate?: number ): Promise { const fee = await wallet!.estimate_tx_fee(address, amount, feeRate); return fee; } /** * Calls upon a LNURL to get the parameters for it. * This contains what kind of LNURL it is (pay, withdrawal, auth, etc). * @param {string} lnurl * @returns {Promise} */ export async function decode_lnurl(lnurl: string): Promise { const lnurlParams = await wallet!.decode_lnurl(lnurl); // PAIN: this is supposed to be returning bigints, but it returns numbers instead return { ...lnurlParams?.value } as LnUrlParams; } /** * Pays a lightning invoice from the selected node. * An amount should only be provided if the invoice does not have an amount. * The amount should be in satoshis. * @param {string} invoice_str * @param {bigint | undefined} amt_sats * @param {(string)[]} labels * @returns {Promise} */ export async function pay_invoice( invoice_str: string, amt_sats: bigint | undefined, labels: string[] ): Promise { const invoice = await wallet!.pay_invoice(invoice_str, amt_sats, labels); if (!invoice) return undefined; return destructureInvoice(invoice); } /** * Calls upon a LNURL and pays it. * This will fail if the LNURL is not a LNURL pay. * @param {string} lnurl * @param {bigint} amount_sats * @param {string | undefined} zap_npub * @param {(string)[]} labels * @param {string | undefined} [comment] * @param {string | undefined} [privacy_level] * @returns {Promise} */ export async function lnurl_pay( lnurl: string, amount_sats: bigint, zap_npub: string | undefined, labels: string[], comment?: string, privacy_level?: string ): Promise { const invoice = await wallet!.lnurl_pay( lnurl, amount_sats, zap_npub, labels, comment, privacy_level ); if (!invoice) return undefined; return destructureInvoice(invoice); } /** * Sweeps all the funds from the wallet to the given address. * The fee rate is in sat/vbyte. * * If a fee rate is not provided, one will be used from the fee estimator. * @param {string} destination_address * @param {(string)[]} labels * @param {number | undefined} [fee_rate] * @returns {Promise} */ export async function sweep_wallet( destination_address: string, labels: string[], fee_rate?: number ): Promise { const payment = await wallet!.sweep_wallet( destination_address, labels, fee_rate ); return payment; } export async function send_payjoin( payjoin_uri: string, amount: bigint, labels: string[], fee_rate?: number ): Promise { const payment = await wallet!.send_payjoin( payjoin_uri, amount, labels, fee_rate ); return payment; } /** * Sends an on-chain transaction to the given address. * The amount is in satoshis and the fee rate is in sat/vbyte. * * If a fee rate is not provided, one will be used from the fee estimator. * @param {string} destination_address * @param {bigint} amount * @param {(string)[]} labels * @param {number | undefined} [fee_rate] * @returns {Promise} */ export async function send_to_address( destination_address: string, amount: bigint, labels: string[], fee_rate?: number ): Promise { const payment = await wallet!.send_to_address( destination_address, amount, labels, fee_rate ); return payment; } /** * Sends a spontaneous payment to a node from the selected node. * The amount should be in satoshis. * @param {string} to_node * @param {bigint} amt_sats * @param {string | undefined} message * @param {(string)[]} labels * @returns {Promise} */ export async function keysend( to_node: string, amt_sats: bigint, message: string | undefined, labels: string[] ): Promise { const invoice = await wallet!.keysend(to_node, amt_sats, message, labels); if (!invoice) return undefined; return destructureInvoice(invoice); } /** * Gets an invoice from the node manager. * This includes sent and received invoices. * @param {string} hash * @returns {Promise} */ export async function get_invoice_by_hash( hash: string ): Promise { const invoice = await wallet!.get_invoice_by_hash(hash); return destructureInvoice(invoice); } /** * Gets an channel closure from the node manager. * @param {string} user_channel_id * @returns {Promise} */ export async function get_channel_closure( user_channel_id: string ): Promise { const channel_closure = await wallet!.get_channel_closure(user_channel_id); return { channel_id: channel_closure.channel_id, node_id: channel_closure.node_id, reason: channel_closure.reason, timestamp: channel_closure.timestamp } as ChannelClosure; } /** * Gets the details of a specific on-chain transaction. * @param {string} txid * @returns {any} */ export async function get_transaction(txid: string): Promise { // TODO: this is an ActivityItem right? const transaction = await wallet!.get_transaction(txid); return transaction as ActivityItem; } /** * Gets a new bitcoin address from the wallet. * Will generate a new address on every call. * * It is recommended to create a new address for every transaction. * @param {(string)[]} labels * @returns {MutinyBip21RawMaterials} */ export async function get_new_address( labels: string[] ): Promise { const mbrw = await wallet!.get_new_address(labels); return { ...mbrw?.value } as MutinyBip21RawMaterials; } /** * Checks if the given address has any transactions. * If it does, it returns the details of the first transaction. * * This should be used to check if a payment has been made to an address. * @param {string} address * @returns {Promise} */ export async function check_address(address: string): Promise { const tx = await wallet!.check_address(address); return tx as OnChainTx; } /** * Lists all the channels for all the nodes in the node manager. * @returns {Promise} */ export async function list_channels(): Promise { const channels = await wallet!.list_channels(); return channels; } /** * This should only be called when the user is setting up a new profile * never for an existing profile * @param {string | undefined} [name] * @param {string | undefined} [img_url] * @param {string | undefined} [lnurl] * @param {string | undefined} [nip05] * @returns {Promise} */ export async function setup_new_profile( name?: string, img_url?: string, lnurl?: string, nip05?: string ): Promise { const profile = await wallet!.setup_new_profile( name, img_url, lnurl, nip05 ); return profile; } /** * Queries our relays for federation announcements * @returns {Promise} */ export async function discover_federations(): Promise< DiscoveredFederation[] | undefined > { const federations = await wallet!.discover_federations(); return federations; } /** * Checks if we have recommended the given federation * @param {string} federation_id * @returns {Promise} */ export async function has_recommended_federation( federation_id: string ): Promise { const hasRecommended = await wallet!.has_recommended_federation(federation_id); return hasRecommended; } /** * Adds a new federation based on its federation code * @param {string} federation_code * @returns {Promise} */ export async function new_federation(inviteCode: string): Promise { const newFederation = await wallet!.new_federation(inviteCode); return newFederation; } export type NostrMetadata = { name?: string; display_name?: string; picture?: string; lud16?: string; nip05?: string; deleted?: boolean; }; /** * Sets the user's nostr profile data * @param {string | undefined} [name] * @param {string | undefined} [img_url] * @param {string | undefined} [lnurl] * @param {string | undefined} [nip05] * @returns {Promise} */ export async function edit_nostr_profile( name?: string, img_url?: string, lnurl?: string, nip05?: string ): Promise { const profile = await wallet!.edit_nostr_profile( name, img_url, lnurl, nip05 ); return { ...profile }; } /** * Uploads a profile pic to nostr.build and returns the uploaded file's URL * @param {string} img_base64 * @returns {Promise} */ export async function upload_profile_pic(data: string): Promise { const url = await wallet!.upload_profile_pic(data); return url; } /** * Lists all pending NWC invoices * @returns {(PendingNwcInvoice)[]} */ export async function get_pending_nwc_invoices(): Promise< PendingNwcInvoice[] | undefined > { const pending = await wallet!.get_pending_nwc_invoices(); // PAIN // Have to rebuild the array because it's an array of pointers const newPending: PendingNwcInvoice[] = []; for (const pendingItem of pending) { newPending.push({ amount_sats: pendingItem.amount_sats, expiry: pendingItem.expiry, id: pendingItem.id, index: pendingItem.index, invoice: pendingItem.invoice, invoice_description: pendingItem.invoice_description, npub: pendingItem.npub, profile_name: pendingItem.profile_name } as PendingNwcInvoice); } return newPending; } /** * Deletes a nostr wallet connect profile * @param {number} profile_index * @returns {Promise} */ export async function delete_nwc_profile(index: number): Promise { await wallet!.delete_nwc_profile(index); } /** * Get nostr wallet connect profiles * @returns {(NwcProfile)[]} */ export async function get_nwc_profiles(): Promise { const profiles = await wallet!.get_nwc_profiles(); // PAIN // Have to rebuild the array because it's an array of pointers const newProfiles: NwcProfile[] = []; for (const profile of profiles) { newProfiles.push({ ...profile.value } as NwcProfile); } return newProfiles; } /** * Approves a nostr wallet auth request. * Creates a new NWC profile and saves to storage. * This will also broadcast the info event to the relay. * @param {string} name * @param {string} uri * @param {bigint} budget * @param {BudgetPeriod} period * @returns {Promise} */ export async function approve_nostr_wallet_auth( name: string, uri: string ): Promise { const profile = await wallet!.approve_nostr_wallet_auth(name, uri); return profile; } /** * Finds a nostr wallet connect profile by index * @param {number} index * @returns {Promise} */ export async function get_nwc_profile(index: number): Promise { const profile = await wallet!.get_nwc_profile(index); console.log("get_nwc_profile", profile); return { ...profile.value } as NwcProfile; } /** * Approves an invoice and sends the payment * @param {string} hash * @returns {Promise} */ export async function approve_invoice(hash: string): Promise { await wallet!.approve_invoice(hash); } /** * Removes an invoice from the pending list, will also remove expired invoices * @param {string} hash * @returns {Promise} */ export async function deny_invoice(hash: string): Promise { await wallet!.deny_invoice(hash); } /** * Removes all invoices from the pending list * @returns {Promise} */ export async function deny_all_pending_nwc(): Promise { await wallet!.deny_all_pending_nwc(); } /** * Set budget for a NWC Profile * @param {number} profile_index * @param {bigint} budget_sats * @param {BudgetPeriod} period * @param {bigint | undefined} [single_max_sats] * @returns {Promise} */ export async function set_nwc_profile_budget( profile_index: number, budget_sats: bigint, period: BudgetPeriod, single_max_sats?: bigint ): Promise { const profile = await wallet!.set_nwc_profile_budget( profile_index, budget_sats, period, single_max_sats ); return profile; } /** * Require approval for a NWC Profile * @param {number} profile_index * @returns {Promise} */ export async function set_nwc_profile_require_approval( profile_index: number ): Promise { const profile = await wallet!.set_nwc_profile_require_approval(profile_index); return profile; } /** * Create a nostr wallet connect profile * @param {string} name * @param {(string)[] | undefined} [commands] * @returns {Promise} */ export async function create_nwc_profile( name: string, commands?: string[] ): Promise { const profile = await wallet!.create_nwc_profile(name, commands); return profile; } /** * Create a budgeted nostr wallet connect profile * @param {string} name * @param {bigint} budget * @param {BudgetPeriod} period * @param {bigint | undefined} [single_max] * @param {(string)[] | undefined} [commands] * @returns {Promise} */ export async function create_budget_nwc_profile( name: string, budget: bigint, period: BudgetPeriod, single_max?: bigint, commands?: string[] ): Promise { const profile = await wallet!.create_budget_nwc_profile( name, budget, period, single_max, commands ); return profile; } /** * Disconnects from a peer from the selected node. * @param {string} peer * @returns {Promise} */ export async function disconnect_peer(pubkey: string): Promise { await wallet!.disconnect_peer(pubkey); } /** * Deletes a peer from the selected node. * This will make it so that the node will not attempt to * reconnect to the peer. * @param {string} peer * @returns {Promise} */ export async function delete_peer(pubkey: string): Promise { await wallet!.delete_peer(pubkey); } /** * Lists all the peers for all the nodes in the node manager. * @returns {Promise} */ export async function list_peers(): Promise { return wallet!.list_peers(); } /** * Attempts to connect to a peer from the selected node. * @param {string} connection_string * @param {string | undefined} [label] * @returns {Promise} */ export async function connect_to_peer( connection_string: string ): Promise { await wallet!.connect_to_peer(connection_string); } /** * Closes a channel with the given outpoint. * * If force is true, the channel will be force closed. * * If abandon is true, the channel will be abandoned. * This will force close without broadcasting the latest transaction. * This should only be used if the channel will never actually be opened. * * If both force and abandon are true, an error will be returned. * @param {string} outpoint * @param {boolean} force * @param {boolean} abandon * @returns {Promise} */ export async function close_channel( outpoint: string, force: boolean, abandon: boolean ): Promise { await wallet!.close_channel(outpoint, force, abandon); } /** * Removes a federation by setting its archived status to true, based on the FederationId. * @param {string} federation_id * @returns {Promise} */ export async function remove_federation(federation_id: string): Promise { await wallet!.remove_federation(federation_id); } /** * Opens a channel from our selected node to the given pubkey. * The amount is in satoshis. * * The node must be online and have a connection to the peer. * The wallet much have enough funds to open the channel. * @param {string | undefined} to_pubkey * @param {bigint} amount * @param {number | undefined} [fee_rate] * @returns {Promise} */ export async function open_channel( to_pubkey: string | undefined, amount: bigint ): Promise { const channel = await wallet!.open_channel(to_pubkey, amount); return { ...channel.value } as MutinyChannel; } /** * Lists the pubkeys of the lightning node in the manager. * @returns {Promise} */ export async function list_nodes(): Promise { return await wallet!.list_nodes(); } /** * Changes all the node's LSPs to the given config. If any of the nodes have an active channel with the * current LSP, it will fail to change the LSP. * * Requires a restart of the node manager to take effect. * @param {string | undefined} [lsp_url] * @param {string | undefined} [lsp_connection_string] * @param {string | undefined} [lsp_token] * @returns {Promise} */ export async function change_lsp( lsp_url?: string, lsp_connection_string?: string, lsps_token?: string ): Promise { await wallet!.change_lsp(lsp_url, lsp_connection_string, lsps_token); } /** * Resets BDK's keychain tracker. This will require a re-sync of the blockchain. * * This can be useful if you get stuck in a bad state. * @returns {Promise} */ export async function reset_onchain_tracker(): Promise { await wallet!.reset_onchain_tracker(); } /** * Starts up all the nodes again. * Not needed after [NodeManager]'s `new()` function. * @returns {Promise} */ export async function start(): Promise { await wallet!.start(); } /** * Authenticates with a LNURL-auth for the given profile. * @param {string} lnurl * @returns {Promise} */ export async function lnurl_auth(lnurl: string): Promise { // TODO: test auth await wallet!.lnurl_auth(lnurl); } type PlanDetails = { id: number; amount_sat: bigint; }; /** * Gets the subscription plans for Mutiny+ subscriptions * @returns {Promise} */ export async function get_subscription_plans(): Promise { return await wallet!.get_subscription_plans(); } /** * Subscribes to a Mutiny+ plan with a specific plan id. * * Returns a lightning invoice so that the plan can be paid for to start it. * @param {number} id * @returns {Promise} */ export async function subscribe_to_plan(id: number): Promise { const invoice = await wallet!.subscribe_to_plan(id); return destructureInvoice(invoice); } /** * Pay the subscription invoice. This will post a NWC automatically afterwards. * @param {string} invoice_str * @param {boolean} autopay * @returns {Promise} */ export async function pay_subscription_invoice( invoice_str: string, autopay: boolean ): Promise { await wallet!.pay_subscription_invoice(invoice_str, autopay); } /** * Change our active nostr keys to the given nsec * @param {string | undefined} [nsec] * @param {string | undefined} [extension_pk] * @returns {Promise} */ export async function change_nostr_keys( nsec?: string, extension_pk?: string ): Promise { return await wallet!.change_nostr_keys(nsec, extension_pk); } /** * Sets the user's nostr profile data to a "deleted" state * @returns {Promise} */ export async function delete_profile(): Promise { await wallet!.delete_profile(); } /** * Export the user's nostr secret key if available * @returns {Promise} */ export async function export_nsec(): Promise { // TODO: is this the right nsec? return await wallet!.export_nsec(); } /** * Returns the mnemonic seed phrase for the wallet. * @returns {string} */ export async function show_seed(): Promise { return await wallet!.show_seed(); } /** * Create a single use nostr wallet connect profile * @param {bigint} amount_sats * @param {string} nwc_uri * @returns {Promise} */ export async function claim_single_use_nwc( amount_sats: bigint, nwc_uri: string ): Promise { return await wallet!.claim_single_use_nwc(amount_sats, nwc_uri); } /** * Calls upon a LNURL and withdraws from it. * This will fail if the LNURL is not a LNURL withdrawal. * @param {string} lnurl * @param {bigint} amount_sats * @returns {Promise} */ export async function lnurl_withdraw( lnurl: string, amount_sats: bigint ): Promise { return await wallet!.lnurl_withdraw(lnurl, amount_sats); } /** * Checks the registered username for the user * @returns {Promise} */ export async function check_lnurl_name(): Promise { return await wallet!.check_lnurl_name(); } /** * Checks if a given LNURL name is available * @param {string} name * @returns {Promise} */ export async function check_available_lnurl_name( name: string ): Promise { return await wallet!.check_available_lnurl_name(name); } /** * Reserves a given LNURL name for the user * @param {string} name * @returns {Promise} */ export async function reserve_lnurl_name(name: string): Promise { return await wallet!.reserve_lnurl_name(name); } /** * Creates a recommendation event for a federation * @param {string} invite_code * @param {string | undefined} [review] * @returns {Promise} */ export async function recommend_federation( invite_code: string, review?: string ): Promise { return await wallet!.recommend_federation(invite_code, review); } /** * Creates a delete event for a federation recommendation * @param {string} federation_id * @returns {Promise} */ export async function delete_federation_recommendation( federation_id: string ): Promise { await wallet!.delete_federation_recommendation(federation_id); } /** * Gets the current balances of each federation. * @returns {Promise} */ export async function get_federation_balances(): Promise { const balances = await wallet!.get_federation_balances(); // PAIN // Have to rebuild the balances from the raw data, which is a bit of a pain const newBalances: FederationBalance[] = []; for (const balance of balances.balances) { const newBalance: FederationBalance = { balance: balance.balance, identity_federation_id: balance.identity_federation_id, identity_uuid: balance.identity_uuid } as FederationBalance; newBalances.push(newBalance); } return { balances: newBalances } as FederationBalances; } export async function change_password( old_password?: string, new_password?: string ): Promise { await wallet!.change_password(old_password, new_password); } /** * Converts a satoshi amount to BTC. * @param {bigint} sats * @returns {number} */ export async function convert_sats_to_btc(sats: bigint): Promise { return await MutinyWallet.convert_sats_to_btc(sats); } /** * Converts a bitcoin amount in BTC to satoshis. * @param {number} btc * @returns {bigint} */ export async function convert_btc_to_sats(btc: number): Promise { return await MutinyWallet.convert_btc_to_sats(btc); } /** * Returns if there is a saved wallet in storage. * This is checked by seeing if a mnemonic seed exists in storage. * @returns {Promise} */ export async function has_node_manager(): Promise { return await MutinyWallet.has_node_manager(); } /** * Convert an npub string to a hex string * @param {string} npub * @returns {Promise} */ export async function npub_to_hexpub(npub: string): Promise { return await MutinyWallet.npub_to_hexpub(npub); } /** * Convert an npub string to a hex string * @param {string} nsec * @returns {Promise} */ export async function nsec_to_npub(nsec: string): Promise { return await MutinyWallet.nsec_to_npub(nsec); } /** * Convert an hex string to a npub string * @param {string} npub * @returns {Promise} */ export async function hexpub_to_npub(hexpub: string): Promise { // TODO: the argument is called "npub" but it's actually a hexpub? return await MutinyWallet.hexpub_to_npub(hexpub); } /** * Restore's the mnemonic after deleting the previous state. * * Backup the state beforehand. Does not restore lightning data. * Should refresh or restart afterwards. Wallet should be stopped. * @param {string} m * @param {string | undefined} [password] * @returns {Promise} */ export async function restore_mnemonic( mnemonic: string, password?: string ): Promise { await MutinyWallet.restore_mnemonic(mnemonic, password); } /** * Restore a node manager from a json object. * @param {string} json * @returns {Promise} */ export async function import_json(json: string): Promise { await MutinyWallet.import_json(json); } /** * Exports the current state of the node manager to a json object. * @param {string | undefined} [password] * @returns {Promise} */ export async function export_json(password?: string): Promise { return await MutinyWallet.export_json(password); } /** * Exports the current state of the node manager to a json object. * @returns {Promise} */ export async function get_logs(): Promise { return await MutinyWallet.get_logs(); } /** * Returns the number of remaining seconds until the device lock expires. */ export async function get_device_lock_remaining_secs( password?: string, auth_url?: string, storage_url?: string ): Promise { return await MutinyWallet.get_device_lock_remaining_secs( password, auth_url, storage_url ); } /** * Opens a channel from our selected node to the given pubkey. * It will spend the all the on-chain utxo in full to fund the channel. * * The node must be online and have a connection to the peer. * @param {string | undefined} [to_pubkey] * @returns {Promise} */ export async function sweep_all_to_channel( to_pubkey?: string ): Promise { return await wallet!.sweep_all_to_channel(to_pubkey); } /** * Estimates the onchain fee for sweeping our on-chain balance to open a lightning channel. * The fee rate is in sat/vbyte. * @param {number | undefined} [fee_rate] * @returns {bigint} */ export async function estimate_sweep_channel_open_fee( fee_rate?: number | undefined ): Promise { return await wallet!.estimate_sweep_channel_open_fee(fee_rate); } /** * Sweep the federation balance into a lightning channel * @param {bigint | undefined} [amount] * @returns {Promise} */ export async function sweep_federation_balance( amount?: bigint, from_federation_id?: string, to_federation_id?: string ): Promise { const result = await wallet!.sweep_federation_balance( amount, from_federation_id, to_federation_id ); return { ...result.value } as FedimintSweepResult; } /** * Estimate the fee before trying to sweep from federation * @param {bigint | undefined} [amount] * @returns {Promise} */ export async function estimate_sweep_federation_fee( amount?: bigint ): Promise { return await wallet!.estimate_sweep_federation_fee(amount); } export async function parse_params(params: string): Promise { const paramsResult = await new PaymentParams(params); // PAIN just another object rebuild return { address: paramsResult.address, amount_msats: paramsResult.amount_msats, amount_sats: paramsResult.amount_sats, cashu_token: paramsResult.cashu_token, disable_output_substitution: paramsResult.disable_output_substitution, fedimint_invite_code: paramsResult.fedimint_invite_code, fedimint_oob_notes: paramsResult.fedimint_oob_notes, invoice: paramsResult.invoice, is_lnurl_auth: paramsResult.is_lnurl_auth, lightning_address: paramsResult.lightning_address, lnurl: paramsResult.lnurl, memo: paramsResult.memo, network: paramsResult.network, node_pubkey: paramsResult.node_pubkey, nostr_pubkey: paramsResult.nostr_pubkey, nostr_wallet_auth: paramsResult.nostr_wallet_auth, offer: paramsResult.offer, payjoin_endpoint: paramsResult.payjoin_endpoint, payjoin_supported: paramsResult.payjoin_supported, refund: paramsResult.refund, string: paramsResult.string } as PaymentParams; }