diff --git a/packages/sdk/src/addresses/__tests__/index.spec.ts b/packages/sdk/src/addresses/__tests__/index.spec.ts index 3738d13d..18c1ebdf 100644 --- a/packages/sdk/src/addresses/__tests__/index.spec.ts +++ b/packages/sdk/src/addresses/__tests__/index.spec.ts @@ -8,6 +8,7 @@ describe("addresses", () => { const MAINNET = "mainnet"; const TESTNET = "testnet"; const REGTEST = "regtest"; + const SIGNET = "signet"; const INVALID_ADDRESS_ERROR = new OrditSDKError("Invalid address"); @@ -28,6 +29,14 @@ describe("addresses", () => { "tb1p3gqcaq2xs0qzm5wvkht64xppcz9h5k0q2q97kf6n80gu7v037dksatsuhz", p2wsh: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", }, + [SIGNET]: { + legacy: "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn", + "p2sh-p2wpkh": "2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc", + segwit: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", + taproot: + "tb1p3gqcaq2xs0qzm5wvkht64xppcz9h5k0q2q97kf6n80gu7v037dksatsuhz", + p2wsh: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + }, [REGTEST]: { legacy: "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn", "p2sh-p2wpkh": "2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc", @@ -106,6 +115,31 @@ describe("addresses", () => { ).not.toThrowError(INVALID_ADDRESS_ERROR); }); + test("should return correct address format for signet", () => { + const network = SIGNET; + expect(getAddressFormat(ADDRESSES[network].legacy, network)).toBe( + "legacy", + ); + expect(getAddressFormat(ADDRESSES[network]["p2sh-p2wpkh"], network)).toBe( + "p2sh-p2wpkh", + ); + expect(getAddressFormat(ADDRESSES[network].segwit, network)).toBe( + "segwit", + ); + expect(getAddressFormat(ADDRESSES[network].taproot, network)).toBe( + "taproot", + ); + expect(getAddressFormat(ADDRESSES[network].p2wsh, network)).toBe("p2wsh"); + + // non-bech32 addresses from regtest will work on testnet/signet + expect(() => + getAddressFormat(ADDRESSES[REGTEST].legacy, network), + ).not.toThrowError(INVALID_ADDRESS_ERROR); + expect(() => + getAddressFormat(ADDRESSES[REGTEST]["p2sh-p2wpkh"], network), + ).not.toThrowError(INVALID_ADDRESS_ERROR); + }); + test("should throw an error if address format is not recognised for mainnet", () => { expect(() => getAddressFormat("", MAINNET)).toThrowError( INVALID_ADDRESS_ERROR, diff --git a/packages/sdk/src/addresses/index.ts b/packages/sdk/src/addresses/index.ts index 4c1d7139..36a00c89 100644 --- a/packages/sdk/src/addresses/index.ts +++ b/packages/sdk/src/addresses/index.ts @@ -37,7 +37,12 @@ export function getAddressFormat( return getAddressFormatForRegTest(address); } - if (!validate(address, network as NetworkEnum)) { + if ( + !validate( + address, + (network === "signet" ? "testnet" : network) as NetworkEnum, + ) + ) { throw new OrditSDKError("Invalid address"); } diff --git a/packages/sdk/src/api/jsonrpc.ts b/packages/sdk/src/api/jsonrpc.ts index 1e9662a6..d42ea1af 100644 --- a/packages/sdk/src/api/jsonrpc.ts +++ b/packages/sdk/src/api/jsonrpc.ts @@ -115,5 +115,6 @@ export const rpc = { }, mainnet: new JsonRpc(getRpcUrl(API_CONFIG.apis.mainnet.batter)), testnet: new JsonRpc(getRpcUrl(API_CONFIG.apis.testnet.batter)), + signet: new JsonRpc(getRpcUrl(API_CONFIG.apis.signet.batter)), regtest: new JsonRpc(getRpcUrl(API_CONFIG.apis.regtest.batter)), } as const; diff --git a/packages/sdk/src/browser-wallets/internal/sats-connect/constants.ts b/packages/sdk/src/browser-wallets/internal/sats-connect/constants.ts index 22d0744c..946a9af9 100644 --- a/packages/sdk/src/browser-wallets/internal/sats-connect/constants.ts +++ b/packages/sdk/src/browser-wallets/internal/sats-connect/constants.ts @@ -3,7 +3,7 @@ import { BitcoinNetworkType } from "sats-connect"; import type { BrowserWalletNetwork } from "../../../config/types"; export const NETWORK_TO_BITCOIN_NETWORK_TYPE: Record< - BrowserWalletNetwork, + Extract, BitcoinNetworkType > = { mainnet: BitcoinNetworkType.Mainnet, diff --git a/packages/sdk/src/browser-wallets/internal/sats-connect/index.ts b/packages/sdk/src/browser-wallets/internal/sats-connect/index.ts index 7eebf0af..05e3b49c 100644 --- a/packages/sdk/src/browser-wallets/internal/sats-connect/index.ts +++ b/packages/sdk/src/browser-wallets/internal/sats-connect/index.ts @@ -45,6 +45,10 @@ async function satsConnectWalletGetAddresses( getProvider: () => Promise, network: BrowserWalletNetwork = "mainnet", ): Promise { + if (network === "signet") { + throw new OrditSDKError("signet network is not supported"); + } + const result: WalletAddress[] = []; const handleOnFinish = (response: GetAddressResponse) => { @@ -116,6 +120,9 @@ async function satsConnectWalletSignPsbt( inputsToSign, }: SatsConnectSignPSBTOptions = { network: "mainnet", inputsToSign: [] }, ): Promise { + if (network === "signet") { + throw new OrditSDKError("signet network is not supported"); + } if (!finalize && extractTx) { throw new BrowserWalletExtractTxFromNonFinalizedPsbtError(); } @@ -213,6 +220,9 @@ async function satsConnectWalletSignMessage( address: string, network: BrowserWalletNetwork = "mainnet", ): Promise { + if (network === "signet") { + throw new OrditSDKError("signet network is not supported"); + } if (!message || !network || !address) { throw new OrditSDKError("Invalid options provided"); } diff --git a/packages/sdk/src/browser-wallets/leather/__tests__/index.spec.ts b/packages/sdk/src/browser-wallets/leather/__tests__/index.spec.ts index ff25d893..cc42cb4a 100644 --- a/packages/sdk/src/browser-wallets/leather/__tests__/index.spec.ts +++ b/packages/sdk/src/browser-wallets/leather/__tests__/index.spec.ts @@ -146,6 +146,62 @@ describe("Leather Wallet", () => { expect(getAddresses(network)).resolves.toEqual(resolvedValue); }); + test("should return address from signet", () => { + const mockData = { + jsonrpc: "2.0", + id: "20b0fcf3-ad9a-4f32-8d7f-b6db6fb96207", + result: { + addresses: [ + { + symbol: "BTC", + type: "p2wpkh", + address: "tb1qy89jvaya2dzfuagxl7h59pytjs74kxudslux25", + publicKey: + "0238427342c868536e9c181caf674d242ab8a3e381ee36692ba375e0b315234180", + derivationPath: "m/84'/1'/0'/0/0", + }, + { + symbol: "BTC", + type: "p2tr", + address: + "tb1pgftv8z8re72kttnyt85k2ae6andngar4c59hasv2kp04kgmmgnnqmxg8uy", + publicKey: + "023ded01d3e800b07278c5bbc5deee1f3493ebd599ae843936d81a42625a5cfb84", + tweakedPublicKey: + "3ded01d3e800b07278c5bbc5deee1f3493ebd599ae843936d81a42625a5cfb84", + derivationPath: "m/86'/1'/0'/0/0", + }, + { + symbol: "STX", + address: "ST3TJ35J2T3NCAD471DCN2MB0502Z2TVNPZHN1NEG", + }, + ], + }, + }; + const network = "signet"; + + const resolvedValue = [ + { + publicKey: + "0238427342c868536e9c181caf674d242ab8a3e381ee36692ba375e0b315234180", + address: "tb1qy89jvaya2dzfuagxl7h59pytjs74kxudslux25", + format: "segwit", + }, + { + publicKey: + "023ded01d3e800b07278c5bbc5deee1f3493ebd599ae843936d81a42625a5cfb84", + address: + "tb1pgftv8z8re72kttnyt85k2ae6andngar4c59hasv2kp04kgmmgnnqmxg8uy", + format: "taproot", + }, + ]; + + vi.stubGlobal("LeatherProvider", { + request: vi.fn().mockResolvedValue(mockData), + }); + expect(getAddresses(network)).resolves.toEqual(resolvedValue); + }); + test("should return error when the network mismatch", () => { const mockData = { jsonrpc: "2.0", diff --git a/packages/sdk/src/browser-wallets/leather/index.ts b/packages/sdk/src/browser-wallets/leather/index.ts index 42489e63..06cdd69a 100644 --- a/packages/sdk/src/browser-wallets/leather/index.ts +++ b/packages/sdk/src/browser-wallets/leather/index.ts @@ -47,7 +47,12 @@ async function getAddresses( // Hacky validation: there's no parameter to specify the network value when getting the address. // TODO: Remove this if the wallet already supports that parameter. - if (getNetworkByAddress(addresses[0].address) !== network) { + const derivedNetwork = getNetworkByAddress(addresses[0].address); + + if ( + (network !== "signet" && derivedNetwork !== network) || + (network === "signet" && derivedNetwork !== "testnet") // error if network is signet but derived network is not testnet (signet has same address format as testnet) + ) { throw new BrowserWalletNetworkMismatchError( "Leather network mismatch, please switch it manually", ); diff --git a/packages/sdk/src/browser-wallets/okx/__tests__/index.spec.ts b/packages/sdk/src/browser-wallets/okx/__tests__/index.spec.ts index ff57ff57..ac7928e3 100644 --- a/packages/sdk/src/browser-wallets/okx/__tests__/index.spec.ts +++ b/packages/sdk/src/browser-wallets/okx/__tests__/index.spec.ts @@ -81,6 +81,28 @@ describe("OKX Wallet", () => { expect(getAddresses(network)).resolves.toEqual([mockData]); }); + test("should return address from signet", () => { + const mockData: WalletAddress = { + publicKey: + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + address: "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", + format: "segwit", + }; + const mockXOnlyPubKey = + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; + const network = "signet"; + + vi.stubGlobal("okxwallet", { + bitcoinSignet: { + connect: vi.fn().mockResolvedValue({ + address: mockData.address, + publicKey: mockXOnlyPubKey, + }), + }, + }); + expect(getAddresses(network)).resolves.toEqual([mockData]); + }); + test("should throw error when user rejects or cancels request", () => { const mockData: WalletAddress = { publicKey: diff --git a/packages/sdk/src/browser-wallets/okx/index.ts b/packages/sdk/src/browser-wallets/okx/index.ts index 35ca6d12..11df3cb9 100644 --- a/packages/sdk/src/browser-wallets/okx/index.ts +++ b/packages/sdk/src/browser-wallets/okx/index.ts @@ -29,16 +29,16 @@ function getOKXWalletProvider( throw new BrowserWalletNotInstalledError("OKX Wallet not installed."); } - const provider = - network === "mainnet" - ? window.okxwallet.bitcoin - : window.okxwallet.bitcoinTestnet; - - if (!provider) { - throw new OrditSDKError("Failed to get OKX Wallet provider."); + switch (network) { + case "mainnet": + return window.okxwallet.bitcoin; + case "testnet": + return window.okxwallet.bitcoinTestnet; + case "signet": + return window.okxwallet.bitcoinSignet; + default: + throw new OrditSDKError("Failed to get OKX Wallet provider."); } - - return provider; } async function getAddresses( diff --git a/packages/sdk/src/browser-wallets/unisat/__tests__/index.spec.ts b/packages/sdk/src/browser-wallets/unisat/__tests__/index.spec.ts index 11cdd7cc..247a152f 100644 --- a/packages/sdk/src/browser-wallets/unisat/__tests__/index.spec.ts +++ b/packages/sdk/src/browser-wallets/unisat/__tests__/index.spec.ts @@ -76,6 +76,12 @@ describe("Unisat Wallet", () => { expect(getAddresses("testnet")).resolves.toEqual([mockData]); }); + test("should return error from signet", () => { + expect(getAddresses("signet")).rejects.toThrowError( + "signet network is not supported", + ); + }); + test("should throw error when user rejects or cancels request", () => { const mockData: WalletAddress = { publicKey: diff --git a/packages/sdk/src/browser-wallets/unisat/constants.ts b/packages/sdk/src/browser-wallets/unisat/constants.ts index e9986a09..08327b4d 100644 --- a/packages/sdk/src/browser-wallets/unisat/constants.ts +++ b/packages/sdk/src/browser-wallets/unisat/constants.ts @@ -1,7 +1,7 @@ import type { BrowserWalletNetwork } from "../../config/types"; export const NETWORK_TO_UNISAT_NETWORK: Record< - BrowserWalletNetwork, + Extract, UnisatNetwork > = { mainnet: "livenet", diff --git a/packages/sdk/src/browser-wallets/unisat/index.ts b/packages/sdk/src/browser-wallets/unisat/index.ts index d01f772c..7ac01c8e 100644 --- a/packages/sdk/src/browser-wallets/unisat/index.ts +++ b/packages/sdk/src/browser-wallets/unisat/index.ts @@ -42,6 +42,9 @@ async function getAddresses( network: BrowserWalletNetwork = "mainnet", readOnly?: boolean, ): Promise { + if (network === "signet") { + throw new OrditSDKError("signet network is not supported"); + } if (!isInstalled()) { throw new BrowserWalletNotInstalledError("Unisat not installed"); } diff --git a/packages/sdk/src/browser-wallets/xverse/__tests__/index.spec.ts b/packages/sdk/src/browser-wallets/xverse/__tests__/index.spec.ts index 3f34d5ca..00db15cb 100644 --- a/packages/sdk/src/browser-wallets/xverse/__tests__/index.spec.ts +++ b/packages/sdk/src/browser-wallets/xverse/__tests__/index.spec.ts @@ -155,6 +155,17 @@ describe("Xverse Wallet", () => { expect(getAddresses("testnet")).resolves.toEqual(mockData); }); + test("should throw error on signet", () => { + vi.stubGlobal("window", { + XverseProviders: { + BitcoinProvider: {}, + }, + }); + expect(getAddresses("signet")).rejects.toThrowError( + "signet network is not supported", + ); + }); + test("should throw error on user cancel", () => { vi.stubGlobal("window", { XverseProviders: { diff --git a/packages/sdk/src/config/index.ts b/packages/sdk/src/config/index.ts index 6875ba16..7f8e3f0e 100644 --- a/packages/sdk/src/config/index.ts +++ b/packages/sdk/src/config/index.ts @@ -10,5 +10,8 @@ export const API_CONFIG = { testnet: { batter: "https://testnet.ordit.io/", }, + signet: { + batter: "https://signet.ordit.io/", + }, }, }; diff --git a/packages/sdk/src/config/types.ts b/packages/sdk/src/config/types.ts index 2f13b94b..2db17fda 100644 --- a/packages/sdk/src/config/types.ts +++ b/packages/sdk/src/config/types.ts @@ -1,3 +1,6 @@ -export type Network = "mainnet" | "testnet" | "regtest"; +export type Network = "mainnet" | "testnet" | "regtest" | "signet"; -export type BrowserWalletNetwork = Extract; +export type BrowserWalletNetwork = Extract< + Network, + "mainnet" | "testnet" | "signet" +>; diff --git a/packages/sdk/src/transactions/__tests__/PSBTBuilder.spec.ts b/packages/sdk/src/transactions/__tests__/PSBTBuilder.spec.ts index 74eb7a22..07f33fe9 100644 --- a/packages/sdk/src/transactions/__tests__/PSBTBuilder.spec.ts +++ b/packages/sdk/src/transactions/__tests__/PSBTBuilder.spec.ts @@ -86,6 +86,7 @@ describe("PSBTBuilder", () => { mainnet: mockJsonRpc, testnet: mockJsonRpc, regtest: mockJsonRpc, + signet: mockJsonRpc, }; afterAll(() => { vi.resetAllMocks(); diff --git a/packages/sdk/src/types.d.ts b/packages/sdk/src/types.d.ts index 12231fc4..a288a5fd 100644 --- a/packages/sdk/src/types.d.ts +++ b/packages/sdk/src/types.d.ts @@ -67,6 +67,7 @@ type OKXWalletProvider = { type OKXWallet = { bitcoin: OKXWalletProvider; bitcoinTestnet: OKXWalletProvider; + bitcoinSignet: OKXWalletProvider; }; declare module "buffer-reverse" { diff --git a/packages/sdk/src/utils/index.ts b/packages/sdk/src/utils/index.ts index 698a0162..da3d35b2 100644 --- a/packages/sdk/src/utils/index.ts +++ b/packages/sdk/src/utils/index.ts @@ -33,6 +33,9 @@ export function getNetwork(value: Network) { if (value === "mainnet") { return networks.bitcoin; } + if (value === "signet") { + return networks.testnet; + } return networks[value]; }