diff --git a/packages/primitives/src/block-header.ts b/packages/primitives/src/block-header.ts index 609116a..1f69789 100644 --- a/packages/primitives/src/block-header.ts +++ b/packages/primitives/src/block-header.ts @@ -23,15 +23,18 @@ import { hash256 } from '@bitauth/libauth'; * ``` */ export class BlockHeader { + protected readonly bytes: Bytes; + // View of our data so that we can extract as numbers. - private view: DataView; + protected readonly view: DataView; /** * Creates a new BlockHeader instance. * @param bytes The bytes representing our Block Header (Big Endian) */ - protected constructor(private readonly bytes: Uint8Array) { - this.view = new DataView(bytes.buffer); + protected constructor(bytes: Bytes | Uint8Array) { + this.bytes = Bytes.from(bytes); + this.view = new DataView(this.bytes.buffer); } /** @@ -58,7 +61,7 @@ export class BlockHeader { * @returns A new BlockHeader instance * @throws Error if the byte array length is not 80 */ - static fromBytes(bytes: Uint8Array): BlockHeader { + static fromBytes(bytes: Bytes | Uint8Array): BlockHeader { if (bytes.length !== 80) { throw new Error('Invalid block header length'); } @@ -134,7 +137,7 @@ export class BlockHeader { * Serializes the block header to a byte array. * @returns 80-byte array containing the serialized block header */ - toBytes(): Uint8Array { + toBytes(): Bytes { return this.bytes; } diff --git a/packages/primitives/src/hd-private-node.ts b/packages/primitives/src/hd-private-node.ts index 891a6de..ff6fd48 100644 --- a/packages/primitives/src/hd-private-node.ts +++ b/packages/primitives/src/hd-private-node.ts @@ -176,9 +176,9 @@ export class HDPrivateNode { * * @returns The Node's Private Key (as a Uint8Array). */ - public toBytes(): Uint8Array { + public toBytes(): Bytes { // Encode the XPriv from our node info. - return this.node.privateKey; + return Bytes.from(this.node.privateKey); } /** diff --git a/packages/primitives/src/private-key.ts b/packages/primitives/src/private-key.ts index fc89624..2123011 100644 --- a/packages/primitives/src/private-key.ts +++ b/packages/primitives/src/private-key.ts @@ -33,16 +33,16 @@ import { */ export class PrivateKey { // The raw bytes representing the Private Key. - protected readonly bytes: Uint8Array; + protected readonly bytes: Bytes; /** * Construct a new Private Key. * * @throws {Error} If Private Key cannot be constructed. * - * @param bytes {Uint8Array} The raw bytes (32) for the Private Key. + * @param bytes The raw bytes (32) for the Private Key. */ - protected constructor(bytes: Uint8Array) { + protected constructor(bytes: Bytes | Uint8Array) { // Ensure that the Private Key is valid. if (!secp256k1.validatePrivateKey(bytes)) { throw new Error( @@ -50,7 +50,7 @@ export class PrivateKey { ); } - this.bytes = bytes; + this.bytes = Bytes.from(bytes); } /** @@ -62,7 +62,7 @@ export class PrivateKey { * * @returns The created Private Key Primitive. */ - public static from(privateKey: Uint8Array | string) { + public static from(privateKey: Bytes | Uint8Array | string) { if (privateKey instanceof Uint8Array) { return this.fromBytes(privateKey); } @@ -83,7 +83,7 @@ export class PrivateKey { * * @returns {PrivateKey} The created Private Key Entity. */ - public static fromBytes(privateKeyBytes: Uint8Array): PrivateKey { + public static fromBytes(privateKeyBytes: Bytes | Uint8Array): PrivateKey { return new this(privateKeyBytes); } @@ -194,9 +194,9 @@ export class PrivateKey { /** * Convert this Private Key to a raw 32 byte representation. * - * @returns {Uint8Array} The raw Private Key as a Uint8Array. + * @returns The raw Private Key as a Uint8Array. */ - public toBytes(): Uint8Array { + public toBytes(): Bytes { // Return the raw bytes for this Private Key. return this.bytes; } diff --git a/packages/primitives/src/public-key.ts b/packages/primitives/src/public-key.ts index 7d74b3f..81c09d5 100644 --- a/packages/primitives/src/public-key.ts +++ b/packages/primitives/src/public-key.ts @@ -24,15 +24,15 @@ import { secp256k1, sha256, ripemd160, utf8ToBin } from '@bitauth/libauth'; */ export class PublicKey { // The raw bytes representing the Public Key. - protected readonly bytes: Uint8Array; + protected readonly bytes: Bytes; /** * Construct a new Public Key. * * @param bytes The raw bytes (33) for the Public Key. */ - protected constructor(bytes: Uint8Array) { - this.bytes = bytes; + protected constructor(bytes: Bytes | Uint8Array) { + this.bytes = Bytes.from(bytes); } /** @@ -44,7 +44,7 @@ export class PublicKey { * * @returns The created Public Key Primitive. */ - public static from(publicKey: Uint8Array | string) { + public static from(publicKey: Bytes | Uint8Array | string) { return publicKey instanceof Uint8Array ? this.fromBytes(publicKey) : this.fromHex(publicKey); @@ -59,7 +59,7 @@ export class PublicKey { * * @returns The created Public Key entity. */ - public static fromBytes(publicKeyBytes: Uint8Array): PublicKey { + public static fromBytes(publicKeyBytes: Bytes | Uint8Array): PublicKey { // Ensure that exactly 33 Bytes are given. if (!secp256k1.validatePublicKey(publicKeyBytes)) { throw new Error(`Invalid Public Key.`); @@ -90,7 +90,7 @@ export class PublicKey { * * @returns A new instance of this primitive. */ - public static fromRaw(bytes: Uint8Array) { + public static fromRaw(bytes: Bytes | Uint8Array) { return new this(bytes); } @@ -108,7 +108,7 @@ export class PublicKey { * * @returns The raw bytes of the Public Key. */ - public toBytes(): Uint8Array { + public toBytes(): Bytes { // Return the raw bytes for this Public Key. return this.bytes; } diff --git a/packages/primitives/src/script.ts b/packages/primitives/src/script.ts index 34e5152..f7794bc 100644 --- a/packages/primitives/src/script.ts +++ b/packages/primitives/src/script.ts @@ -28,15 +28,15 @@ import { * ``` */ export class Script { - protected readonly bytes: Uint8Array; + protected readonly bytes: Bytes; /** * Construct a new Script * * @param bytes {Uint8Array} The raw bytes of the Script. */ - protected constructor(bytes: Uint8Array) { - this.bytes = bytes; + protected constructor(bytes: Bytes | Uint8Array) { + this.bytes = Bytes.from(bytes); } /** @@ -48,7 +48,7 @@ export class Script { * * @returns The created Script Primitive. */ - public static from(script: Uint8Array | string) { + public static from(script: Bytes | Uint8Array | string) { return script instanceof Uint8Array ? this.fromBytes(script) : this.fromHex(script); @@ -63,7 +63,7 @@ export class Script { * * @returns {Script} The created Script Entity. */ - public static fromBytes(bytes: Uint8Array): Script { + public static fromBytes(bytes: Bytes | Uint8Array): Script { // Disassemble the bytecode. const disassembledBytecode = disassembleBytecodeBCH(bytes); @@ -213,7 +213,7 @@ export class Script { * * @returns {Uint8Array} The compiled bytes. */ - public toBytes(): Uint8Array { + public toBytes(): Bytes { return this.bytes; } diff --git a/packages/primitives/tests/block-header.spec.ts b/packages/primitives/tests/block-header.spec.ts index 6b230b7..1374c99 100644 --- a/packages/primitives/tests/block-header.spec.ts +++ b/packages/primitives/tests/block-header.spec.ts @@ -5,9 +5,9 @@ import { Bytes } from '../src/bytes'; describe('Success cases', () => { // Test cases with different block header representations type BlockHeaderCase = { - raw: Uint8Array; + raw: Bytes; representations: { - bytes: Uint8Array; + bytes: Bytes; hex: string; }; }; @@ -16,11 +16,11 @@ describe('Success cases', () => { { raw: Bytes.fromHex( '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d', - ).toUint8Array(), + ), representations: { bytes: Bytes.fromHex( '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d', - ).toUint8Array(), + ), hex: '0000602093ea70b294aeef69db85981bd387285824fc5af732e487010000000000000000135576e1ba9cae5509d60b4fbb9e66612f35b5ee8db264cb12612d46cc8db60b5e34a164d46b021864abbb1d', }, }, @@ -35,19 +35,19 @@ describe('Success cases', () => { fromMethods.forEach(({ name, key }) => { it.each(testCases)(`${name}()`, ({ raw, representations }) => { const blockHeader = BlockHeader[name](representations[key]); - expect(blockHeader.toRaw()).toStrictEqual(raw); + expect(blockHeader.toRaw()).toEqual(raw); }); }); // Test generic from method it.each(testCases)('from(Uint8Array)', ({ raw, representations }) => { const blockHeader = BlockHeader.from(representations.bytes); - expect(blockHeader.toRaw()).toStrictEqual(raw); + expect(blockHeader.toRaw()).toEqual(raw); }); it.each(testCases)('from(string)', ({ raw, representations }) => { const blockHeader = BlockHeader.from(representations.hex); - expect(blockHeader.toRaw()).toStrictEqual(raw); + expect(blockHeader.toRaw()).toEqual(raw); }); // Test to methods with their respective types diff --git a/packages/primitives/tests/script.spec.ts b/packages/primitives/tests/script.spec.ts index 7c591f6..8ea2703 100644 --- a/packages/primitives/tests/script.spec.ts +++ b/packages/primitives/tests/script.spec.ts @@ -1,13 +1,13 @@ import { describe, it, expect } from 'vitest'; -import { Bytes } from '~/src/bytes'; -import { Script } from '~/src/script'; +import { Bytes } from '../src/bytes'; +import { Script } from '../src/script'; describe('Success cases', () => { // Test cases with different script representations type ScriptCase = { - raw: Uint8Array; + raw: Bytes; representations: { - bytes: Uint8Array; + bytes: Bytes; asm: string; hex: string; }; @@ -15,13 +15,11 @@ describe('Success cases', () => { const testCases: ScriptCase[] = [ { - raw: Bytes.fromHex( - '76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac', - ).toUint8Array(), + raw: Bytes.fromHex('76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac'), representations: { bytes: Bytes.fromHex( '76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac', - ).toUint8Array(), + ), asm: 'OP_DUP\nOP_HASH160\nOP_PUSHBYTES_20\n0x876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d6\nOP_EQUALVERIFY\nOP_CHECKSIG', hex: '76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac', }, diff --git a/packages/stack/src/wallet/transaction-builder.ts b/packages/stack/src/wallet/transaction-builder.ts index 9c697de..dc45676 100644 --- a/packages/stack/src/wallet/transaction-builder.ts +++ b/packages/stack/src/wallet/transaction-builder.ts @@ -1,4 +1,4 @@ -import { type WalletUTXO } from './types.js'; +import { type TransactionTemplate, type WalletUTXO } from './types.js'; import { type UTXOSet, ExtMap, Outpoint } from '@xocash/primitives'; @@ -28,13 +28,26 @@ import { */ export class TransactionBuilder { static build( - transaction: TransactionTemplateFixed, + txTemplate: Partial, unspents: Array = [], changeLockingBytecode: Uint8Array, sourceOutputs: UTXOSet = new ExtMap(), ) { + // Populate the final transaction template by merging with defaults. + // NOTE: This is just a convenience to make this utility easier to use. + const txTemplateFinal: TransactionTemplate = { + ...{ + version: 2, + locktime: 0, + inputs: [], + outputs: [], + }, + ...txTemplate, + }; + + // Build the template. const txBuilder = new TransactionBuilder( - transaction, + txTemplateFinal, unspents, sourceOutputs, ); diff --git a/packages/stack/src/wallet/wallet-hd.ts b/packages/stack/src/wallet/wallet-hd.ts index ab0822c..9a99f23 100644 --- a/packages/stack/src/wallet/wallet-hd.ts +++ b/packages/stack/src/wallet/wallet-hd.ts @@ -305,18 +305,6 @@ export class WalletHD extends BaseWallet { async signTransaction( txTemplate: Partial, ): Promise { - // Populate the final transaction template by merging with defaults. - // NOTE: This is just a convenience to make "send" easier to use. - const txTemplateFinal: TransactionTemplate = { - ...{ - version: 2, - locktime: 0, - inputs: [], - outputs: [], - }, - ...txTemplate, - }; - // Get the receiving address of this wallet. // NOTE: As per BIP44 spec, we set the change address to use the internal chain. const changeAddress = await this.getReceivingAddress(CHAIN_EXTERNAL); @@ -329,7 +317,7 @@ export class WalletHD extends BaseWallet { // Build the transaction. return TransactionBuilder.build( - txTemplateFinal, + txTemplate, unspents.toArray(), changeLockingBytecode, ); diff --git a/packages/stack/src/wallet/wallet-p2pkh.ts b/packages/stack/src/wallet/wallet-p2pkh.ts index 9cc4085..b84b6f0 100644 --- a/packages/stack/src/wallet/wallet-p2pkh.ts +++ b/packages/stack/src/wallet/wallet-p2pkh.ts @@ -338,18 +338,6 @@ export class WalletP2PKH extends BaseWallet { } async signTransaction(txTemplate: Partial) { - // Populate the final transaction template by merging with defaults. - // NOTE: This is just a convenience to make "send" easier to use. - const txTemplateFinal: TransactionTemplate = { - ...{ - version: 2, - locktime: 0, - inputs: [], - outputs: [], - }, - ...txTemplate, - }; - // Get the receiving address of this wallet. const receivingAddress = await this.getReceivingAddress(); @@ -361,7 +349,7 @@ export class WalletP2PKH extends BaseWallet { // Build the transaction. return TransactionBuilder.build( - txTemplateFinal, + txTemplate, unspents.toArray(), changeLockingBytecode, );