UNPKG

@gobob/sats-wagmi

Version:
1,248 lines (1,223 loc) 52.3 kB
"use client"; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/provider.tsx import { Network as BitcoinNetwork } from "bitcoin-address-validation"; import { createContext, useCallback, useContext, useEffect, useState } from "react"; import { useLocalStorage } from "usehooks-ts"; import { QueryClientProvider } from "@tanstack/react-query"; // src/connectors/xverse.ts import { AddressPurpose, BitcoinNetworkType, getAddress, sendBtcTransaction, signTransaction, signMessage } from "sats-connect"; import { base64 as base642, hex as hex2 } from "@scure/base"; import validate from "bitcoin-address-validation"; // src/connectors/base.ts import { EsploraClient, createBitcoinPsbt } from "@gobob/bob-sdk"; import retry from "async-retry"; import { Transaction } from "@scure/btc-signer"; import { hex, base64 } from "@scure/base"; import { getAddressInfo } from "bitcoin-address-validation"; var SatsConnector = class { constructor(network, id, name, homepage, icon, esploraBaseUrl) { /** Unique connector id */ __publicField(this, "id"); /** Connector name */ __publicField(this, "name"); /** Extension or Snap homepage */ __publicField(this, "homepage"); /** Connector icon */ __publicField(this, "icon"); /** Whether connector is usable */ __publicField(this, "ready", false); /** Address types depend on which wallet is connected. Some wallets support connecting multiple addresses. For example: - Xverse: payment (P2SH) and ordinals (P2TR) - UniSat: depends on the user selection in the extension - Leather: payment (P2WPKH) and ordinals (P2TR) - BOB MM Snap: payment (P2WPKH) and ordinals (P2TR) */ __publicField(this, "paymentAddress", ""); /** P2TR address for ordinals and runes is kept separate from the payment address to ensure that the user does not accidentally spend from the ordinals address */ __publicField(this, "ordinalsAddress", ""); /** The public key is required to spend from P2SH and P2WSH addresses */ __publicField(this, "publicKey"); /** The Bitcoin network (mainnet, testnet, regtest) */ // NOTE: signet is currently not supported __publicField(this, "network"); /** Override for the Esplora API */ __publicField(this, "esploraBaseUrl"); this.network = network; this.id = id; this.name = name; this.homepage = homepage; this.icon = icon; this.esploraBaseUrl = esploraBaseUrl; } /** Disconnect from the wallet */ disconnect() { this.paymentAddress = void 0; this.ordinalsAddress = void 0; this.publicKey = void 0; } /** Get the payment address * @returns The payment address */ getPaymentAddress() { return this.paymentAddress; } /** Get the ordinals address * @returns The ordinals address */ getOrdinalsAddress() { return this.ordinalsAddress; } /** Convenience wrapper around the getAddressInfo function * @param address - The address to get the type of. * @returns The address type of the address. */ getAddressType(address) { return getAddressInfo(address).type; } /** Check if the address is authorized */ isAuthorized() { const address = this.getPaymentAddress(); return !!address; } /** Return the public key of the connected address. * @returns The public key of the connected address. */ getPublicKey() { return this.publicKey; } get esploraClient() { let esploraClient; if (this.esploraBaseUrl) { esploraClient = new EsploraClient(this.esploraBaseUrl); } else { esploraClient = new EsploraClient(this.network); } return esploraClient; } /** Get the transaction from the transaction ID * @param txId - The transaction ID to get the transaction from. * @returns The transaction hex. * * @example * ```typescript * import { Transaction } from '@scure/btc-signer'; * * const txId = 'f5e7 ... 3b'; * const txHex = await connector.getTransaction(txId); * * // Decode the transaction * Transaction.fromRaw(Buffer.from(txHex)); * ``` */ async getTransaction(txId) { const esploraClient = this.esploraClient; return retry( async (bail) => { const txHex = await esploraClient.getTransactionHex(txId); if (!txHex) { bail(new Error("Failed")); } return txHex; }, { retries: 20, minTimeout: 2e3, maxTimeout: 5e3 } ); } /** Send BTC to an address * @param toAddress - The address to send BTC to. Can be any valid BTC address. * @param amount - The BTC to send denomination in satoshis. * @returns The transaction ID of the sent transaction. * * @example * ```typescript * const toAddress = 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'; * const amount = 10000; // 0.0001 BTC * const txId = await connector.sendToAddress(toAddress, amount); * ``` */ async sendToAddress(toAddress, amount) { const esploraClient = this.esploraClient; const signedTx = await this.createAndSignTx(toAddress, amount); const txId = await esploraClient.broadcastTx(signedTx); return txId; } /** Send BTC to an address with data in an OP_RETURN output * @param toAddress - The address to send BTC to. Can be any valid BTC address. * @param amount - The BTC to send denomination in satoshis. * @param data - Optional OP_RETURN data to include in the transaction. * @returns The transaction ID of the sent transaction. * * @example * ```typescript * const toAddress = 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'; * const amount = 10000; // 0.0001 BTC * const data = 'Hello, World!'; * const txId = await connector.sendToAddressWithData(toAddress, amount, data); * ``` * @note Most wallets don't support transfers with OP_RETURN data, so we need to handle this case separately * where wallets do not support it. This function is overwritten in the connectors that support transfer with OP_RETURN. * @note Most Bitcoin nodes accept 80 bytes maximum for OP_RETURN data. If you want to include more data, * consider using a dedicated service that can include such transactions in a block. */ async sendToAddressWithOpReturn(toAddress, amount, data) { const esploraClient = this.esploraClient; const signedTx = await this.createAndSignTx(toAddress, amount, data); const txId = await esploraClient.broadcastTx(signedTx); return txId; } /** Create and sign a transaction with an OP_RETURN output * @param toAddress - The address to send BTC to. Can be any valid BTC address. * @param amount - The BTC to send denomination in satoshis. * @param data - The OP_RETURN data to include in the transaction. * @returns The signed transaction hex. * * @example * ```typescript * const toAddress = 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'; * const amount = 10000; // 0.0001 BTC * const data = 'Hello, World!'; * const signedTx = await connector.createTxWithOpReturn(toAddress, amount, data); * ``` */ async createAndSignTx(toAddress, amount, data) { this.validatePaymentAddress(); const psbtBase64 = await createBitcoinPsbt(this.paymentAddress, toAddress, amount, this.publicKey, data); return this.signAllInputs(psbtBase64); } /** Sign all PSBT inputs * @param psbtBase64 - The Base64 encoded PSBT. * @returns The signed transaction hex. */ async signAllInputs(psbtBase64) { this.validatePaymentAddress(); const unsignedTx = Transaction.fromPSBT(base64.decode(psbtBase64)); const inputLength = unsignedTx.inputsLength; const inputsToSign = Array.from({ length: inputLength }, (_, i) => i); const psbt = unsignedTx.toPSBT(0); const psbtHex = hex.encode(psbt); const signedPsbtHex = await this.signPsbt(psbtHex, [ { address: this.paymentAddress, signingIndexes: inputsToSign } ]); const signedTx = Transaction.fromPSBT(hex.decode(signedPsbtHex)); signedTx.finalize(); return signedTx.hex; } validatePaymentAddress() { if (!this.paymentAddress) { throw new Error("No payment address specified"); } const addressType = this.getAddressType(this.paymentAddress); if (!addressType) { throw new Error("Invalid address type"); } } }; // src/connectors/xverse.ts var getWalletNetwork = (network) => ({ type: network === "mainnet" ? BitcoinNetworkType.Mainnet : BitcoinNetworkType.Testnet }); var XverseConnector = class extends SatsConnector { constructor(network) { super(network, "xverse", "Xverse", "https://www.xverse.app/"); } async connect() { return new Promise(async (resolve, reject) => { await getAddress({ payload: { purposes: [AddressPurpose.Ordinals, AddressPurpose.Payment], message: "Address for receiving Ordinals and payments", network: getWalletNetwork(this.network) }, onFinish: (res) => { const { address: ordinalsAddress } = res.addresses.find( (address) => address.purpose === AddressPurpose.Ordinals ); const { address: paymentAddress, publicKey } = res.addresses.find( (address) => address.purpose === AddressPurpose.Payment ); if (!validate(paymentAddress, this.network)) { throw new Error(`Invalid Network. Please switch to Bitcoin ${this.network}.`); } this.paymentAddress = paymentAddress; this.ordinalsAddress = ordinalsAddress; this.publicKey = publicKey; resolve(); }, onCancel: () => { reject(new Error("User rejected connect")); } }); }); } on() { } removeListener() { } async isReady() { this.ready = !!window.XverseProviders; return this.ready; } async signMessage(message) { return new Promise(async (resolve, reject) => { if (!this.paymentAddress) { return reject(new Error("Something went wrong while connecting")); } await signMessage({ payload: { address: this.paymentAddress, message, network: getWalletNetwork(this.network) }, onFinish: (response) => { resolve(response); }, onCancel: () => reject(new Error("Canceled")) }); }); } async sendToAddress(toAddress, amount) { return new Promise(async (resolve, reject) => { if (!this.paymentAddress) { return reject(new Error("Something went wrong while connecting")); } await sendBtcTransaction({ payload: { network: getWalletNetwork(this.network), recipients: [{ address: toAddress, amountSats: BigInt(amount) }], senderAddress: this.paymentAddress }, onFinish: (response) => { resolve(response); }, onCancel: () => { reject(new Error("Send BTC Transaction canceled")); } }); }); } async signPsbt(psbtHex, psbtInputAccounts) { return new Promise(async (resolve, reject) => { if (!this.ordinalsAddress) { return reject(new Error("Something went wrong while connecting")); } const psbtBase64 = base642.encode(hex2.decode(psbtHex)); await signTransaction({ payload: { network: getWalletNetwork(this.network), message: "Sign Transaction", psbtBase64, broadcast: false, inputsToSign: psbtInputAccounts }, onFinish: (response) => { resolve(hex2.encode(base642.decode(response.psbtBase64))); }, onCancel: () => reject(new Error("Canceled")) }); }); } }; // src/connectors/leather.ts import validate2 from "bitcoin-address-validation"; var LeatherConnector = class extends SatsConnector { constructor(network) { super(network, "leather", "Leather", "https://leather.io/"); __publicField(this, "derivationPath"); } async connect() { const userAddresses = await window.LeatherProvider.request("getAddresses"); const paymentAccount = userAddresses.result.addresses.find((el) => el.type === "p2wpkh"); const ordinalsAccount = userAddresses.result.addresses.find((el) => el.type === "p2tr"); if (!paymentAccount || !ordinalsAccount) { throw new Error("Failed to connect wallet"); } if (!validate2(paymentAccount.address, this.network)) { throw new Error(`Invalid Network. Please switch to Bitcoin ${this.network}.`); } this.paymentAddress = paymentAccount.address; this.ordinalsAddress = ordinalsAccount.address; this.publicKey = paymentAccount.publicKey; this.derivationPath = paymentAccount.derivationPath; } on() { } removeListener() { } async isReady() { this.ready = !!window.LeatherProvider; return this.ready; } async signMessage(message) { const resp = await window.LeatherProvider.request("signMessage", { message, network: this.network }); return resp.result.signature; } async sendToAddress(toAddress, amount) { const resp = await window.LeatherProvider.request("sendTransfer", { address: toAddress, amount: amount.toString(), network: this.network }); return resp.result.txid; } async signPsbt(psbtHex, psbtInputAccounts) { let inputs = []; for (const input of psbtInputAccounts) { for (const index of input.signingIndexes) { inputs.push(index); } } const response = await window.LeatherProvider.request("signPsbt", { hex: psbtHex, signAtIndex: inputs, network: this.network, broadcast: false }); return response.result.hex; } }; // src/connectors/unisat.ts import { AddressType as AddressType2 } from "bitcoin-address-validation"; // src/assets/bitget.ts var bitgetLogo = "data:image/png;base64,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"; // src/connectors/unisat.ts var metadata = { bitkeep: { id: "bitget", name: "Bitget Wallet", homepage: "https://web3.bitget.com", icon: bitgetLogo }, binancew3w: { id: "binancew3w", name: "Binance Web3 Wallet", homepage: "https://www.binance.com/en-GB" }, unisat: { id: "unisat", name: "UniSat", homepage: "https://unisat.io/" } }; var UnisatConnector = class extends SatsConnector { constructor(network, source) { const { homepage, id, name, icon } = metadata[source]; super(network, id, name, homepage, icon); __publicField(this, "source"); this.source = source; } getSource() { var _a, _b; switch (this.source) { case "bitkeep": return ((_a = window == null ? void 0 : window.bitkeep) == null ? void 0 : _a.unisat) || void 0; case "binancew3w": return ((_b = window == null ? void 0 : window.binancew3w) == null ? void 0 : _b.bitcoin) || void 0; case "unisat": return (window == null ? void 0 : window.unisat) || void 0; } } async connect() { const walletSource = this.getSource(); await walletSource.switchChain("BITCOIN_SIGNET"); const [accounts, publicKey] = await Promise.all([walletSource.requestAccounts(), walletSource.getPublicKey()]); this.paymentAddress = accounts[0]; this.ordinalsAddress = accounts[0]; this.publicKey = publicKey; walletSource.on("accountsChanged", ([account]) => this.changeAccount(account)); } disconnect() { this.paymentAddress = void 0; this.publicKey = void 0; } signMessage(message) { return this.getSource().signMessage(message); } on(callback) { this.getSource().on("accountsChanged", ([account]) => { callback(account); this.changeAccount(account); }); } removeListener(callback) { this.getSource().removeListener("accountsChanged", ([account]) => { callback(account); this.changeAccount(account); }); } async changeAccount(account) { this.paymentAddress = account; this.publicKey = await this.getSource().getPublicKey(); } async isReady() { this.ready = typeof this.getSource() !== "undefined"; return this.ready; } async sendToAddress(toAddress, amount) { const source = this.getSource(); if ("sendBitcoin" in source) { return source.sendBitcoin(toAddress, amount); } else { return super.sendToAddress(toAddress, amount); } } async signPsbt(psbtHex, psbtInputAccounts) { if (!this.publicKey) { throw new Error("Something went wrong while connecting"); } let inputs = []; for (const input of psbtInputAccounts) { for (const index of input.signingIndexes) { inputs.push(index); } } if (!this.paymentAddress) { throw new Error("No payment address specified"); } const toSignInputs = inputs.map((index) => { return { index, publicKey: this.publicKey, disableTweakSigner: this.getAddressType(this.paymentAddress) !== AddressType2.p2tr }; }); const signedPsbtHex = await this.getSource().signPsbt(psbtHex, { autoFinalized: false, toSignInputs }); return signedPsbtHex; } }; // src/connectors/mm-snap.ts import * as ecc from "@bitcoin-js/tiny-secp256k1-asmjs"; import { BIP32Factory } from "bip32"; import * as bitcoin from "bitcoinjs-lib"; import bs58check from "bs58check"; import { base64 as base643, hex as hex3 } from "@scure/base"; // src/utils/constants.ts var INTERVAL = { HOUR: 36e5, MINUTE: 6e4, SECONDS_30: 3e4, SECONDS_15: 15e3, SECONDS_10: 1e4 }; // src/utils/metamask.ts var PsbtValidateErrors = [ { code: 10001, name: "InputsDataInsufficient", message: "Not all inputs have prev Tx raw hex" }, { code: 10002, name: "InputsNetworkNotMatch", message: "Not every input matches network" }, { code: 10003, name: "OutputsNetworkNotMatch", message: "Not every input matches network" }, { code: 10004, name: "InputNotSpendable", message: "Not all inputs belongs to current account" }, { code: 10005, name: "ChangeAddressInvalid", message: "Change address doesn't belongs to current account" }, { code: 10006, name: "FeeTooHigh", message: "Too much fee" }, { code: 10007, name: "AmountNotMatch", message: "Transaction input amount not match" } ]; var SnapRequestErrors = [ { code: 2e4, name: "NoPermission", message: "Unauthorized to perform action." }, { code: 20001, name: "RejectKey", message: "User reject to access the key" }, { code: 20002, name: "RejectSign", message: "User reject the sign request" }, { code: 20003, name: "SignInvalidPath", message: "invalid path" }, { code: 20004, name: "SignFailed", message: "Sign transaction failed" }, { code: 20005, name: "NetworkNotMatch", message: "Network not match" }, { code: 20006, name: "ScriptTypeNotSupport", message: "ScriptType is not supported." }, { code: 20007, name: "MethodNotSupport", message: "Method not found." }, { code: 20008, name: "ActionNotSupport", message: "Action not supported" }, { code: 20009, name: "UserReject", message: "User rejected the request." } ]; var BaseError = class extends Error { constructor(code) { super(); __publicField(this, "code"); __publicField(this, "resolve", (fn) => { fn(); }); this.code = code; } }; var SnapError = class extends BaseError { constructor(message) { const userFriendlyError = mapErrorToUserFriendlyError(message); super(userFriendlyError.code); this.name = userFriendlyError.name; this.message = userFriendlyError.message; } }; var mapErrorToUserFriendlyError = (message) => { const psbtValidateError = PsbtValidateErrors.find((item) => message.startsWith(item.message)); const snapRequestError = SnapRequestErrors.find((item) => message.startsWith(item.message)); if (psbtValidateError) { switch (psbtValidateError.name) { case "FeeTooHigh": return { ...psbtValidateError, message: "Fee too high" }; default: return { ...psbtValidateError, message: "Transaction is invalid" }; } } if (snapRequestError) { switch (snapRequestError.name) { case "NoPermission": return { ...snapRequestError, message: "This error is usually caused by resetting the recovery phrase, please try to reinstall MetaMask" }; case "SignInvalidPath": return { ...snapRequestError, message: "Sign transaction failed" }; case "ScriptTypeNotSupport": case "MethodNotSupport": case "ActionNotSupport": return { ...snapRequestError, message: "Request error" }; default: return snapRequestError; } } return { message, code: 0, name: "UnknownSnapError" }; }; // src/connectors/mm-snap.ts var getSnapNetwork = (network) => { switch (network) { default: case "mainnet": return "main"; case "testnet": return "test"; } }; var getBitcoinJsNetwork = (network) => { switch (network) { default: case "mainnet": return bitcoin.networks.bitcoin; case "testnet": return bitcoin.networks.testnet; } }; function anyPubToXpub(xyzpub, network) { let data = bs58check.decode(xyzpub); data = data.subarray(4); const tpubPrefix = "043587cf"; const xpubPrefix = "0488b21e"; const prefix = network === bitcoin.networks.testnet ? tpubPrefix : xpubPrefix; data = Buffer.concat([Buffer.from(prefix, "hex"), data]); return bs58check.encode(data); } function addressFromExtPubKey(bip32, xyzpub, network) { const forcedXpub = anyPubToXpub(xyzpub, network); const pubkey = bip32.fromBase58(forcedXpub, network).derive(0).derive(0).publicKey; return bitcoin.payments.p2wpkh({ pubkey, network }).address; } var BitcoinScriptType = /* @__PURE__ */ ((BitcoinScriptType2) => { BitcoinScriptType2["P2WPKH"] = "P2WPKH"; return BitcoinScriptType2; })(BitcoinScriptType || {}); var getDefaultBip32Path = (scriptType, network) => { switch (scriptType) { case "P2WPKH" /* P2WPKH */: return `m/84'/${network === "main" ? "0" : "1"}'/0'/0/0`; } }; var DEFAULT_SCRIPT_TYPE = "P2WPKH" /* P2WPKH */; var snapId = "npm:@gobob/bob-snap"; var MMSnapConnector = class extends SatsConnector { constructor(network) { super(network, "metamask_snap", "MetaMask", "https://snaps.metamask.io/snap/npm/gobob/bob-snap/"); __publicField(this, "extendedPublicKey"); __publicField(this, "snapNetwork", "main"); __publicField(this, "bip32"); this.snapNetwork = getSnapNetwork(network); bitcoin.initEccLib(ecc); this.bip32 = BIP32Factory(ecc); } async connect() { var _a; try { const result = await window.ethereum.request({ method: "wallet_requestSnaps", params: { [snapId]: { version: "2.2.2" } } }); console.log("Using snap version:", (_a = result == null ? void 0 : result[snapId]) == null ? void 0 : _a.version); } finally { const actualNetwork = await this.getNetworkInSnap(); if (actualNetwork === "" || actualNetwork !== this.snapNetwork) { await this.setNetworkInSnap(this.snapNetwork); } this.extendedPublicKey = await this.getExtendedPublicKey(); this.publicKey = this.getPublicKey(); this.paymentAddress = addressFromExtPubKey( this.bip32, this.extendedPublicKey.xpub, getBitcoinJsNetwork(this.network) ); } } async isReady() { const snaps = await window.ethereum.request({ method: "wallet_getSnaps" }); return Object.keys(snaps || {}).includes(snapId); } on() { } removeListener() { } async getExtendedPublicKey() { if (this.extendedPublicKey) { return this.extendedPublicKey; } return await this.snapRequest({ method: "btc_getPublicExtendedKey", params: { network: this.snapNetwork, scriptType: DEFAULT_SCRIPT_TYPE }, errMsg: "Get extended public key failed" }); } getPublicKey() { if (!this.extendedPublicKey) { throw new Error("Something wrong with connect"); } const network = getBitcoinJsNetwork(this.network); const forcedXpub = anyPubToXpub(this.extendedPublicKey.xpub, network); const pubkey = this.bip32.fromBase58(forcedXpub, network).derive(0).derive(0).publicKey; return pubkey.toString("hex"); } async signMessage(message) { return await this.snapRequest({ method: "btc_signMessage", params: { message, hdPath: getDefaultBip32Path(DEFAULT_SCRIPT_TYPE, this.snapNetwork) }, errMsg: "Could not sign message" }); } async signInput(inputIndex, psbt) { const psbtBase64 = await this.snapRequest({ method: "btc_signInput", params: { psbt: psbt.toBase64(), network: this.snapNetwork, scriptType: DEFAULT_SCRIPT_TYPE, inputIndex, path: getDefaultBip32Path(DEFAULT_SCRIPT_TYPE, this.snapNetwork) }, errMsg: "Sign input failed" }); return bitcoin.Psbt.fromBase64(psbtBase64); } async getMasterFingerprint() { return await this.snapRequest({ method: "btc_getMasterFingerprint", errMsg: "Snap get master fingerprint failed" }); } async signPsbt(psbtHex, _psbtInputAccounts) { const psbt = bitcoin.Psbt.fromHex(psbtHex); const masterFingerprint = Buffer.from(await this.getMasterFingerprint(), "hex"); const publicKey = Buffer.from(this.publicKey, "hex"); const bip32Path = getDefaultBip32Path(DEFAULT_SCRIPT_TYPE, this.snapNetwork); psbt.data.inputs.forEach( (psbtInput) => psbtInput.bip32Derivation = [ { masterFingerprint, path: bip32Path, pubkey: publicKey } ] ); const psbtBase64 = await this.snapRequest({ method: "btc_signPsbt", params: { psbt: psbt.toBase64(), network: this.snapNetwork, scriptType: DEFAULT_SCRIPT_TYPE, opts: { autoFinalize: false } }, errMsg: "Could not sign psbt" }); return hex3.encode(base643.decode(psbtBase64)); } async getNetworkInSnap() { return await this.snapRequest({ method: "btc_network", params: { action: "get" }, errMsg: "Snap get network failed" }); } async setNetworkInSnap(expectedNetwork) { return await this.snapRequest({ method: "btc_network", params: { action: "set", network: expectedNetwork }, errMsg: "Snap set network failed" }); } async snapRequest(req) { try { return await window.ethereum.request({ method: "wallet_invokeSnap", params: { snapId, request: { method: req.method, params: req.params } } }); } catch (err) { const error = new SnapError((err == null ? void 0 : err.message) || req.errMsg); throw error; } } }; // src/connectors/okx.ts import { AddressType as AddressType3, Network as Network5 } from "bitcoin-address-validation"; // src/assets/okx.ts var okxLogo = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJDSURBVHgB7Zq9jtpAEMfHlhEgQLiioXEkoAGECwoKxMcTRHmC5E3IoyRPkPAEkI7unJYmTgEFTYwA8a3NTKScLnCHN6c9r1e3P2llWQy7M/s1Gv1twCP0ej37dDq9x+Zut1t3t9vZjDEHIiSRSPg4ZpDL5fxkMvn1cDh8m0wmfugfO53OoFQq/crn8wxfY9EymQyrVCqMfHvScZx1p9ls3pFxXBy/bKlUipGPrVbLuQqAfsCliq3zl0H84zwtjQrOw4Mt1W63P5LvBm2d+Xz+YzqdgkqUy+WgWCy+Mc/nc282m4FqLBYL+3g8fjDxenq72WxANZbLJeA13zDX67UDioL5ybXwafMYu64Ltn3bdDweQ5R97fd7GyhBQMipx4POeEDHIu2LfDdBIGGz+hJ9CQ1ABjoA2egAZPM6AgiCAEQhsi/C4jHyPA/6/f5NG3Ks2+3CYDC4aTccDrn6ojG54MnEvG00GoVmWLIRNZ7wTCwDHYBsdACy0QHIhiuRETxlICWpMMhGZHmqS8qH6JLyGegAZKMDkI0uKf8X4SWlaZo+Pp1bRrwlJU8ZKLIvUjKh0WiQ3sRUbNVq9c5Ebew7KEo2m/1p4jJ4qAmDaqDQBzj5XyiAT4VCQezJigAU+IDU+z8vJFnGWeC+bKQV/5VZ71FV6L7PA3gg3tXrdQ+DgLhC+75Wq3no69P3MC0NFQpx2lL04Ql9gHK1bRDjsSBIvScBnDTk1WrlGIZBorIDEYJj+rhdgnQ67VmWRe0zlplXl81vcyEt0rSoYDUAAAAASUVORK5CYII="; // src/connectors/okx.ts var getLibNetwork = (network) => { switch (network) { case "livenet": return Network5.mainnet; case "testnet": return Network5.testnet; } }; var OKXConnector = class extends SatsConnector { constructor(network) { super(network, "OKX Wallet", "OKX Wallet", "https://www.okx.com/web3", okxLogo); } async connect() { const network = await window.okxwallet.bitcoin.getNetwork(); const mappedNetwork = getLibNetwork(network); if (mappedNetwork !== this.network) { throw new Error(`Invalid Network. Please switch to Bitcoin ${this.network}.`); } const [accounts, publickKey] = await Promise.all([ window.okxwallet.bitcoin.requestAccounts(), window.okxwallet.bitcoin.getPublicKey() ]); this.paymentAddress = accounts[0]; this.ordinalsAddress = accounts[0]; this.publicKey = publickKey; window.okxwallet.bitcoin.on("accountChanged", this.changeAccount); } signMessage(message) { return window.okxwallet.bitcoin.signMessage(message); } on(callback) { window.okxwallet.bitcoin.on("accountChanged", ({ address, publicKey, compressedPublicKey }) => { callback(address); this.changeAccount({ address, publicKey, compressedPublicKey }); }); } removeListener() { } async changeAccount({ address, publicKey }) { this.paymentAddress = address; this.publicKey = publicKey; } async isReady() { var _a; this.ready = typeof ((_a = window.okxwallet) == null ? void 0 : _a.bitcoin) !== "undefined"; return this.ready; } async sendToAddress(toAddress, amount) { return window.okxwallet.bitcoin.sendBitcoin(toAddress, amount); } async signPsbt(psbtHex, psbtInputAccounts) { const publicKey = this.getPublicKey(); if (!publicKey) { throw new Error("Something went wrong while connecting"); } let inputs = []; for (const input of psbtInputAccounts) { for (const index of input.signingIndexes) { inputs.push(index); } } const addressType = this.getAddressType(this.paymentAddress); const toSignInputs = inputs.map((index) => { return { index, ...addressType === AddressType3.p2tr ? { address: this.paymentAddress, disableTweakSigner: false } : { publicKey, disableTweakSigner: true } }; }); const signedPsbtHex = await window.okxwallet.bitcoin.signPsbt(psbtHex, { autoFinalized: false, toSignInputs }); return signedPsbtHex; } }; // src/provider.tsx import { jsx } from "react/jsx-runtime"; var SatsWagmiContext = createContext({ connector: void 0, connectors: [], setConnector: () => { }, network: BitcoinNetwork.mainnet }); var useSatsWagmi = () => { const context = useContext(SatsWagmiContext); if (context === void 0) { throw new Error("useSatsWagmi must be used within a SatsWagmiConfig!"); } return context; }; var SatsWagmiConfig = ({ children, queryClient, network = BitcoinNetwork.mainnet }) => { const [connectors, setConnectors] = useState([]); const [connector, setCurrentConnector] = useState(); const [storedConnector, setStoredConnector] = useLocalStorage( "satsWagmiConnector" /* CONNECTOR */, void 0, { initializeWithValue: typeof window !== "undefined" } ); useEffect(() => { const init = () => { const readyConnectors = []; if (network === "mainnet") { const okx = new OKXConnector(network); readyConnectors.push(okx); } const xverse = new XverseConnector(network); readyConnectors.push(xverse); const unisat = new UnisatConnector(network, "unisat"); readyConnectors.push(unisat); const bitkeep = new UnisatConnector(network, "bitkeep"); readyConnectors.push(bitkeep); const binancew3w = new UnisatConnector(network, "binancew3w"); readyConnectors.push(binancew3w); const mmSnap = new MMSnapConnector(network); readyConnectors.push(mmSnap); const leather = new LeatherConnector(network); readyConnectors.push(leather); setConnectors(readyConnectors); }; init(); }, [network]); const setConnector = useCallback( (connector2) => { setCurrentConnector(connector2); setStoredConnector(connector2 == null ? void 0 : connector2.id); }, [setStoredConnector] ); useEffect(() => { const autoConnect = async () => { const connector2 = connectors.find((connector3) => connector3.id === storedConnector); if (!connector2) return; try { await connector2.connect(); setConnector(connector2); } catch (e) { setStoredConnector(void 0); } }; if (!connector && storedConnector && connectors.length) { autoConnect(); } }, [connectors]); return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(SatsWagmiContext.Provider, { value: { connectors, connector, setConnector, network }, children }) }); }; // src/hooks/useAccount.tsx import { useQuery } from "@tanstack/react-query"; import { getAddressInfo as getAddressInfo2 } from "bitcoin-address-validation"; import { useEffect as useEffect2 } from "react"; var useAccount = ({ onConnect } = {}) => { const { connector } = useSatsWagmi(); const { data, error, isError, isLoading, isSuccess, refetch } = useQuery({ queryKey: ["sats-account", connector], queryFn: () => { if (!connector) return void 0; const address = connector.getPaymentAddress(); onConnect == null ? void 0 : onConnect({ address, connector }); const publicKey = connector.getPublicKey(); const addressType = address ? getAddressInfo2(address).type : void 0; return { address, type: addressType, publicKey }; }, enabled: !!connector }); useEffect2(() => { if (!connector) return; connector.on(() => refetch()); return () => { connector.removeListener(() => refetch()); }; }, [connector, refetch]); return { connector, address: data == null ? void 0 : data.address, addressType: data == null ? void 0 : data.type, publicKey: data == null ? void 0 : data.publicKey, error, isError, isLoading, isSuccess, refetch }; }; // src/hooks/useBalance.tsx import { useQuery as useQuery2 } from "@tanstack/react-query"; import { getBalance } from "@gobob/bob-sdk"; var useBalance = (props = {}) => { const { network } = useSatsWagmi(); const { address } = useAccount(); return useQuery2({ enabled: Boolean(address), queryKey: ["sats-balance", network, address], refetchInterval: INTERVAL.SECONDS_30, queryFn: () => getBalance(address), ...props }); }; // src/hooks/useConnect.tsx import { useMutation } from "@tanstack/react-query"; var useConnect = () => { const { connectors, setConnector } = useSatsWagmi(); const { mutate, mutateAsync, ...query } = useMutation({ mutationKey: ["sats-connect"], mutationFn: async ({ connector }) => { if (!connector) { throw new Error("invalid connector id"); } if (connector.name !== "MetaMask" && !await connector.isReady()) { window.open(connector.homepage, "_blank", "noopener"); throw new Error("Wallet is not installed"); } await connector.connect(); return { address: connector.paymentAddress }; }, onSuccess: (_, { connector }) => { setConnector(connector); } }); return { ...query, connectors, connect: mutate, connectAsync: mutateAsync }; }; // src/hooks/useDisconnect.tsx import { useMutation as useMutation2 } from "@tanstack/react-query"; var useDisconnect = () => { const { setConnector } = useSatsWagmi(); const { mutate, mutateAsync, ...mutation } = useMutation2({ mutationKey: ["sats-disconnect"], mutationFn: async () => { setConnector(void 0); } }); return { ...mutation, disconnect: mutate, disconnectAsync: mutateAsync }; }; // src/hooks/useFeeEstimate.tsx import { estimateTxFee } from "@gobob/bob-sdk"; import { useQuery as useQuery4 } from "@tanstack/react-query"; // src/hooks/useFeeRate.tsx import { EsploraClient as EsploraClient2, MempoolClient } from "@gobob/bob-sdk"; import { useQuery as useQuery3 } from "@tanstack/react-query"; function useFeeRate({ query } = {}) { const { network } = useSatsWagmi(); return useQuery3({ queryKey: ["sats-fee-rate", network], queryFn: async () => { const memPoolClient = new MempoolClient(network); const esploraClient = new EsploraClient2(network); const [memPoolFeeRate, esploraFeeRate] = await Promise.all([ memPoolClient.getRecommendedFees(), esploraClient.getFeeEstimates() ]); return { memPool: memPoolFeeRate, esplora: esploraFeeRate }; }, refetchOnWindowFocus: false, refetchOnMount: false, refetchInterval: INTERVAL.MINUTE, ...query }); } // src/hooks/useFeeEstimate.tsx function useFeeEstimate({ amount, opReturnData, feeRate: feeRateProp, query } = {}) { const { address, publicKey } = useAccount(); const { data: feeRateData } = useFeeRate(); const { network } = useSatsWagmi(); const enabled = Boolean(feeRateData && address && ((query == null ? void 0 : query.enabled) !== void 0 ? query.enabled : true)); const feeRate = feeRateProp || (feeRateData == null ? void 0 : feeRateData.esplora[6]); return useQuery4({ queryKey: ["sats-fee-estimate", amount, address, opReturnData, network, feeRate], queryFn: async () => { if (!address || !feeRate) { throw new Error("Failed to estimate fee"); } return { feeRate, amount: await estimateTxFee(address, amount, publicKey, opReturnData, feeRate) }; }, refetchOnWindowFocus: false, refetchOnMount: false, refetchInterval: INTERVAL.MINUTE, ...query, enabled }); } // src/hooks/useSendGatewayTransaction.tsx import { useMutation as useMutation3 } from "@tanstack/react-query"; import { GatewaySDK } from "@gobob/bob-sdk"; var useSendGatewayTransaction = ({ gatewaySDK, toChain = "bob", ...props }) => { const { address: btcAddress, publicKey: btcPublicKey, connector } = useAccount(); const { mutate, mutateAsync, ...result } = useMutation3({ mutationKey: ["sats-send-gateway-transaction", btcAddress], mutationFn: async ({ toToken, evmAddress, value }) => { if (!connector) return void 0; if (!btcAddress) return void 0; const gatewayClient = gatewaySDK || new GatewaySDK(toChain); const params = { ...props, fromChain: props.fromChain || "bitcoin", fromToken: props.fromToken || "BTC", toChain, toToken, gasRefill: props.gasRefill || 2e3, fromUserAddress: btcAddress, fromUserPublicKey: btcPublicKey, toUserAddress: evmAddress, amount: Number(value) }; const quote = await gatewayClient.getQuote(params); const { uuid, psbtBase64 } = await gatewayClient.startOrder(quote, params); if (!psbtBase64) throw new Error("No psbt"); const bitcoinTxHex = await connector.signAllInputs(psbtBase64); return await gatewayClient.finalizeOrder(uuid, bitcoinTxHex); }, ...props }); return { ...result, sendGatewayTransaction: mutate, sendGatewayTransactionAsync: mutateAsync }; }; // src/hooks/useSendTransaction.tsx import { useMutation as useMutation4 } from "@tanstack/react-query"; var useSendTransaction = (props = {}) => { const { connector } = useSatsWagmi(); const { address } =