import { Hex, sha256, ripemd160, encodePacked } from "viem" import { BitcoinAddressHelper } from ".." import testData from "./data" describe("Bitcoin address helper", () => { describe("recoverTruncatedBitcoinAddress", () => { describe("from P2WPKH format", () => { it.each(testData.p2wpkhAddresses)( "should return correct hex for $compression $type", ({ address, publicKey }) => { const expected = ripemd160(sha256(publicKey as Hex)) expect( BitcoinAddressHelper.recoverTruncatedBitcoinAddress(address), ).toBe(expected) }, ) }) describe("from P2PKH format", () => { it.each(testData.p2pkhAddresses)( "should return correct hex for $compression $type", ({ address, publicKey }) => { const expected = ripemd160(sha256(publicKey as Hex)) expect( BitcoinAddressHelper.recoverTruncatedBitcoinAddress(address), ).toBe(expected) }, ) }) describe("from P2SH-P2WPKH format", () => { it.each(testData.nestedSegwitAddresses)( "should return correct hex for $compression $type", ({ address, publicKey }) => { const pubKeyHash = ripemd160(sha256(publicKey as Hex)) const expected = ripemd160( sha256(encodePacked(["bytes2", "bytes20"], ["0x0014", pubKeyHash])), ) expect( BitcoinAddressHelper.recoverTruncatedBitcoinAddress( address, publicKey, ), ).toBe(expected) }, ) }) describe("unsupported addresses", () => { it.each(testData.unsupportedP2TRAddresses)( "should throw an error for $type address", ({ address }) => { expect(() => { BitcoinAddressHelper.recoverTruncatedBitcoinAddress(address) }).toThrow("Unsupported address. Supported addresses: P2WPKH, P2PKH") }, ) }) }) describe("isP2PKHAddress", () => { const data = [ ...testData.p2pkhAddresses, ...testData.p2wpkhAddresses, ...testData.unsupportedP2TRAddresses, ...testData.unsupportedP2SHAddresses, ].map((address) => ({ ...address, expectedResult: address.type === "P2PKH", })) // TODO: Test fails for P2SH-P2WPKH address. It will be fixed once we merge // https://github.com/thesis/orangekit/pull/46. it.each(data)( "should return $expectedResult for an $type address", ({ address, expectedResult }) => { expect(BitcoinAddressHelper.isP2PKHAddress(address)).toBe( expectedResult, ) }, ) }) describe("isP2WPKHAddress", () => { const data = [ ...testData.p2pkhAddresses, ...testData.p2wpkhAddresses, ...testData.unsupportedP2TRAddresses, ...testData.unsupportedP2SHAddresses, ].map((address) => ({ ...address, expectedResult: address.type === "P2WPKH", })) it.each(data)( "should return $expectedResult for an $type address", ({ address, expectedResult }) => { expect(BitcoinAddressHelper.isP2WPKHAddress(address)).toBe( expectedResult, ) }, ) }) describe("toNestedSegwitAddress", () => { describe("for compressed public key", () => { describe("mainnet", () => { // Test data generated with https://www.btcschools.net/bitcoin/bitcoin_script_p2sh_p2wpkh.php const publicKey = "0385a365b47f35aff49fdf53cd0b31ab25dec1ef7a4ee3c9d3060ff298ba448d71" const expectedAddress = "3PqcLLkJ2CDrCXpNbfdmkkPN8PUibE9EVG" it("should generate desired base58 address", () => { expect(BitcoinAddressHelper.toNestedSegwitAddress(publicKey)).toBe( expectedAddress, ) }) }) describe("testnet", () => { // Test data generated with https://www.btcschools.net/bitcoin/bitcoin_script_p2sh_p2wpkh.php const publicKey = "020bbb301197f692bf757f57c1e7ac07db5e976247cdd3585b0f509f717afb8f98" const expectedAddress = "2MytdHfAAh7VbHr34Eyp63kWQ1LmTfHCiPm" it("should generate desired base58 address", () => { expect( BitcoinAddressHelper.toNestedSegwitAddress(publicKey, true), ).toBe(expectedAddress) }) }) }) describe("for uncompressed public key", () => { const publicKey = "04825e7980b7ff457d10d457e76f9d0e2a871643b451b372808d48ebfa37adb315a515182b757880a1ce177bfea25efc9174684141aed34513a5d72cbbae37ed6c" it("should throw an error", () => { expect(() => BitcoinAddressHelper.toNestedSegwitAddress(publicKey), ).toThrow(`The value [${publicKey}] is not a compressed public key`) }) }) }) })