From 54796fba346f6c71fb0e80d6cd477d7fbbbc02e1 Mon Sep 17 00:00:00 2001 From: Hanssen0 Date: Sat, 24 Aug 2024 22:18:51 +0800 Subject: [PATCH] feat(UTXO Global): network switching --- packages/ccc/src/signersController.ts | 2 +- packages/core/src/signer/signer/index.ts | 6 +- packages/utxo-global/src/advancedBarrel.ts | 8 ++- packages/utxo-global/src/btc/index.ts | 79 ++++++++++++++++++++++ packages/utxo-global/src/ckb/index.ts | 31 +++++++++ packages/utxo-global/src/signersFactory.ts | 11 ++- 6 files changed, 130 insertions(+), 7 deletions(-) diff --git a/packages/ccc/src/signersController.ts b/packages/ccc/src/signersController.ts index a2031be5..ea2b34ca 100644 --- a/packages/ccc/src/signersController.ts +++ b/packages/ccc/src/signersController.ts @@ -92,7 +92,7 @@ export class SignersController { wallets, "UTXO Global Wallet", UTXO_GLOBAL_SVG, - UtxoGlobal.getUtxoGlobalSigners(client), + UtxoGlobal.getUtxoGlobalSigners(client, preferredNetworks), onUpdate, configs, ); diff --git a/packages/core/src/signer/signer/index.ts b/packages/core/src/signer/signer/index.ts index f2f9369e..b0e9f19a 100644 --- a/packages/core/src/signer/signer/index.ts +++ b/packages/core/src/signer/signer/index.ts @@ -46,7 +46,8 @@ export type NetworkPreference = { * BTC: // They made a mess... * btc * btcTestnet - * btcSignet // OKX + * btcTestnet4 // UTXO Global + * btcSignet // OKX & UTXO Global * fractalBtc // UniSat */ network: string; @@ -82,9 +83,10 @@ export abstract class Signer { // undefined otherwise matchNetworkPreference( preferences: NetworkPreference[], - currentNetwork: string, + currentNetwork: string | undefined, ): NetworkPreference | undefined { if ( + currentNetwork !== undefined && preferences.some(({ signerType, addressPrefix, network }) => { signerType === this.type && addressPrefix === this.client.addressPrefix && diff --git a/packages/utxo-global/src/advancedBarrel.ts b/packages/utxo-global/src/advancedBarrel.ts index 073cd9e4..679f8633 100644 --- a/packages/utxo-global/src/advancedBarrel.ts +++ b/packages/utxo-global/src/advancedBarrel.ts @@ -1,3 +1,5 @@ +import { ccc } from "@ckb-ccc/core"; + export interface Provider { requestAccounts(): Promise; getAccount(): Promise; @@ -5,8 +7,10 @@ export interface Provider { connect(): Promise; isConnected(): Promise; signMessage(msg: string, address: string): Promise; - createTx(tx: any): Promise; - signTransaction(tx: any): Promise; + signTransaction(tx: ccc.TransactionLike): Promise; + + getNetwork(): Promise; + switchNetwork(network: string): Promise; on: OnMethod; removeListener( diff --git a/packages/utxo-global/src/btc/index.ts b/packages/utxo-global/src/btc/index.ts index 65a248ab..57e73594 100644 --- a/packages/utxo-global/src/btc/index.ts +++ b/packages/utxo-global/src/btc/index.ts @@ -10,6 +10,18 @@ export class SignerBtc extends ccc.SignerBtc { constructor( client: ccc.Client, public readonly provider: Provider, + private readonly preferredNetworks: ccc.NetworkPreference[] = [ + { + addressPrefix: "ckb", + signerType: ccc.SignerType.BTC, + network: "btc", + }, + { + addressPrefix: "ckt", + signerType: ccc.SignerType.BTC, + network: "btcTestnet", + }, + ], ) { super(client); } @@ -32,11 +44,78 @@ export class SignerBtc extends ccc.SignerBtc { return ccc.hexFrom(pubKey.publicKey); } + /** + * Ensure the BTC network is the same as CKB network. + */ + async ensureNetwork(): Promise { + const network = await this._getNetworkToChange(); + if (!network) { + return; + } + + const chain = { + btc: "btc", + btcTestnet: "btc_testnet", + btcTestnet4: "btc_testnet_4", + btcSignet: "btc_signet", + }[network]; + + if (chain) { + await this.provider.switchNetwork(chain); + return; + } + + throw new Error( + `UTXO Global wallet doesn't support the requested chain ${network}`, + ); + } + + async _getNetworkToChange(): Promise { + const currentNetwork = { + btc: "btc", + btc_testnet: "btcTestnet", + btc_testnet_4: "btcTestnet4", + btc_signet: "btcSignet", + }[await this.provider.getNetwork()]; + + const { network } = this.matchNetworkPreference( + this.preferredNetworks, + currentNetwork, + ) ?? { network: currentNetwork }; + if (network === currentNetwork) { + return; + } + + return network; + } + + 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]; + } + async connect(): Promise { await this.provider.connect(); + await this.ensureNetwork(); } async isConnected(): Promise { + if ((await this._getNetworkToChange()) !== undefined) { + return false; + } + return await this.provider.isConnected(); } diff --git a/packages/utxo-global/src/ckb/index.ts b/packages/utxo-global/src/ckb/index.ts index 7a8e30e2..8755fe97 100644 --- a/packages/utxo-global/src/ckb/index.ts +++ b/packages/utxo-global/src/ckb/index.ts @@ -60,11 +60,42 @@ export class SignerCkb extends ccc.Signer { return ccc.hexFrom(pubKey.publicKey); } + get ckbNetwork(): string { + return this.client.addressPrefix === "ckb" ? "nervos" : "nervos_testnet"; + } + async connect(): Promise { await this.provider.connect(); + + if (this.ckbNetwork === (await this.provider.getNetwork())) { + return; + } + + await this.provider.switchNetwork(this.ckbNetwork); + } + + 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]; } async isConnected(): Promise { + if ((await this.provider.getNetwork()) !== this.ckbNetwork) { + return false; + } + return await this.provider.isConnected(); } diff --git a/packages/utxo-global/src/signersFactory.ts b/packages/utxo-global/src/signersFactory.ts index ab2a25ed..120ceeac 100644 --- a/packages/utxo-global/src/signersFactory.ts +++ b/packages/utxo-global/src/signersFactory.ts @@ -6,7 +6,10 @@ import { SignerCkb } from "./ckb/index.js"; /** * @public */ -export function getUtxoGlobalSigners(client: ccc.Client): ccc.SignerInfo[] { +export function getUtxoGlobalSigners( + client: ccc.Client, + preferredNetworks?: ccc.NetworkPreference[], +): ccc.SignerInfo[] { const windowRef = window as { utxoGlobal?: { bitcoinSigner: Provider; @@ -25,7 +28,11 @@ export function getUtxoGlobalSigners(client: ccc.Client): ccc.SignerInfo[] { }, { name: "BTC", - signer: new SignerBtc(client, windowRef.utxoGlobal.bitcoinSigner), + signer: new SignerBtc( + client, + windowRef.utxoGlobal.bitcoinSigner, + preferredNetworks, + ), }, ]; }