import { ccc } from "@ckb-ccc/core"; import { Provider } from "./advancedBarrel.js"; /** * Class representing a Bitcoin signer that extends SignerBtc * @public */ export class Signer extends ccc.SignerBtc { /** * Creates an instance of Signer. * @param client - The client instance. * @param provider - The provider instance. */ constructor( client: ccc.Client, public readonly provider: Provider, _preferredNetworks?: ccc.NetworkPreference[], public readonly network = "btc", ) { super(client); } async _getNetworkToChange(): Promise { const currentNetwork = await (async () => { if (this.provider.getChain) { return ( { BITCOIN_MAINNET: "btc", BITCOIN_TESTNET: "btcTestnet", BITCOIN_TESTNET4: "btcTestnet4", BITCOIN_SIGNET: "btcSignet", FRACTAL_BITCOIN_MAINNET: "fractalBtc", FRACTAL_BITCOIN_TESTNET: "fractalBtcTestnet", }[(await this.provider.getChain()).enum] ?? "" ); } return (await this.provider.getNetwork()) === "livenet" ? "btc" : "btcTestnet"; })(); if (this.network === currentNetwork) { return; } return this.network; } /** * Ensure the BTC network is the same as CKB network. */ async ensureNetwork(): Promise { const network = await this._getNetworkToChange(); if (!network) { return; } if (this.provider.switchChain) { const chain = { btc: "BITCOIN_MAINNET", btcTestnet: "BITCOIN_TESTNET", btcTestnet4: "BITCOIN_TESTNET4", btcSignet: "BITCOIN_SIGNET", fractalBtc: "FRACTAL_BITCOIN_MAINNET", fractalBtcTestnet: "FRACTAL_BITCOIN_TESTNET", }[network]; if (chain) { await this.provider.switchChain(chain); return; } } else if (network === "btc" || network === "btcTestnet") { await this.provider.switchNetwork( network === "btc" ? "livenet" : "testnet", ); return; } throw new Error( `UniSat wallet doesn't support the requested chain ${network}`, ); } /** * Gets the Bitcoin account address. * @returns A promise that resolves to the Bitcoin account address. */ async getBtcAccount(): Promise { return (await this.provider.getAccounts())[0]; } /** * Gets the Bitcoin public key. * @returns A promise that resolves to the Bitcoin public key. */ async getBtcPublicKey(): Promise { return ccc.hexFrom(await this.provider.getPublicKey()); } /** * Connects to the provider by requesting accounts. * @returns A promise that resolves when the connection is established. */ async connect(): Promise { await this.provider.requestAccounts(); await this.ensureNetwork(); } onReplaced(listener: () => void): () => void { const stop: (() => void)[] = []; const replacer = async () => { listener(); stop[0]?.(); }; stop.push(() => { this.provider.removeListener("accountsChanged", replacer); this.provider.removeListener("networkChanged", replacer); }); this.provider.on("accountsChanged", replacer); this.provider.on("networkChanged", replacer); return stop[0]; } /** * Checks if the signer is connected. * @returns A promise that resolves to true if connected, false otherwise. */ async isConnected(): Promise { if (await this._getNetworkToChange()) { return false; } return (await this.provider.getAccounts()).length !== 0; } /** * Signs a raw message with the Bitcoin account. * @param message - The message to sign. * @returns A promise that resolves to the signed message. */ async signMessageRaw(message: string | ccc.BytesLike): Promise { const challenge = typeof message === "string" ? message : ccc.hexFrom(message).slice(2); return this.provider.signMessage(challenge, "ecdsa"); } /** * Signs a PSBT using UniSat wallet. * * @param psbtHex - The hex string of PSBT to sign. * @param options - Options for signing the PSBT * @returns A promise that resolves to the signed PSBT as a Hex string */ async signPsbt( psbtHex: ccc.HexLike, options?: ccc.SignPsbtOptionsLike, ): Promise { return ccc.hexFrom( await this.provider.signPsbt(ccc.hexFrom(psbtHex).slice(2), options), ); } /** * Broadcasts a signed PSBT to the Bitcoin network. * * @param psbtHex - The hex string of signed PSBT to broadcast. * @returns A promise that resolves to the transaction ID as a Hex string */ async broadcastPsbt( psbtHex: ccc.HexLike, _options?: ccc.SignPsbtOptionsLike, ): Promise { const txid = await this.provider.pushPsbt(ccc.hexFrom(psbtHex).slice(2)); return ccc.hexFrom(txid); } }