@bsv/sdk 1.0.36 → 1.0.37
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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/PrivateKey.js +5 -3
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/primitives/PublicKey.js +14 -1
- package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
- package/dist/cjs/src/script/templates/P2PKH.js +1 -0
- package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +38 -8
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/primitives/PrivateKey.js +5 -3
- package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
- package/dist/esm/src/primitives/PublicKey.js +14 -1
- package/dist/esm/src/primitives/PublicKey.js.map +1 -1
- package/dist/esm/src/script/templates/P2PKH.js +1 -0
- package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +38 -8
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/primitives/PrivateKey.d.ts +6 -4
- package/dist/types/src/primitives/PrivateKey.d.ts.map +1 -1
- package/dist/types/src/primitives/PublicKey.d.ts +4 -2
- package/dist/types/src/primitives/PublicKey.d.ts.map +1 -1
- package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +9 -2
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/TransactionInput.d.ts +4 -0
- package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/primitives.md +12 -8
- package/docs/transaction.md +19 -4
- package/package.json +1 -1
- package/src/primitives/PrivateKey.ts +6 -4
- package/src/primitives/PublicKey.ts +13 -2
- package/src/script/templates/P2PKH.ts +1 -0
- package/src/transaction/Transaction.ts +39 -8
- package/src/transaction/TransactionInput.ts +4 -0
- package/src/transaction/__tests/Transaction.test.ts +61 -0
|
@@ -293,26 +293,35 @@ export default class Transaction {
|
|
|
293
293
|
/**
|
|
294
294
|
* Computes fees prior to signing.
|
|
295
295
|
* If no fee model is provided, uses a SatoshisPerKilobyte fee model that pays 10 sat/kb.
|
|
296
|
+
* If fee is a number, the transaction uses that value as fee.
|
|
296
297
|
*
|
|
297
|
-
* @param
|
|
298
|
+
* @param modelOrFee - The initialized fee model to use or fixed fee for the transaction
|
|
298
299
|
* @param changeDistribution - Specifies how the change should be distributed
|
|
299
300
|
* amongst the change outputs
|
|
300
301
|
*
|
|
301
302
|
* TODO: Benford's law change distribution.
|
|
302
303
|
*/
|
|
303
|
-
async fee (
|
|
304
|
+
async fee (modelOrFee?: FeeModel | number, changeDistribution: 'equal' | 'random' = 'equal'): Promise<void> {
|
|
304
305
|
this.cachedHash = undefined
|
|
305
|
-
if (typeof
|
|
306
|
-
|
|
306
|
+
if (typeof modelOrFee === 'undefined') {
|
|
307
|
+
modelOrFee = new SatoshisPerKilobyte(10)
|
|
307
308
|
}
|
|
308
|
-
|
|
309
|
+
if (typeof modelOrFee === 'number') {
|
|
310
|
+
const sats = modelOrFee
|
|
311
|
+
modelOrFee = {
|
|
312
|
+
computeFee: async () => sats
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const fee = await modelOrFee.computeFee(this)
|
|
309
316
|
// change = inputs - fee - non-change outputs
|
|
310
317
|
let change = 0
|
|
311
318
|
for (const input of this.inputs) {
|
|
312
|
-
if (typeof input.sourceTransaction !== 'object') {
|
|
313
|
-
throw new Error('Source transactions are required for all inputs during fee computation')
|
|
319
|
+
if (typeof input.sourceTransaction !== 'object' && typeof input.sourceSatoshis !== 'number') {
|
|
320
|
+
throw new Error('Source transactions or sourceSatoshis are required for all inputs during fee computation')
|
|
314
321
|
}
|
|
315
|
-
change += input.sourceTransaction
|
|
322
|
+
change += input.sourceTransaction
|
|
323
|
+
? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
|
|
324
|
+
: input.sourceSatoshis
|
|
316
325
|
}
|
|
317
326
|
change -= fee
|
|
318
327
|
let changeCount = 0
|
|
@@ -350,6 +359,28 @@ export default class Transaction {
|
|
|
350
359
|
}
|
|
351
360
|
}
|
|
352
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Utility method that returns the current fee based on inputs and outputs
|
|
364
|
+
*
|
|
365
|
+
* @returns The current transaction fee
|
|
366
|
+
*/
|
|
367
|
+
getFee (): number {
|
|
368
|
+
let totalIn = 0
|
|
369
|
+
for (const input of this.inputs) {
|
|
370
|
+
if (typeof input.sourceTransaction !== 'object' && typeof input.sourceSatoshis !== 'number') {
|
|
371
|
+
throw new Error('Source transactions or sourceSatoshis are required for all inputs to calculate fee')
|
|
372
|
+
}
|
|
373
|
+
totalIn += input.sourceTransaction
|
|
374
|
+
? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
|
|
375
|
+
: input.sourceSatoshis || 0
|
|
376
|
+
}
|
|
377
|
+
let totalOut = 0
|
|
378
|
+
for (const output of this.outputs) {
|
|
379
|
+
totalOut += output.satoshis || 0
|
|
380
|
+
}
|
|
381
|
+
return totalIn - totalOut
|
|
382
|
+
}
|
|
383
|
+
|
|
353
384
|
/**
|
|
354
385
|
* Signs a transaction, hydrating all its unlocking scripts based on the provided script templates where they are available.
|
|
355
386
|
*/
|
|
@@ -16,6 +16,9 @@ import Transaction from './Transaction.js'
|
|
|
16
16
|
* @property {number} sourceOutputIndex - The index of the output in the source transaction
|
|
17
17
|
* that this input is spending. It is zero-based, indicating the position of the
|
|
18
18
|
* output in the array of outputs of the source transaction.
|
|
19
|
+
* @property {number} [sourceSatoshis] - The amount of satoshis of the source transaction
|
|
20
|
+
* output that this input is spending, used for fee calculation and signing when
|
|
21
|
+
* source transaction is not present
|
|
19
22
|
* @property {UnlockingScript} [unlockingScript] - Optional. The script that 'unlocks' the
|
|
20
23
|
* source output for spending. This script typically contains signatures and
|
|
21
24
|
* public keys that evidence the ownership of the output.
|
|
@@ -54,6 +57,7 @@ export default interface TransactionInput {
|
|
|
54
57
|
sourceTransaction?: Transaction
|
|
55
58
|
sourceTXID?: string
|
|
56
59
|
sourceOutputIndex: number
|
|
60
|
+
sourceSatoshis?: number
|
|
57
61
|
unlockingScript?: UnlockingScript
|
|
58
62
|
unlockingScriptTemplate?: {
|
|
59
63
|
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
@@ -317,6 +317,32 @@ describe('Transaction', () => {
|
|
|
317
317
|
// 4000 sats in - 1000 sats out - 1033 sats fee = expected 1967 sats change
|
|
318
318
|
expect(spendTx.outputs[1].satoshis).toEqual(1967)
|
|
319
319
|
})
|
|
320
|
+
it('Computes fee using FixedFee model', async () => {
|
|
321
|
+
const privateKey = new PrivateKey(1)
|
|
322
|
+
const publicKey = new Curve().g.mul(privateKey)
|
|
323
|
+
const publicKeyHash = hash160(publicKey.encode(true)) as number[]
|
|
324
|
+
const p2pkh = new P2PKH()
|
|
325
|
+
const sourceTx = new Transaction(1, [], [{
|
|
326
|
+
lockingScript: p2pkh.lock(publicKeyHash),
|
|
327
|
+
satoshis: 4000
|
|
328
|
+
}], 0)
|
|
329
|
+
const spendTx = new Transaction(1, [{
|
|
330
|
+
sourceTransaction: sourceTx,
|
|
331
|
+
sourceOutputIndex: 0,
|
|
332
|
+
unlockingScriptTemplate: p2pkh.unlock(privateKey),
|
|
333
|
+
sequence: 0xffffffff
|
|
334
|
+
}], [{
|
|
335
|
+
satoshis: 1000,
|
|
336
|
+
lockingScript: p2pkh.lock(publicKeyHash)
|
|
337
|
+
}, {
|
|
338
|
+
lockingScript: p2pkh.lock(publicKeyHash),
|
|
339
|
+
change: true
|
|
340
|
+
}], 0)
|
|
341
|
+
expect(spendTx.outputs[1].satoshis).not.toBeDefined()
|
|
342
|
+
await spendTx.fee(1033)
|
|
343
|
+
// 4000 sats in - 1000 sats out - 1033 sats fee = expected 1967 sats change
|
|
344
|
+
expect(spendTx.outputs[1].satoshis).toEqual(1967)
|
|
345
|
+
})
|
|
320
346
|
it('Distributes change among multiple change outputs', async () => {
|
|
321
347
|
const privateKey = new PrivateKey(1)
|
|
322
348
|
const publicKey = new Curve().g.mul(privateKey)
|
|
@@ -352,6 +378,41 @@ describe('Transaction', () => {
|
|
|
352
378
|
expect(spendTx.outputs[1].satoshis).toEqual(983)
|
|
353
379
|
expect(spendTx.outputs[2].satoshis).toEqual(983)
|
|
354
380
|
})
|
|
381
|
+
it('Calculates fee for utxo based transaction', async () => {
|
|
382
|
+
const utxos = [ // WoC format utxos
|
|
383
|
+
{
|
|
384
|
+
height: 1600000,
|
|
385
|
+
tx_pos: 0,
|
|
386
|
+
tx_hash: '672dd6a93fa5d7ba6794e0bdf8b479440b95a55ec10ad3d9e03585ecb5628d8d',
|
|
387
|
+
value: 10000
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
height: 1600000,
|
|
391
|
+
tx_pos: 0,
|
|
392
|
+
tx_hash: 'f33505acf37a7726cc37d391bc6f889b8684ac2a2d581c4be2a4b1c8b46609bc',
|
|
393
|
+
value: 10000
|
|
394
|
+
},
|
|
395
|
+
]
|
|
396
|
+
const priv = PrivateKey.fromRandom()
|
|
397
|
+
const tx = new Transaction()
|
|
398
|
+
utxos.forEach(utxo => {
|
|
399
|
+
const script = new P2PKH().lock(priv.toPublicKey().toHash())
|
|
400
|
+
tx.addInput({
|
|
401
|
+
sourceTXID: utxo.tx_hash,
|
|
402
|
+
sourceOutputIndex: utxo.tx_pos,
|
|
403
|
+
sourceSatoshis: utxo.value,
|
|
404
|
+
unlockingScriptTemplate: new P2PKH()
|
|
405
|
+
.unlock(priv, 'all', false, utxo.value, script)
|
|
406
|
+
})
|
|
407
|
+
})
|
|
408
|
+
tx.addOutput({
|
|
409
|
+
lockingScript: new P2PKH().lock(priv.toAddress()),
|
|
410
|
+
change: true
|
|
411
|
+
})
|
|
412
|
+
await tx.fee({ computeFee: async () => 10 })
|
|
413
|
+
expect(tx.outputs[0].satoshis).toEqual(20000 - 10)
|
|
414
|
+
expect(tx.getFee()).toEqual(10)
|
|
415
|
+
})
|
|
355
416
|
})
|
|
356
417
|
|
|
357
418
|
describe('Broadcast', () => {
|