@bsv/sdk 1.2.14 → 1.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1524,6 +1524,7 @@ export default class Transaction {
1524
1524
  constructor(version: number = 1, inputs: TransactionInput[] = [], outputs: TransactionOutput[] = [], lockTime: number = 0, metadata: Record<string, any> = {}, merklePath?: MerklePath)
1525
1525
  addInput(input: TransactionInput): void
1526
1526
  addOutput(output: TransactionOutput): void
1527
+ addP2PKHOutput(address: number[] | string, satoshis?: number): void
1527
1528
  updateMetadata(metadata: Record<string, any>): void
1528
1529
  async fee(modelOrFee: FeeModel | number = new SatoshisPerKilobyte(10), changeDistribution: "equal" | "random" = "equal"): Promise<void>
1529
1530
  getFee(): number
@@ -1583,6 +1584,21 @@ Argument Details
1583
1584
  + **output**
1584
1585
  + The TransactionOutput object to add to the transaction.
1585
1586
 
1587
+ #### Method addP2PKHOutput
1588
+
1589
+ Adds a new P2PKH output to the transaction.
1590
+
1591
+ ```ts
1592
+ addP2PKHOutput(address: number[] | string, satoshis?: number): void
1593
+ ```
1594
+
1595
+ Argument Details
1596
+
1597
+ + **address**
1598
+ + The P2PKH address of the output.
1599
+ + **satoshis**
1600
+ + The number of satoshis to send to the address - if not provided, the output is considered a change output.
1601
+
1586
1602
  #### Method broadcast
1587
1603
 
1588
1604
  Broadcasts a transaction.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.2.14",
3
+ "version": "1.2.15",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -30,6 +30,7 @@ export default class P2PKH implements ScriptTemplate {
30
30
  } else {
31
31
  data = pubkeyhash
32
32
  }
33
+ if (data.length !== 20) throw new Error('P2PKH hash length must be 20 bytes')
33
34
  return new LockingScript([
34
35
  { op: OP.OP_DUP },
35
36
  { op: OP.OP_HASH160 },
@@ -13,6 +13,7 @@ import ChainTracker from './ChainTracker.js'
13
13
  import { defaultBroadcaster } from './broadcasters/DefaultBroadcaster.js'
14
14
  import { defaultChainTracker } from './chaintrackers/DefaultChainTracker.js'
15
15
  import { ATOMIC_BEEF, BEEF_MAGIC } from './Beef.js'
16
+ import P2PKH from '../script/templates/P2PKH.js'
16
17
 
17
18
  /**
18
19
  * Represents a complete Bitcoin transaction. This class encapsulates all the details
@@ -455,9 +456,30 @@ export default class Transaction {
455
456
  */
456
457
  addOutput (output: TransactionOutput): void {
457
458
  this.cachedHash = undefined
459
+ if (!output.change) {
460
+ if (typeof output.satoshis === 'undefined') throw new Error('either satoshis must be defined or change must be set to true')
461
+ if (output.satoshis <= 0) throw new Error('satoshis must be a positive integer or zero')
462
+ }
463
+ if (!output.lockingScript) throw new Error('lockingScript must be defined')
458
464
  this.outputs.push(output)
459
465
  }
460
466
 
467
+ /**
468
+ * Adds a new P2PKH output to the transaction.
469
+ *
470
+ * @param {number[] | string} address - The P2PKH address of the output.
471
+ * @param {number} [satoshis] - The number of satoshis to send to the address - if not provided, the output is considered a change output.
472
+ *
473
+ */
474
+ addP2PKHOutput (address: number[] | string, satoshis?: number): void {
475
+ const lockingScript = new P2PKH().lock(address)
476
+ if (typeof satoshis === 'undefined') { return this.addOutput({ lockingScript, change: true }) }
477
+ this.addOutput({
478
+ lockingScript,
479
+ satoshis
480
+ })
481
+ }
482
+
461
483
  /**
462
484
  * Updates the transaction's metadata.
463
485
  *
@@ -42,7 +42,7 @@ describe('Transaction Verification Benchmark', () => {
42
42
  }
43
43
 
44
44
  // Build the chain
45
- for (let i = 0; i < depth; i++) {
45
+ for (let i = 1; i < depth + 1; i++) {
46
46
  const newTx = new Transaction()
47
47
  newTx.addInput({
48
48
  sourceTransaction: tx,
@@ -52,7 +52,7 @@ describe('Transaction Verification Benchmark', () => {
52
52
  })
53
53
  newTx.addOutput({
54
54
  lockingScript: p2pkh.lock(publicKeyHash),
55
- satoshis: 100000 - 1000 * (i + 1)
55
+ satoshis: 100000 - (i * 10)
56
56
  })
57
57
  await newTx.sign()
58
58
  tx = newTx
@@ -973,4 +973,48 @@ describe('Transaction', () => {
973
973
  }).toThrowError('Transaction with TXID 0000000000000000000000000000000000000000000000000000000000000000 not found in BEEF data.')
974
974
  })
975
975
  })
976
+
977
+ describe('addP2PKHOutput', () => {
978
+ it('should create an output on the current transaction using an address hash or string', async () => {
979
+ const privateKey = PrivateKey.fromRandom()
980
+ const lockingScript = new P2PKH().lock(privateKey.toAddress())
981
+ const tx = new Transaction()
982
+ tx.addInput({
983
+ sourceTXID: '00'.repeat(32),
984
+ sourceOutputIndex: 0,
985
+ unlockingScriptTemplate: new P2PKH().unlock(privateKey),
986
+ })
987
+ tx.addP2PKHOutput(privateKey.toAddress(), 10000)
988
+ expect(tx.outputs.length).toEqual(1)
989
+ expect(tx.outputs[0].satoshis).toEqual(10000)
990
+ expect(tx.outputs[0].lockingScript.toHex() === lockingScript.toHex()).toBeTruthy()
991
+ })
992
+ it('should error is the address is non base58', async () => {
993
+ const tx = new Transaction()
994
+ expect(() => tx.addP2PKHOutput('A small chicken', 10000)).toThrow("Invalid base58 character ")
995
+ })
996
+ it('should error if the address is incorrectly copied', async () => {
997
+ const tx = new Transaction()
998
+ expect(() => tx.addP2PKHOutput('14afWk1jLH9Uwi2mC9C5ehrsvcFxTLYDp', 10000)).toThrow("Invalid checksum")
999
+ })
1000
+ it('should error if the address is a hash of wrong length', async () => {
1001
+ const tx = new Transaction()
1002
+ const address = [1,2,3,4,5]
1003
+ expect(() => tx.addP2PKHOutput(address, 10000)).toThrow("P2PKH hash length must be 20 bytes")
1004
+ })
1005
+ it('should set the output to a change output if the satoshi value is not given', async () => {
1006
+ const tx = new Transaction()
1007
+ tx.addP2PKHOutput('18E63MgXH43KBb288ETM8u91J2cqdGNEmg')
1008
+ expect(tx.outputs[0].change).toBeTruthy()
1009
+ expect(tx.outputs[0].satoshis).toBeUndefined()
1010
+ })
1011
+ it('throw error when satoshi value is negative', async () => {
1012
+ const tx = new Transaction()
1013
+ expect(() => tx.addP2PKHOutput('18E63MgXH43KBb288ETM8u91J2cqdGNEmg', -1000)).toThrow('satoshis must be a positive integer or zero')
1014
+ })
1015
+ it('throw error when output does not set satoshi value', async () => {
1016
+ const tx = new Transaction()
1017
+ expect(() => tx.addOutput({ lockingScript: Script.fromASM('OP_TRUE') })).toThrow('either satoshis must be defined or change must be set to true')
1018
+ })
1019
+ })
976
1020
  })