@btc-vision/transaction 1.0.85 → 1.0.86
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/.gitattributes +2 -2
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/keypair/Wallet.d.ts +3 -0
- package/browser/transaction/builders/TapUnwarpTransaction.d.ts +40 -40
- package/browser/transaction/builders/UnwarpTransaction.d.ts +34 -34
- package/browser/utxo/UTXOManager.d.ts +7 -7
- package/build/Utils.d.ts +0 -0
- package/build/Utils.js +1 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/consensus/metadata/RoswsellConsensus.d.ts +2 -0
- package/build/consensus/metadata/RoswsellConsensus.js +4 -0
- package/build/contracts/ContractMetadataManager.d.ts +0 -0
- package/build/contracts/ContractMetadataManager.js +1 -0
- package/build/generators/OPNetAddressGenerator.d.ts +0 -0
- package/build/generators/OPNetAddressGenerator.js +1 -0
- package/build/generators/builders/UnwrapGenerator.d.ts +8 -0
- package/build/generators/builders/UnwrapGenerator.js +79 -0
- package/build/keypair/Wallet.d.ts +3 -0
- package/build/keypair/Wallet.js +8 -0
- package/build/keypair/interfaces/GeneratedWallet.d.ts +5 -0
- package/build/keypair/interfaces/GeneratedWallet.js +1 -0
- package/build/metadata/CommonContracts.d.ts +6 -0
- package/build/metadata/CommonContracts.js +5 -0
- package/build/metadata/ContractMetadataManager.d.ts +1 -0
- package/build/metadata/ContractMetadataManager.js +9 -0
- package/build/metadata/contracts/ContractBase.d.ts +9 -0
- package/build/metadata/contracts/ContractBase.js +13 -0
- package/build/metadata/contracts/ContractBaseMetadata.d.ts +9 -0
- package/build/metadata/contracts/ContractBaseMetadata.js +13 -0
- package/build/metadata/contracts/ContractMetadataManager.d.ts +0 -0
- package/build/metadata/contracts/ContractMetadataManager.js +1 -0
- package/build/network/NetworkConverter.d.ts +0 -0
- package/build/network/NetworkConverter.js +14 -0
- package/build/scripts/Regtest.d.ts +2 -0
- package/build/scripts/Regtest.js +15 -0
- package/build/scripts/test.d.ts +1 -0
- package/build/scripts/test.js +74 -0
- package/build/signer/Regtest.d.ts +2 -0
- package/build/signer/Regtest.js +15 -0
- package/build/tests/Regtest.d.ts +3 -0
- package/build/tests/Regtest.js +29 -0
- package/build/tests/adaptPSBT.d.ts +1 -0
- package/build/tests/adaptPSBT.js +44 -0
- package/build/tests/btc/send.d.ts +1 -0
- package/build/tests/btc/send.js +35 -0
- package/build/tests/btc/transfer.d.ts +1 -0
- package/build/tests/btc/transfer.js +35 -0
- package/build/tests/createPairReg.d.ts +1 -0
- package/build/tests/createPairReg.js +73 -0
- package/build/tests/deploy/deployMoto.d.ts +4 -0
- package/build/tests/deploy/deployMoto.js +89 -0
- package/build/tests/deploy/deployPool.d.ts +1 -0
- package/build/tests/deploy/deployPool.js +5 -0
- package/build/tests/deploy/deployStep1.d.ts +1 -0
- package/build/tests/deploy/deployStep1.js +5 -0
- package/build/tests/deploy/deployStep2.d.ts +1 -0
- package/build/tests/deploy/deployStep2.js +5 -0
- package/build/tests/deploy/deployStep3.d.ts +1 -0
- package/build/tests/deploy/deployStep3.js +5 -0
- package/build/tests/deploy.d.ts +1 -0
- package/build/tests/deploy.js +41 -0
- package/build/tests/deployMotoRegStep1.d.ts +1 -0
- package/build/tests/deployMotoRegStep1.js +85 -0
- package/build/tests/deployReg.d.ts +1 -0
- package/build/tests/deployReg.js +85 -0
- package/build/tests/factory/createPairReg.d.ts +1 -0
- package/build/tests/factory/createPairReg.js +13 -0
- package/build/tests/gen.d.ts +1 -0
- package/build/tests/gen.js +19 -0
- package/build/tests/interaction.d.ts +5 -0
- package/build/tests/interaction.js +62 -0
- package/build/tests/massWrapReg.d.ts +1 -0
- package/build/tests/massWrapReg.js +105 -0
- package/build/tests/mineReg.d.ts +1 -0
- package/build/tests/mineReg.js +19 -0
- package/build/tests/moto/airdropToken.d.ts +1 -0
- package/build/tests/moto/airdropToken.js +21 -0
- package/build/tests/moto/airdropTokens.d.ts +1 -0
- package/build/tests/moto/airdropTokens.js +60 -0
- package/build/tests/moto/allowance.d.ts +1 -0
- package/build/tests/moto/allowance.js +6 -0
- package/build/tests/moto/approve.d.ts +1 -0
- package/build/tests/moto/approve.js +10 -0
- package/build/tests/moto/approveWBTC.d.ts +1 -0
- package/build/tests/moto/approveWBTC.js +12 -0
- package/build/tests/moto/balanceOf.d.ts +1 -0
- package/build/tests/moto/balanceOf.js +12 -0
- package/build/tests/moto/transfer.d.ts +1 -0
- package/build/tests/moto/transfer.js +16 -0
- package/build/tests/motoswap/airdropToken.d.ts +11 -0
- package/build/tests/motoswap/airdropToken.js +36 -0
- package/build/tests/motoswap/deployMoto.d.ts +4 -0
- package/build/tests/motoswap/deployMoto.js +89 -0
- package/build/tests/motoswap/deployMotoRegStep1.d.ts +1 -0
- package/build/tests/motoswap/deployMotoRegStep1.js +91 -0
- package/build/tests/motoswap/deployMotoRegStep2.d.ts +1 -0
- package/build/tests/motoswap/deployMotoRegStep2.js +91 -0
- package/build/tests/motoswap/deployPool.d.ts +1 -0
- package/build/tests/motoswap/deployPool.js +5 -0
- package/build/tests/motoswap/deployStep1.d.ts +1 -0
- package/build/tests/motoswap/deployStep1.js +5 -0
- package/build/tests/motoswap/deployStep2.d.ts +1 -0
- package/build/tests/motoswap/deployStep2.js +5 -0
- package/build/tests/motoswap/deployStep3.d.ts +1 -0
- package/build/tests/motoswap/deployStep3.js +5 -0
- package/build/tests/motoswap/interaction.d.ts +3 -0
- package/build/tests/motoswap/interaction.js +63 -0
- package/build/tests/motoswap/routerAddLiquidity.d.ts +11 -0
- package/build/tests/motoswap/routerAddLiquidity.js +35 -0
- package/build/tests/motoswap-router/addLiquidity.d.ts +11 -0
- package/build/tests/motoswap-router/addLiquidity.js +36 -0
- package/build/tests/motoswap-router/deployMoto.d.ts +4 -0
- package/build/tests/motoswap-router/deployMoto.js +89 -0
- package/build/tests/motoswap-router/deployPool.d.ts +1 -0
- package/build/tests/motoswap-router/deployPool.js +5 -0
- package/build/tests/motoswap-router/deployStep1.d.ts +1 -0
- package/build/tests/motoswap-router/deployStep1.js +5 -0
- package/build/tests/motoswap-router/deployStep2.d.ts +1 -0
- package/build/tests/motoswap-router/deployStep2.js +5 -0
- package/build/tests/motoswap-router/deployStep3.d.ts +1 -0
- package/build/tests/motoswap-router/deployStep3.js +5 -0
- package/build/tests/motoswap-router/getAmountsOut.d.ts +5 -0
- package/build/tests/motoswap-router/getAmountsOut.js +34 -0
- package/build/tests/motoswap-router/routerAddLiquidity.d.ts +11 -0
- package/build/tests/motoswap-router/routerAddLiquidity.js +35 -0
- package/build/tests/motoswap-router/swap.d.ts +8 -0
- package/build/tests/motoswap-router/swap.js +24 -0
- package/build/tests/multisign.d.ts +1 -0
- package/build/tests/multisign.js +47 -0
- package/build/tests/multisign2.d.ts +1 -0
- package/build/tests/multisign2.js +27 -0
- package/build/tests/pool/DecodePoolAddress.d.ts +6 -0
- package/build/tests/pool/DecodePoolAddress.js +12 -0
- package/build/tests/pool/decodeReserves.d.ts +5 -0
- package/build/tests/pool/decodeReserves.js +13 -0
- package/build/tests/pool/reserves.d.ts +1 -0
- package/build/tests/pool/reserves.js +18 -0
- package/build/tests/shared/Utils.d.ts +2 -0
- package/build/tests/shared/Utils.js +14 -0
- package/build/tests/shared/interaction.d.ts +7 -0
- package/build/tests/shared/interaction.js +85 -0
- package/build/tests/shared/tokens.d.ts +6 -0
- package/build/tests/shared/tokens.js +5 -0
- package/build/tests/stakeReg.d.ts +1 -0
- package/build/tests/stakeReg.js +73 -0
- package/build/tests/stakedReg.d.ts +1 -0
- package/build/tests/stakedReg.js +28 -0
- package/build/tests/test.d.ts +1 -0
- package/build/tests/test.js +51 -0
- package/build/tests/test2.d.ts +1 -0
- package/build/tests/test2.js +73 -0
- package/build/tests/testReg.d.ts +1 -0
- package/build/tests/testReg.js +91 -0
- package/build/tests/tokens.d.ts +6 -0
- package/build/tests/tokens.js +5 -0
- package/build/tests/totalRewardReg.d.ts +1 -0
- package/build/tests/totalRewardReg.js +28 -0
- package/build/tests/transfer.d.ts +1 -0
- package/build/tests/transfer.js +74 -0
- package/build/tests/transferReg.d.ts +1 -0
- package/build/tests/transferReg.js +74 -0
- package/build/tests/unStakeReg.d.ts +1 -0
- package/build/tests/unStakeReg.js +72 -0
- package/build/tests/unwrapReg.d.ts +1 -0
- package/build/tests/unwrapReg.js +61 -0
- package/build/tests/unwrapReg2.d.ts +1 -0
- package/build/tests/unwrapReg2.js +56 -0
- package/build/tests/unwrapRegSegwit.d.ts +1 -0
- package/build/tests/unwrapRegSegwit.js +83 -0
- package/build/tests/wbtc/approve.d.ts +1 -0
- package/build/tests/wbtc/approve.js +6 -0
- package/build/tests/wbtc/approveWBTC.d.ts +1 -0
- package/build/tests/wbtc/approveWBTC.js +12 -0
- package/build/tests/wbtc/massWrapReg.d.ts +1 -0
- package/build/tests/wbtc/massWrapReg.js +105 -0
- package/build/tests/wbtc/transfer.d.ts +1 -0
- package/build/tests/wbtc/transfer.js +16 -0
- package/build/tests/wbtc/transferReg.d.ts +1 -0
- package/build/tests/wbtc/transferReg.js +16 -0
- package/build/tests/wbtc/unStakeReg.d.ts +1 -0
- package/build/tests/wbtc/unStakeReg.js +72 -0
- package/build/tests/wbtc/unwrapReg.d.ts +1 -0
- package/build/tests/wbtc/unwrapReg.js +60 -0
- package/build/tests/wbtc/unwrapRegSegwit.d.ts +1 -0
- package/build/tests/wbtc/unwrapRegSegwit.js +83 -0
- package/build/tests/wbtc/withdrawalRequestReg.d.ts +1 -0
- package/build/tests/wbtc/withdrawalRequestReg.js +71 -0
- package/build/tests/wbtc/wrapReg.d.ts +1 -0
- package/build/tests/wbtc/wrapReg.js +65 -0
- package/build/tests/wbtc/wrapTest.d.ts +1 -0
- package/build/tests/wbtc/wrapTest.js +66 -0
- package/build/tests/withdrawalRequestReg.d.ts +1 -0
- package/build/tests/withdrawalRequestReg.js +71 -0
- package/build/tests/wrap.d.ts +1 -0
- package/build/tests/wrap.js +65 -0
- package/build/tests/wrapReg.d.ts +1 -0
- package/build/tests/wrapReg.js +68 -0
- package/build/tests/wrapTest.d.ts +1 -0
- package/build/tests/wrapTest.js +66 -0
- package/build/tests/wrapTestg.d.ts +1 -0
- package/build/tests/wrapTestg.js +66 -0
- package/build/tests/writers/allowance.d.ts +3 -0
- package/build/tests/writers/allowance.js +10 -0
- package/build/tests/writers/approve.d.ts +4 -0
- package/build/tests/writers/approve.js +11 -0
- package/build/transaction/TransactionBuilder.d.ts +60 -0
- package/build/transaction/TransactionBuilder.js +244 -0
- package/build/transaction/browser/BrowserSigner.d.ts +11 -0
- package/build/transaction/browser/BrowserSigner.js +10 -0
- package/build/transaction/browser/extensions/Unisat.d.ts +54 -0
- package/build/transaction/browser/extensions/Unisat.js +11 -0
- package/build/transaction/builders/GenericTransaction.d.ts +11 -0
- package/build/transaction/builders/GenericTransaction.js +23 -0
- package/build/transaction/builders/TapUnwarpTransaction.d.ts +37 -0
- package/build/transaction/builders/TapUnwarpTransaction.js +201 -0
- package/build/transaction/builders/UnwarpSegwitTransaction.d.ts +34 -0
- package/build/transaction/builders/UnwarpSegwitTransaction.js +184 -0
- package/build/transaction/builders/UnwarpTransaction.d.ts +35 -0
- package/build/transaction/builders/UnwarpTransaction.js +199 -0
- package/build/transaction/interfaces/ITransactions.d.ts +32 -0
- package/build/transaction/interfaces/ITransactions.js +1 -0
- package/build/utxo/IUTXO.d.ts +0 -0
- package/build/utxo/IUTXO.js +1 -0
- package/build/utxo/OPNetUtils.d.ts +7 -0
- package/build/utxo/OPNetUtils.js +47 -0
- package/build/utxo/UTXOManager.d.ts +7 -0
- package/build/utxo/UTXOManager.js +47 -0
- package/build/wbtc/BroadcastResponse.d.ts +0 -0
- package/build/wbtc/BroadcastResponse.js +1 -0
- package/gulpfile.js +152 -152
- package/package.json +109 -109
- package/src/_version.ts +1 -1
- package/src/consensus/Consensus.ts +36 -36
- package/src/consensus/ConsensusConfig.ts +39 -39
- package/src/crypto/crypto-browser.js +75 -75
- package/src/generators/AddressGenerator.ts +24 -24
- package/src/generators/Features.ts +5 -5
- package/src/generators/Generator.ts +75 -75
- package/src/generators/builders/CalldataGenerator.ts +148 -148
- package/src/generators/builders/DeploymentGenerator.ts +66 -66
- package/src/index.ts +4 -4
- package/src/keypair/AddressVerificator.ts +40 -40
- package/src/keypair/EcKeyPair.ts +282 -282
- package/src/keypair/Wallet.ts +120 -97
- package/src/keypair/interfaces/IWallet.ts +19 -19
- package/src/metadata/ContractBaseMetadata.ts +23 -23
- package/src/metadata/contracts/wBTC.ts +60 -60
- package/src/metadata/tokens.ts +135 -135
- package/src/network/NetworkInformation.ts +7 -7
- package/src/transaction/TransactionFactory.ts +496 -496
- package/src/transaction/browser/BrowserSignerBase.ts +37 -37
- package/src/transaction/browser/Web3Provider.ts +46 -46
- package/src/transaction/browser/extensions/UnisatSigner.ts +218 -218
- package/src/transaction/browser/types/Unisat.ts +97 -97
- package/src/transaction/builders/FundingTransaction.ts +40 -40
- package/src/transaction/builders/InteractionTransaction.ts +38 -38
- package/src/transaction/builders/SharedInteractionTransaction.ts +368 -368
- package/src/transaction/builders/TransactionBuilder.ts +665 -665
- package/src/transaction/builders/UnwrapSegwitTransaction.ts +365 -365
- package/src/transaction/builders/UnwrapTransaction.ts +507 -507
- package/src/transaction/builders/WrapTransaction.ts +346 -346
- package/src/transaction/interfaces/ITransactionParameters.ts +59 -59
- package/src/transaction/interfaces/Tap.ts +26 -26
- package/src/transaction/psbt/PSBTTypes.ts +3 -3
- package/src/transaction/shared/TweakedTransaction.ts +539 -539
- package/src/utxo/OPNetLimitedProvider.ts +244 -244
- package/src/utxo/interfaces/BroadcastResponse.ts +10 -10
- package/src/utxo/interfaces/IUTXO.ts +29 -29
- package/src/verification/TapscriptVerificator.ts +89 -89
- package/src/wbtc/Generate.ts +40 -40
- package/src/wbtc/UnwrapGeneration.ts +13 -13
- package/src/wbtc/WrappedGenerationParameters.ts +33 -33
- package/webpack.config.js +78 -78
- /package/build/generators/builders/{MultiSignGenerator.d.ts → MultisignGenerator.d.ts} +0 -0
- /package/build/generators/builders/{MultiSignGenerator.js → MultisignGenerator.js} +0 -0
- /package/build/generators/{Features.d.ts → features.d.ts} +0 -0
- /package/build/generators/{Features.js → features.js} +0 -0
|
@@ -1,665 +1,665 @@
|
|
|
1
|
-
import { initEccLib, Network, opcodes, Psbt, script, Signer, Transaction } from 'bitcoinjs-lib';
|
|
2
|
-
import { varuint } from 'bitcoinjs-lib/src/bufferutils.js';
|
|
3
|
-
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
4
|
-
import { PsbtInputExtended, PsbtOutputExtended, UpdateInput } from '../interfaces/Tap.js';
|
|
5
|
-
import { TransactionType } from '../enums/TransactionType.js';
|
|
6
|
-
import {
|
|
7
|
-
IFundingTransactionParameters,
|
|
8
|
-
ITransactionParameters,
|
|
9
|
-
} from '../interfaces/ITransactionParameters.js';
|
|
10
|
-
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
11
|
-
import { Address } from '@btc-vision/bsi-binary';
|
|
12
|
-
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
13
|
-
import { ECPairInterface } from 'ecpair';
|
|
14
|
-
import { AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
15
|
-
import { TweakedTransaction } from '../shared/TweakedTransaction.js';
|
|
16
|
-
|
|
17
|
-
initEccLib(ecc);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Allows to build a transaction like you would on Ethereum.
|
|
21
|
-
* @description The transaction builder class
|
|
22
|
-
* @abstract
|
|
23
|
-
* @class TransactionBuilder
|
|
24
|
-
*/
|
|
25
|
-
export abstract class TransactionBuilder<T extends TransactionType> extends TweakedTransaction {
|
|
26
|
-
public static readonly LOCK_LEAF_SCRIPT: Buffer = script.compile([
|
|
27
|
-
opcodes.OP_0,
|
|
28
|
-
//opcodes.OP_VERIFY, - verify that this is not needed.
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
public static readonly MINIMUM_DUST: bigint = 330n;
|
|
32
|
-
|
|
33
|
-
public abstract readonly type: T;
|
|
34
|
-
public readonly logColor: string = '#785def';
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @description The overflow fees of the transaction
|
|
38
|
-
* @public
|
|
39
|
-
*/
|
|
40
|
-
public overflowFees: bigint = 0n;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @description Cost in satoshis of the transaction fee
|
|
44
|
-
*/
|
|
45
|
-
public transactionFee: bigint = 0n;
|
|
46
|
-
/**
|
|
47
|
-
* @description The estimated fees of the transaction
|
|
48
|
-
*/
|
|
49
|
-
public estimatedFees: bigint = 0n;
|
|
50
|
-
/**
|
|
51
|
-
* @description The transaction itself.
|
|
52
|
-
*/
|
|
53
|
-
protected transaction: Psbt;
|
|
54
|
-
/**
|
|
55
|
-
* @description Inputs to update later on.
|
|
56
|
-
*/
|
|
57
|
-
protected readonly updateInputs: UpdateInput[] = [];
|
|
58
|
-
/**
|
|
59
|
-
* @description The outputs of the transaction
|
|
60
|
-
*/
|
|
61
|
-
protected readonly outputs: PsbtOutputExtended[] = [];
|
|
62
|
-
/**
|
|
63
|
-
* @description Output that will be used to pay the fees
|
|
64
|
-
*/
|
|
65
|
-
protected feeOutput: PsbtOutputExtended | null = null;
|
|
66
|
-
/**
|
|
67
|
-
* @description The total amount of satoshis in the inputs
|
|
68
|
-
*/
|
|
69
|
-
protected totalInputAmount: bigint;
|
|
70
|
-
/**
|
|
71
|
-
* @description The signer of the transaction
|
|
72
|
-
*/
|
|
73
|
-
protected readonly signer: Signer;
|
|
74
|
-
/**
|
|
75
|
-
* @description The network where the transaction will be broadcasted
|
|
76
|
-
*/
|
|
77
|
-
protected readonly network: Network;
|
|
78
|
-
/**
|
|
79
|
-
* @description The fee rate of the transaction
|
|
80
|
-
*/
|
|
81
|
-
protected readonly feeRate: number;
|
|
82
|
-
/**
|
|
83
|
-
* @description The opnet priority fee of the transaction
|
|
84
|
-
*/
|
|
85
|
-
protected priorityFee: bigint;
|
|
86
|
-
/**
|
|
87
|
-
* @description The utxos used in the transaction
|
|
88
|
-
*/
|
|
89
|
-
protected utxos: UTXO[];
|
|
90
|
-
/**
|
|
91
|
-
* @description The address where the transaction is sent to
|
|
92
|
-
* @protected
|
|
93
|
-
*/
|
|
94
|
-
protected to: Address | undefined;
|
|
95
|
-
/**
|
|
96
|
-
* @description The address where the transaction is sent from
|
|
97
|
-
* @protected
|
|
98
|
-
*/
|
|
99
|
-
protected from: Address;
|
|
100
|
-
/**
|
|
101
|
-
* @description The maximum fee rate of the transaction
|
|
102
|
-
*/
|
|
103
|
-
protected _maximumFeeRate: number = 100000000;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @param {ITransactionParameters} parameters - The transaction parameters
|
|
107
|
-
*/
|
|
108
|
-
protected constructor(parameters: ITransactionParameters) {
|
|
109
|
-
super(parameters);
|
|
110
|
-
|
|
111
|
-
if (parameters.estimatedFees) {
|
|
112
|
-
this.estimatedFees = parameters.estimatedFees;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
this.signer = parameters.signer;
|
|
116
|
-
this.network = parameters.network;
|
|
117
|
-
this.feeRate = parameters.feeRate;
|
|
118
|
-
this.priorityFee = parameters.priorityFee;
|
|
119
|
-
this.utxos = parameters.utxos;
|
|
120
|
-
this.to = parameters.to || undefined;
|
|
121
|
-
|
|
122
|
-
this.from = TransactionBuilder.getFrom(
|
|
123
|
-
parameters.from,
|
|
124
|
-
this.signer as ECPairInterface,
|
|
125
|
-
this.network,
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
this.totalInputAmount = this.calculateTotalUTXOAmount();
|
|
129
|
-
|
|
130
|
-
const totalVOut: bigint = this.calculateTotalVOutAmount();
|
|
131
|
-
if (totalVOut < this.totalInputAmount) {
|
|
132
|
-
throw new Error(`Vout value is less than the value to send`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.transaction = new Psbt({
|
|
136
|
-
network: this.network,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
public static getFrom(
|
|
141
|
-
from: string | undefined,
|
|
142
|
-
keypair: ECPairInterface,
|
|
143
|
-
network: Network,
|
|
144
|
-
): Address {
|
|
145
|
-
return from || EcKeyPair.getTaprootAddress(keypair, network);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @description Converts the witness stack to a script witness
|
|
150
|
-
* @param {Buffer[]} witness - The witness stack
|
|
151
|
-
* @protected
|
|
152
|
-
* @returns {Buffer}
|
|
153
|
-
*/
|
|
154
|
-
public static witnessStackToScriptWitness(witness: Buffer[]): Buffer {
|
|
155
|
-
let buffer = Buffer.allocUnsafe(0);
|
|
156
|
-
|
|
157
|
-
function writeSlice(slice: Buffer) {
|
|
158
|
-
buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function writeVarInt(i: number) {
|
|
162
|
-
const currentLen = buffer.length;
|
|
163
|
-
const varintLen = varuint.encodingLength(i);
|
|
164
|
-
|
|
165
|
-
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
166
|
-
varuint.encode(i, buffer, currentLen);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function writeVarSlice(slice: Buffer) {
|
|
170
|
-
writeVarInt(slice.length);
|
|
171
|
-
writeSlice(slice);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function writeVector(vector: Buffer[]) {
|
|
175
|
-
writeVarInt(vector.length);
|
|
176
|
-
vector.forEach(writeVarSlice);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
writeVector(witness);
|
|
180
|
-
|
|
181
|
-
return buffer;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
public async getFundingTransactionParameters(): Promise<IFundingTransactionParameters> {
|
|
185
|
-
if (!this.estimatedFees) {
|
|
186
|
-
this.estimatedFees = await this.estimateTransactionFees();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
utxos: this.utxos,
|
|
191
|
-
to: this.getScriptAddress(),
|
|
192
|
-
signer: this.signer,
|
|
193
|
-
network: this.network,
|
|
194
|
-
feeRate: this.feeRate,
|
|
195
|
-
priorityFee: this.priorityFee,
|
|
196
|
-
from: this.from,
|
|
197
|
-
amount: this.estimatedFees,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Set the destination address of the transaction
|
|
203
|
-
* @param {Address} address - The address to set
|
|
204
|
-
*/
|
|
205
|
-
public setDestinationAddress(address: Address): void {
|
|
206
|
-
this.to = address; // this.getScriptAddress()
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Set the maximum fee rate of the transaction in satoshis per byte
|
|
211
|
-
* @param {number} feeRate - The fee rate to set
|
|
212
|
-
* @public
|
|
213
|
-
*/
|
|
214
|
-
public setMaximumFeeRate(feeRate: number): void {
|
|
215
|
-
this._maximumFeeRate = feeRate;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* @description Signs the transaction
|
|
220
|
-
* @public
|
|
221
|
-
* @returns {Promise<Transaction>} - The signed transaction in hex format
|
|
222
|
-
* @throws {Error} - If something went wrong
|
|
223
|
-
*/
|
|
224
|
-
public async signTransaction(): Promise<Transaction> {
|
|
225
|
-
if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
226
|
-
throw new Error(
|
|
227
|
-
'Invalid contract address. The contract address must be a taproot address.',
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (this.signed) throw new Error('Transaction is already signed');
|
|
232
|
-
this.signed = true;
|
|
233
|
-
|
|
234
|
-
await this.buildTransaction();
|
|
235
|
-
|
|
236
|
-
const builtTx = await this.internalBuildTransaction(this.transaction);
|
|
237
|
-
if (builtTx) {
|
|
238
|
-
if (this.regenerated) {
|
|
239
|
-
throw new Error('Transaction was regenerated');
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return this.transaction.extractTransaction(false);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
throw new Error('Could not sign transaction');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* @description Generates the transaction minimal signatures
|
|
250
|
-
* @public
|
|
251
|
-
*/
|
|
252
|
-
public async generateTransactionMinimalSignatures(): Promise<void> {
|
|
253
|
-
if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
254
|
-
throw new Error(
|
|
255
|
-
'Invalid contract address. The contract address must be a taproot address.',
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
await this.buildTransaction();
|
|
260
|
-
|
|
261
|
-
if (this.transaction.data.inputs.length === 0) {
|
|
262
|
-
const inputs: PsbtInputExtended[] = this.getInputs();
|
|
263
|
-
const outputs: PsbtOutputExtended[] = this.getOutputs();
|
|
264
|
-
|
|
265
|
-
this.transaction.setMaximumFeeRate(this._maximumFeeRate);
|
|
266
|
-
this.transaction.addInputs(inputs);
|
|
267
|
-
|
|
268
|
-
for (let i = 0; i < this.updateInputs.length; i++) {
|
|
269
|
-
this.transaction.updateInput(i, this.updateInputs[i]);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
this.transaction.addOutputs(outputs);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* @description Signs the transaction
|
|
278
|
-
* @public
|
|
279
|
-
* @returns {Promise<Psbt>} - The signed transaction in hex format
|
|
280
|
-
* @throws {Error} - If something went wrong
|
|
281
|
-
*/
|
|
282
|
-
public async signPSBT(): Promise<Psbt> {
|
|
283
|
-
if (await this.signTransaction()) {
|
|
284
|
-
return this.transaction;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
throw new Error('Could not sign transaction');
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Add an input to the transaction.
|
|
292
|
-
* @param {PsbtInputExtended} input - The input to add
|
|
293
|
-
* @public
|
|
294
|
-
* @returns {void}
|
|
295
|
-
*/
|
|
296
|
-
public addInput(input: PsbtInputExtended): void {
|
|
297
|
-
this.inputs.push(input);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Add an output to the transaction.
|
|
302
|
-
* @param {PsbtOutputExtended} output - The output to add
|
|
303
|
-
* @public
|
|
304
|
-
* @returns {void}
|
|
305
|
-
*/
|
|
306
|
-
public addOutput(output: PsbtOutputExtended): void {
|
|
307
|
-
if (output.value === 0) return;
|
|
308
|
-
if (output.value < TransactionBuilder.MINIMUM_DUST) {
|
|
309
|
-
throw new Error(
|
|
310
|
-
`Output value is less than the minimum dust ${output.value} < ${TransactionBuilder.MINIMUM_DUST}`,
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
this.outputs.push(output);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Receiver address.
|
|
319
|
-
* @public
|
|
320
|
-
* @returns {Address} - The receiver address
|
|
321
|
-
*/
|
|
322
|
-
public toAddress(): string | undefined {
|
|
323
|
-
return this.to;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* @description Returns the script address
|
|
328
|
-
* @returns {Address} - The script address
|
|
329
|
-
*/
|
|
330
|
-
public address(): Address | undefined {
|
|
331
|
-
return this.tapData?.address;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Estimates the transaction fees.
|
|
336
|
-
* @public
|
|
337
|
-
* @returns {Promise<bigint>} - The estimated transaction fees
|
|
338
|
-
*/
|
|
339
|
-
public async estimateTransactionFees(): Promise<bigint> {
|
|
340
|
-
if (this.estimatedFees) return this.estimatedFees;
|
|
341
|
-
|
|
342
|
-
const fakeTx = new Psbt({
|
|
343
|
-
network: this.network,
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
const builtTx = await this.internalBuildTransaction(fakeTx);
|
|
347
|
-
if (builtTx) {
|
|
348
|
-
const tx = fakeTx.extractTransaction(true, true);
|
|
349
|
-
const size = tx.virtualSize();
|
|
350
|
-
const fee: number = this.feeRate * size;
|
|
351
|
-
|
|
352
|
-
this.estimatedFees = BigInt(Math.ceil(fee) + 1);
|
|
353
|
-
|
|
354
|
-
return this.estimatedFees;
|
|
355
|
-
} else {
|
|
356
|
-
throw new Error(
|
|
357
|
-
`Could not build transaction to estimate fee. Something went wrong while building the transaction.`,
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
public async rebuildFromBase64(base64: string): Promise<Psbt> {
|
|
363
|
-
this.transaction = Psbt.fromBase64(base64, { network: this.network });
|
|
364
|
-
this.signed = false;
|
|
365
|
-
|
|
366
|
-
this.sighashTypes = [Transaction.SIGHASH_ANYONECANPAY, Transaction.SIGHASH_ALL];
|
|
367
|
-
|
|
368
|
-
return await this.signPSBT();
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
public setPSBT(psbt: Psbt): void {
|
|
372
|
-
this.transaction = psbt;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* @description Adds the refund output to the transaction
|
|
377
|
-
* @param {bigint} amountSpent - The amount spent
|
|
378
|
-
* @protected
|
|
379
|
-
* @returns {Promise<void>}
|
|
380
|
-
*/
|
|
381
|
-
protected async addRefundOutput(amountSpent: bigint): Promise<void> {
|
|
382
|
-
/** Add the refund output */
|
|
383
|
-
const sendBackAmount: bigint = this.totalInputAmount - amountSpent;
|
|
384
|
-
if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
|
|
385
|
-
if (AddressVerificator.isValidP2TRAddress(this.from, this.network)) {
|
|
386
|
-
await this.setFeeOutput({
|
|
387
|
-
value: Number(sendBackAmount),
|
|
388
|
-
address: this.from,
|
|
389
|
-
tapInternalKey: this.internalPubKeyToXOnly(),
|
|
390
|
-
});
|
|
391
|
-
} else {
|
|
392
|
-
await this.setFeeOutput({
|
|
393
|
-
value: Number(sendBackAmount),
|
|
394
|
-
address: this.from,
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
this.warn(
|
|
402
|
-
`Amount to send back (${sendBackAmount} sat) is less than the minimum dust (${TransactionBuilder.MINIMUM_DUST} sat), it will be consumed in fees instead.`,
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* @description Adds the value to the output
|
|
408
|
-
* @param {number | bigint} value - The value to add
|
|
409
|
-
* @protected
|
|
410
|
-
* @returns {void}
|
|
411
|
-
*/
|
|
412
|
-
protected addValueToToOutput(value: number | bigint): void {
|
|
413
|
-
if (value < TransactionBuilder.MINIMUM_DUST) {
|
|
414
|
-
throw new Error(
|
|
415
|
-
`Value to send is less than the minimum dust ${value} < ${TransactionBuilder.MINIMUM_DUST}`,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
for (let output of this.outputs) {
|
|
420
|
-
if ('address' in output && output.address === this.to) {
|
|
421
|
-
output.value += Number(value);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
throw new Error('Output not found');
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* @description Returns the transaction opnet fee
|
|
431
|
-
* @protected
|
|
432
|
-
* @returns {bigint}
|
|
433
|
-
*/
|
|
434
|
-
protected getTransactionOPNetFee(): bigint {
|
|
435
|
-
if (this.priorityFee > TransactionBuilder.MINIMUM_DUST) {
|
|
436
|
-
return this.priorityFee;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return TransactionBuilder.MINIMUM_DUST;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* @description Returns the total amount of satoshis in the inputs
|
|
444
|
-
* @protected
|
|
445
|
-
* @returns {bigint}
|
|
446
|
-
*/
|
|
447
|
-
protected calculateTotalUTXOAmount(): bigint {
|
|
448
|
-
let total: bigint = 0n;
|
|
449
|
-
for (let utxo of this.utxos) {
|
|
450
|
-
total += utxo.value;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return total;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* @description Returns the total amount of satoshis in the outputs
|
|
458
|
-
* @protected
|
|
459
|
-
* @returns {bigint}
|
|
460
|
-
*/
|
|
461
|
-
protected calculateTotalVOutAmount(): bigint {
|
|
462
|
-
let total: bigint = 0n;
|
|
463
|
-
for (let utxo of this.utxos) {
|
|
464
|
-
total += utxo.value;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return total;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* @description Adds the inputs from the utxos
|
|
472
|
-
* @protected
|
|
473
|
-
* @returns {void}
|
|
474
|
-
*/
|
|
475
|
-
protected addInputsFromUTXO(): void {
|
|
476
|
-
if (this.utxos.length) {
|
|
477
|
-
//throw new Error('No UTXOs specified');
|
|
478
|
-
|
|
479
|
-
if (this.totalInputAmount < TransactionBuilder.MINIMUM_DUST) {
|
|
480
|
-
throw new Error(
|
|
481
|
-
`Total input amount is ${this.totalInputAmount} sat which is less than the minimum dust ${TransactionBuilder.MINIMUM_DUST} sat.`,
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
for (let i = 0; i < this.utxos.length; i++) {
|
|
486
|
-
const utxo = this.utxos[i];
|
|
487
|
-
const input = this.generatePsbtInputExtended(utxo, i);
|
|
488
|
-
|
|
489
|
-
this.addInput(input);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* Internal init.
|
|
496
|
-
* @protected
|
|
497
|
-
*/
|
|
498
|
-
protected override internalInit(): void {
|
|
499
|
-
this.verifyUTXOValidity();
|
|
500
|
-
|
|
501
|
-
super.internalInit();
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Builds the transaction.
|
|
506
|
-
* @protected
|
|
507
|
-
* @returns {Promise<void>}
|
|
508
|
-
*/
|
|
509
|
-
protected abstract buildTransaction(): Promise<void>;
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Add an input update
|
|
513
|
-
* @param {UpdateInput} input - The input to update
|
|
514
|
-
* @protected
|
|
515
|
-
* @returns {void}
|
|
516
|
-
*/
|
|
517
|
-
protected updateInput(input: UpdateInput): void {
|
|
518
|
-
this.updateInputs.push(input);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Returns the witness of the tap transaction.
|
|
523
|
-
* @protected
|
|
524
|
-
* @returns {Buffer}
|
|
525
|
-
*/
|
|
526
|
-
protected getWitness(): Buffer {
|
|
527
|
-
if (!this.tapData || !this.tapData.witness) {
|
|
528
|
-
throw new Error('Witness is required');
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (this.tapData.witness.length === 0) {
|
|
532
|
-
throw new Error('Witness is empty');
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return this.tapData.witness[this.tapData.witness.length - 1];
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Returns the tap output.
|
|
540
|
-
* @protected
|
|
541
|
-
* @returns {Buffer}
|
|
542
|
-
*/
|
|
543
|
-
protected getTapOutput(): Buffer {
|
|
544
|
-
if (!this.tapData || !this.tapData.output) {
|
|
545
|
-
throw new Error('Tap data is required');
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
return this.tapData.output;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* Returns the inputs of the transaction.
|
|
553
|
-
* @protected
|
|
554
|
-
* @returns {PsbtInputExtended[]}
|
|
555
|
-
*/
|
|
556
|
-
protected getInputs(): PsbtInputExtended[] {
|
|
557
|
-
return this.inputs;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Returns the outputs of the transaction.
|
|
562
|
-
* @protected
|
|
563
|
-
* @returns {PsbtOutputExtended[]}
|
|
564
|
-
*/
|
|
565
|
-
protected getOutputs(): PsbtOutputExtended[] {
|
|
566
|
-
const outputs: PsbtOutputExtended[] = [...this.outputs];
|
|
567
|
-
if (this.feeOutput) outputs.push(this.feeOutput);
|
|
568
|
-
|
|
569
|
-
return outputs;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Verifies that the utxos are valid.
|
|
574
|
-
* @protected
|
|
575
|
-
*/
|
|
576
|
-
protected verifyUTXOValidity(): void {
|
|
577
|
-
for (let utxo of this.utxos) {
|
|
578
|
-
if (!utxo.scriptPubKey) {
|
|
579
|
-
throw new Error('Address is required');
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Set transaction fee output.
|
|
586
|
-
* @param {PsbtOutputExtended} output - The output to set the fees
|
|
587
|
-
* @protected
|
|
588
|
-
* @returns {Promise<void>}
|
|
589
|
-
*/
|
|
590
|
-
protected async setFeeOutput(output: PsbtOutputExtended): Promise<void> {
|
|
591
|
-
const initialValue = output.value;
|
|
592
|
-
|
|
593
|
-
let fee = await this.estimateTransactionFees();
|
|
594
|
-
output.value = initialValue - Number(fee);
|
|
595
|
-
|
|
596
|
-
if (output.value < TransactionBuilder.MINIMUM_DUST) {
|
|
597
|
-
this.feeOutput = null;
|
|
598
|
-
|
|
599
|
-
if (output.value < 0) {
|
|
600
|
-
throw new Error(
|
|
601
|
-
`setFeeOutput: Insufficient funds to pay the fees. Fee: ${fee} > Value: ${initialValue}. Total input: ${this.totalInputAmount} sat`,
|
|
602
|
-
);
|
|
603
|
-
}
|
|
604
|
-
} else {
|
|
605
|
-
this.feeOutput = output;
|
|
606
|
-
|
|
607
|
-
let fee = await this.estimateTransactionFees();
|
|
608
|
-
if (fee > BigInt(initialValue)) {
|
|
609
|
-
throw new Error(
|
|
610
|
-
`estimateTransactionFees: Insufficient funds to pay the fees. Fee: ${fee} > Value: ${initialValue}. Total input: ${this.totalInputAmount} sat`,
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
const valueLeft = initialValue - Number(fee);
|
|
615
|
-
if (valueLeft < TransactionBuilder.MINIMUM_DUST) {
|
|
616
|
-
this.feeOutput = null;
|
|
617
|
-
} else {
|
|
618
|
-
this.feeOutput.value = valueLeft;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
this.overflowFees = BigInt(valueLeft);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/**
|
|
626
|
-
* Builds the transaction.
|
|
627
|
-
* @param {Psbt} transaction - The transaction to build
|
|
628
|
-
* @protected
|
|
629
|
-
* @returns {Promise<boolean>}
|
|
630
|
-
* @throws {Error} - If something went wrong while building the transaction
|
|
631
|
-
*/
|
|
632
|
-
protected async internalBuildTransaction(transaction: Psbt): Promise<boolean> {
|
|
633
|
-
if (transaction.data.inputs.length === 0) {
|
|
634
|
-
const inputs: PsbtInputExtended[] = this.getInputs();
|
|
635
|
-
const outputs: PsbtOutputExtended[] = this.getOutputs();
|
|
636
|
-
|
|
637
|
-
transaction.setMaximumFeeRate(this._maximumFeeRate);
|
|
638
|
-
transaction.addInputs(inputs);
|
|
639
|
-
|
|
640
|
-
for (let i = 0; i < this.updateInputs.length; i++) {
|
|
641
|
-
transaction.updateInput(i, this.updateInputs[i]);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
transaction.addOutputs(outputs);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
try {
|
|
648
|
-
await this.signInputs(transaction);
|
|
649
|
-
|
|
650
|
-
if (this.finalized) {
|
|
651
|
-
this.transactionFee = BigInt(transaction.getFee());
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
return true;
|
|
655
|
-
} catch (e) {
|
|
656
|
-
const err: Error = e as Error;
|
|
657
|
-
|
|
658
|
-
this.error(
|
|
659
|
-
`[internalBuildTransaction] Something went wrong while getting building the transaction: ${err.stack}`,
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
return false;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
1
|
+
import { initEccLib, Network, opcodes, Psbt, script, Signer, Transaction } from 'bitcoinjs-lib';
|
|
2
|
+
import { varuint } from 'bitcoinjs-lib/src/bufferutils.js';
|
|
3
|
+
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
4
|
+
import { PsbtInputExtended, PsbtOutputExtended, UpdateInput } from '../interfaces/Tap.js';
|
|
5
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
6
|
+
import {
|
|
7
|
+
IFundingTransactionParameters,
|
|
8
|
+
ITransactionParameters,
|
|
9
|
+
} from '../interfaces/ITransactionParameters.js';
|
|
10
|
+
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
11
|
+
import { Address } from '@btc-vision/bsi-binary';
|
|
12
|
+
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
13
|
+
import { ECPairInterface } from 'ecpair';
|
|
14
|
+
import { AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
15
|
+
import { TweakedTransaction } from '../shared/TweakedTransaction.js';
|
|
16
|
+
|
|
17
|
+
initEccLib(ecc);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Allows to build a transaction like you would on Ethereum.
|
|
21
|
+
* @description The transaction builder class
|
|
22
|
+
* @abstract
|
|
23
|
+
* @class TransactionBuilder
|
|
24
|
+
*/
|
|
25
|
+
export abstract class TransactionBuilder<T extends TransactionType> extends TweakedTransaction {
|
|
26
|
+
public static readonly LOCK_LEAF_SCRIPT: Buffer = script.compile([
|
|
27
|
+
opcodes.OP_0,
|
|
28
|
+
//opcodes.OP_VERIFY, - verify that this is not needed.
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
public static readonly MINIMUM_DUST: bigint = 330n;
|
|
32
|
+
|
|
33
|
+
public abstract readonly type: T;
|
|
34
|
+
public readonly logColor: string = '#785def';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @description The overflow fees of the transaction
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
public overflowFees: bigint = 0n;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @description Cost in satoshis of the transaction fee
|
|
44
|
+
*/
|
|
45
|
+
public transactionFee: bigint = 0n;
|
|
46
|
+
/**
|
|
47
|
+
* @description The estimated fees of the transaction
|
|
48
|
+
*/
|
|
49
|
+
public estimatedFees: bigint = 0n;
|
|
50
|
+
/**
|
|
51
|
+
* @description The transaction itself.
|
|
52
|
+
*/
|
|
53
|
+
protected transaction: Psbt;
|
|
54
|
+
/**
|
|
55
|
+
* @description Inputs to update later on.
|
|
56
|
+
*/
|
|
57
|
+
protected readonly updateInputs: UpdateInput[] = [];
|
|
58
|
+
/**
|
|
59
|
+
* @description The outputs of the transaction
|
|
60
|
+
*/
|
|
61
|
+
protected readonly outputs: PsbtOutputExtended[] = [];
|
|
62
|
+
/**
|
|
63
|
+
* @description Output that will be used to pay the fees
|
|
64
|
+
*/
|
|
65
|
+
protected feeOutput: PsbtOutputExtended | null = null;
|
|
66
|
+
/**
|
|
67
|
+
* @description The total amount of satoshis in the inputs
|
|
68
|
+
*/
|
|
69
|
+
protected totalInputAmount: bigint;
|
|
70
|
+
/**
|
|
71
|
+
* @description The signer of the transaction
|
|
72
|
+
*/
|
|
73
|
+
protected readonly signer: Signer;
|
|
74
|
+
/**
|
|
75
|
+
* @description The network where the transaction will be broadcasted
|
|
76
|
+
*/
|
|
77
|
+
protected readonly network: Network;
|
|
78
|
+
/**
|
|
79
|
+
* @description The fee rate of the transaction
|
|
80
|
+
*/
|
|
81
|
+
protected readonly feeRate: number;
|
|
82
|
+
/**
|
|
83
|
+
* @description The opnet priority fee of the transaction
|
|
84
|
+
*/
|
|
85
|
+
protected priorityFee: bigint;
|
|
86
|
+
/**
|
|
87
|
+
* @description The utxos used in the transaction
|
|
88
|
+
*/
|
|
89
|
+
protected utxos: UTXO[];
|
|
90
|
+
/**
|
|
91
|
+
* @description The address where the transaction is sent to
|
|
92
|
+
* @protected
|
|
93
|
+
*/
|
|
94
|
+
protected to: Address | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* @description The address where the transaction is sent from
|
|
97
|
+
* @protected
|
|
98
|
+
*/
|
|
99
|
+
protected from: Address;
|
|
100
|
+
/**
|
|
101
|
+
* @description The maximum fee rate of the transaction
|
|
102
|
+
*/
|
|
103
|
+
protected _maximumFeeRate: number = 100000000;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {ITransactionParameters} parameters - The transaction parameters
|
|
107
|
+
*/
|
|
108
|
+
protected constructor(parameters: ITransactionParameters) {
|
|
109
|
+
super(parameters);
|
|
110
|
+
|
|
111
|
+
if (parameters.estimatedFees) {
|
|
112
|
+
this.estimatedFees = parameters.estimatedFees;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.signer = parameters.signer;
|
|
116
|
+
this.network = parameters.network;
|
|
117
|
+
this.feeRate = parameters.feeRate;
|
|
118
|
+
this.priorityFee = parameters.priorityFee;
|
|
119
|
+
this.utxos = parameters.utxos;
|
|
120
|
+
this.to = parameters.to || undefined;
|
|
121
|
+
|
|
122
|
+
this.from = TransactionBuilder.getFrom(
|
|
123
|
+
parameters.from,
|
|
124
|
+
this.signer as ECPairInterface,
|
|
125
|
+
this.network,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
this.totalInputAmount = this.calculateTotalUTXOAmount();
|
|
129
|
+
|
|
130
|
+
const totalVOut: bigint = this.calculateTotalVOutAmount();
|
|
131
|
+
if (totalVOut < this.totalInputAmount) {
|
|
132
|
+
throw new Error(`Vout value is less than the value to send`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.transaction = new Psbt({
|
|
136
|
+
network: this.network,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public static getFrom(
|
|
141
|
+
from: string | undefined,
|
|
142
|
+
keypair: ECPairInterface,
|
|
143
|
+
network: Network,
|
|
144
|
+
): Address {
|
|
145
|
+
return from || EcKeyPair.getTaprootAddress(keypair, network);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @description Converts the witness stack to a script witness
|
|
150
|
+
* @param {Buffer[]} witness - The witness stack
|
|
151
|
+
* @protected
|
|
152
|
+
* @returns {Buffer}
|
|
153
|
+
*/
|
|
154
|
+
public static witnessStackToScriptWitness(witness: Buffer[]): Buffer {
|
|
155
|
+
let buffer = Buffer.allocUnsafe(0);
|
|
156
|
+
|
|
157
|
+
function writeSlice(slice: Buffer) {
|
|
158
|
+
buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function writeVarInt(i: number) {
|
|
162
|
+
const currentLen = buffer.length;
|
|
163
|
+
const varintLen = varuint.encodingLength(i);
|
|
164
|
+
|
|
165
|
+
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
166
|
+
varuint.encode(i, buffer, currentLen);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function writeVarSlice(slice: Buffer) {
|
|
170
|
+
writeVarInt(slice.length);
|
|
171
|
+
writeSlice(slice);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function writeVector(vector: Buffer[]) {
|
|
175
|
+
writeVarInt(vector.length);
|
|
176
|
+
vector.forEach(writeVarSlice);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
writeVector(witness);
|
|
180
|
+
|
|
181
|
+
return buffer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public async getFundingTransactionParameters(): Promise<IFundingTransactionParameters> {
|
|
185
|
+
if (!this.estimatedFees) {
|
|
186
|
+
this.estimatedFees = await this.estimateTransactionFees();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
utxos: this.utxos,
|
|
191
|
+
to: this.getScriptAddress(),
|
|
192
|
+
signer: this.signer,
|
|
193
|
+
network: this.network,
|
|
194
|
+
feeRate: this.feeRate,
|
|
195
|
+
priorityFee: this.priorityFee,
|
|
196
|
+
from: this.from,
|
|
197
|
+
amount: this.estimatedFees,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Set the destination address of the transaction
|
|
203
|
+
* @param {Address} address - The address to set
|
|
204
|
+
*/
|
|
205
|
+
public setDestinationAddress(address: Address): void {
|
|
206
|
+
this.to = address; // this.getScriptAddress()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Set the maximum fee rate of the transaction in satoshis per byte
|
|
211
|
+
* @param {number} feeRate - The fee rate to set
|
|
212
|
+
* @public
|
|
213
|
+
*/
|
|
214
|
+
public setMaximumFeeRate(feeRate: number): void {
|
|
215
|
+
this._maximumFeeRate = feeRate;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @description Signs the transaction
|
|
220
|
+
* @public
|
|
221
|
+
* @returns {Promise<Transaction>} - The signed transaction in hex format
|
|
222
|
+
* @throws {Error} - If something went wrong
|
|
223
|
+
*/
|
|
224
|
+
public async signTransaction(): Promise<Transaction> {
|
|
225
|
+
if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
'Invalid contract address. The contract address must be a taproot address.',
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (this.signed) throw new Error('Transaction is already signed');
|
|
232
|
+
this.signed = true;
|
|
233
|
+
|
|
234
|
+
await this.buildTransaction();
|
|
235
|
+
|
|
236
|
+
const builtTx = await this.internalBuildTransaction(this.transaction);
|
|
237
|
+
if (builtTx) {
|
|
238
|
+
if (this.regenerated) {
|
|
239
|
+
throw new Error('Transaction was regenerated');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return this.transaction.extractTransaction(false);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
throw new Error('Could not sign transaction');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @description Generates the transaction minimal signatures
|
|
250
|
+
* @public
|
|
251
|
+
*/
|
|
252
|
+
public async generateTransactionMinimalSignatures(): Promise<void> {
|
|
253
|
+
if (this.to && !EcKeyPair.verifyContractAddress(this.to, this.network)) {
|
|
254
|
+
throw new Error(
|
|
255
|
+
'Invalid contract address. The contract address must be a taproot address.',
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
await this.buildTransaction();
|
|
260
|
+
|
|
261
|
+
if (this.transaction.data.inputs.length === 0) {
|
|
262
|
+
const inputs: PsbtInputExtended[] = this.getInputs();
|
|
263
|
+
const outputs: PsbtOutputExtended[] = this.getOutputs();
|
|
264
|
+
|
|
265
|
+
this.transaction.setMaximumFeeRate(this._maximumFeeRate);
|
|
266
|
+
this.transaction.addInputs(inputs);
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < this.updateInputs.length; i++) {
|
|
269
|
+
this.transaction.updateInput(i, this.updateInputs[i]);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
this.transaction.addOutputs(outputs);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @description Signs the transaction
|
|
278
|
+
* @public
|
|
279
|
+
* @returns {Promise<Psbt>} - The signed transaction in hex format
|
|
280
|
+
* @throws {Error} - If something went wrong
|
|
281
|
+
*/
|
|
282
|
+
public async signPSBT(): Promise<Psbt> {
|
|
283
|
+
if (await this.signTransaction()) {
|
|
284
|
+
return this.transaction;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
throw new Error('Could not sign transaction');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Add an input to the transaction.
|
|
292
|
+
* @param {PsbtInputExtended} input - The input to add
|
|
293
|
+
* @public
|
|
294
|
+
* @returns {void}
|
|
295
|
+
*/
|
|
296
|
+
public addInput(input: PsbtInputExtended): void {
|
|
297
|
+
this.inputs.push(input);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Add an output to the transaction.
|
|
302
|
+
* @param {PsbtOutputExtended} output - The output to add
|
|
303
|
+
* @public
|
|
304
|
+
* @returns {void}
|
|
305
|
+
*/
|
|
306
|
+
public addOutput(output: PsbtOutputExtended): void {
|
|
307
|
+
if (output.value === 0) return;
|
|
308
|
+
if (output.value < TransactionBuilder.MINIMUM_DUST) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Output value is less than the minimum dust ${output.value} < ${TransactionBuilder.MINIMUM_DUST}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.outputs.push(output);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Receiver address.
|
|
319
|
+
* @public
|
|
320
|
+
* @returns {Address} - The receiver address
|
|
321
|
+
*/
|
|
322
|
+
public toAddress(): string | undefined {
|
|
323
|
+
return this.to;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @description Returns the script address
|
|
328
|
+
* @returns {Address} - The script address
|
|
329
|
+
*/
|
|
330
|
+
public address(): Address | undefined {
|
|
331
|
+
return this.tapData?.address;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Estimates the transaction fees.
|
|
336
|
+
* @public
|
|
337
|
+
* @returns {Promise<bigint>} - The estimated transaction fees
|
|
338
|
+
*/
|
|
339
|
+
public async estimateTransactionFees(): Promise<bigint> {
|
|
340
|
+
if (this.estimatedFees) return this.estimatedFees;
|
|
341
|
+
|
|
342
|
+
const fakeTx = new Psbt({
|
|
343
|
+
network: this.network,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const builtTx = await this.internalBuildTransaction(fakeTx);
|
|
347
|
+
if (builtTx) {
|
|
348
|
+
const tx = fakeTx.extractTransaction(true, true);
|
|
349
|
+
const size = tx.virtualSize();
|
|
350
|
+
const fee: number = this.feeRate * size;
|
|
351
|
+
|
|
352
|
+
this.estimatedFees = BigInt(Math.ceil(fee) + 1);
|
|
353
|
+
|
|
354
|
+
return this.estimatedFees;
|
|
355
|
+
} else {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`Could not build transaction to estimate fee. Something went wrong while building the transaction.`,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
public async rebuildFromBase64(base64: string): Promise<Psbt> {
|
|
363
|
+
this.transaction = Psbt.fromBase64(base64, { network: this.network });
|
|
364
|
+
this.signed = false;
|
|
365
|
+
|
|
366
|
+
this.sighashTypes = [Transaction.SIGHASH_ANYONECANPAY, Transaction.SIGHASH_ALL];
|
|
367
|
+
|
|
368
|
+
return await this.signPSBT();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
public setPSBT(psbt: Psbt): void {
|
|
372
|
+
this.transaction = psbt;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @description Adds the refund output to the transaction
|
|
377
|
+
* @param {bigint} amountSpent - The amount spent
|
|
378
|
+
* @protected
|
|
379
|
+
* @returns {Promise<void>}
|
|
380
|
+
*/
|
|
381
|
+
protected async addRefundOutput(amountSpent: bigint): Promise<void> {
|
|
382
|
+
/** Add the refund output */
|
|
383
|
+
const sendBackAmount: bigint = this.totalInputAmount - amountSpent;
|
|
384
|
+
if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
|
|
385
|
+
if (AddressVerificator.isValidP2TRAddress(this.from, this.network)) {
|
|
386
|
+
await this.setFeeOutput({
|
|
387
|
+
value: Number(sendBackAmount),
|
|
388
|
+
address: this.from,
|
|
389
|
+
tapInternalKey: this.internalPubKeyToXOnly(),
|
|
390
|
+
});
|
|
391
|
+
} else {
|
|
392
|
+
await this.setFeeOutput({
|
|
393
|
+
value: Number(sendBackAmount),
|
|
394
|
+
address: this.from,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.warn(
|
|
402
|
+
`Amount to send back (${sendBackAmount} sat) is less than the minimum dust (${TransactionBuilder.MINIMUM_DUST} sat), it will be consumed in fees instead.`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @description Adds the value to the output
|
|
408
|
+
* @param {number | bigint} value - The value to add
|
|
409
|
+
* @protected
|
|
410
|
+
* @returns {void}
|
|
411
|
+
*/
|
|
412
|
+
protected addValueToToOutput(value: number | bigint): void {
|
|
413
|
+
if (value < TransactionBuilder.MINIMUM_DUST) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
`Value to send is less than the minimum dust ${value} < ${TransactionBuilder.MINIMUM_DUST}`,
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for (let output of this.outputs) {
|
|
420
|
+
if ('address' in output && output.address === this.to) {
|
|
421
|
+
output.value += Number(value);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
throw new Error('Output not found');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* @description Returns the transaction opnet fee
|
|
431
|
+
* @protected
|
|
432
|
+
* @returns {bigint}
|
|
433
|
+
*/
|
|
434
|
+
protected getTransactionOPNetFee(): bigint {
|
|
435
|
+
if (this.priorityFee > TransactionBuilder.MINIMUM_DUST) {
|
|
436
|
+
return this.priorityFee;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return TransactionBuilder.MINIMUM_DUST;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* @description Returns the total amount of satoshis in the inputs
|
|
444
|
+
* @protected
|
|
445
|
+
* @returns {bigint}
|
|
446
|
+
*/
|
|
447
|
+
protected calculateTotalUTXOAmount(): bigint {
|
|
448
|
+
let total: bigint = 0n;
|
|
449
|
+
for (let utxo of this.utxos) {
|
|
450
|
+
total += utxo.value;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return total;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @description Returns the total amount of satoshis in the outputs
|
|
458
|
+
* @protected
|
|
459
|
+
* @returns {bigint}
|
|
460
|
+
*/
|
|
461
|
+
protected calculateTotalVOutAmount(): bigint {
|
|
462
|
+
let total: bigint = 0n;
|
|
463
|
+
for (let utxo of this.utxos) {
|
|
464
|
+
total += utxo.value;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return total;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @description Adds the inputs from the utxos
|
|
472
|
+
* @protected
|
|
473
|
+
* @returns {void}
|
|
474
|
+
*/
|
|
475
|
+
protected addInputsFromUTXO(): void {
|
|
476
|
+
if (this.utxos.length) {
|
|
477
|
+
//throw new Error('No UTXOs specified');
|
|
478
|
+
|
|
479
|
+
if (this.totalInputAmount < TransactionBuilder.MINIMUM_DUST) {
|
|
480
|
+
throw new Error(
|
|
481
|
+
`Total input amount is ${this.totalInputAmount} sat which is less than the minimum dust ${TransactionBuilder.MINIMUM_DUST} sat.`,
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
for (let i = 0; i < this.utxos.length; i++) {
|
|
486
|
+
const utxo = this.utxos[i];
|
|
487
|
+
const input = this.generatePsbtInputExtended(utxo, i);
|
|
488
|
+
|
|
489
|
+
this.addInput(input);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Internal init.
|
|
496
|
+
* @protected
|
|
497
|
+
*/
|
|
498
|
+
protected override internalInit(): void {
|
|
499
|
+
this.verifyUTXOValidity();
|
|
500
|
+
|
|
501
|
+
super.internalInit();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Builds the transaction.
|
|
506
|
+
* @protected
|
|
507
|
+
* @returns {Promise<void>}
|
|
508
|
+
*/
|
|
509
|
+
protected abstract buildTransaction(): Promise<void>;
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Add an input update
|
|
513
|
+
* @param {UpdateInput} input - The input to update
|
|
514
|
+
* @protected
|
|
515
|
+
* @returns {void}
|
|
516
|
+
*/
|
|
517
|
+
protected updateInput(input: UpdateInput): void {
|
|
518
|
+
this.updateInputs.push(input);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Returns the witness of the tap transaction.
|
|
523
|
+
* @protected
|
|
524
|
+
* @returns {Buffer}
|
|
525
|
+
*/
|
|
526
|
+
protected getWitness(): Buffer {
|
|
527
|
+
if (!this.tapData || !this.tapData.witness) {
|
|
528
|
+
throw new Error('Witness is required');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (this.tapData.witness.length === 0) {
|
|
532
|
+
throw new Error('Witness is empty');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return this.tapData.witness[this.tapData.witness.length - 1];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Returns the tap output.
|
|
540
|
+
* @protected
|
|
541
|
+
* @returns {Buffer}
|
|
542
|
+
*/
|
|
543
|
+
protected getTapOutput(): Buffer {
|
|
544
|
+
if (!this.tapData || !this.tapData.output) {
|
|
545
|
+
throw new Error('Tap data is required');
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return this.tapData.output;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Returns the inputs of the transaction.
|
|
553
|
+
* @protected
|
|
554
|
+
* @returns {PsbtInputExtended[]}
|
|
555
|
+
*/
|
|
556
|
+
protected getInputs(): PsbtInputExtended[] {
|
|
557
|
+
return this.inputs;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Returns the outputs of the transaction.
|
|
562
|
+
* @protected
|
|
563
|
+
* @returns {PsbtOutputExtended[]}
|
|
564
|
+
*/
|
|
565
|
+
protected getOutputs(): PsbtOutputExtended[] {
|
|
566
|
+
const outputs: PsbtOutputExtended[] = [...this.outputs];
|
|
567
|
+
if (this.feeOutput) outputs.push(this.feeOutput);
|
|
568
|
+
|
|
569
|
+
return outputs;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Verifies that the utxos are valid.
|
|
574
|
+
* @protected
|
|
575
|
+
*/
|
|
576
|
+
protected verifyUTXOValidity(): void {
|
|
577
|
+
for (let utxo of this.utxos) {
|
|
578
|
+
if (!utxo.scriptPubKey) {
|
|
579
|
+
throw new Error('Address is required');
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Set transaction fee output.
|
|
586
|
+
* @param {PsbtOutputExtended} output - The output to set the fees
|
|
587
|
+
* @protected
|
|
588
|
+
* @returns {Promise<void>}
|
|
589
|
+
*/
|
|
590
|
+
protected async setFeeOutput(output: PsbtOutputExtended): Promise<void> {
|
|
591
|
+
const initialValue = output.value;
|
|
592
|
+
|
|
593
|
+
let fee = await this.estimateTransactionFees();
|
|
594
|
+
output.value = initialValue - Number(fee);
|
|
595
|
+
|
|
596
|
+
if (output.value < TransactionBuilder.MINIMUM_DUST) {
|
|
597
|
+
this.feeOutput = null;
|
|
598
|
+
|
|
599
|
+
if (output.value < 0) {
|
|
600
|
+
throw new Error(
|
|
601
|
+
`setFeeOutput: Insufficient funds to pay the fees. Fee: ${fee} > Value: ${initialValue}. Total input: ${this.totalInputAmount} sat`,
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
} else {
|
|
605
|
+
this.feeOutput = output;
|
|
606
|
+
|
|
607
|
+
let fee = await this.estimateTransactionFees();
|
|
608
|
+
if (fee > BigInt(initialValue)) {
|
|
609
|
+
throw new Error(
|
|
610
|
+
`estimateTransactionFees: Insufficient funds to pay the fees. Fee: ${fee} > Value: ${initialValue}. Total input: ${this.totalInputAmount} sat`,
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const valueLeft = initialValue - Number(fee);
|
|
615
|
+
if (valueLeft < TransactionBuilder.MINIMUM_DUST) {
|
|
616
|
+
this.feeOutput = null;
|
|
617
|
+
} else {
|
|
618
|
+
this.feeOutput.value = valueLeft;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
this.overflowFees = BigInt(valueLeft);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Builds the transaction.
|
|
627
|
+
* @param {Psbt} transaction - The transaction to build
|
|
628
|
+
* @protected
|
|
629
|
+
* @returns {Promise<boolean>}
|
|
630
|
+
* @throws {Error} - If something went wrong while building the transaction
|
|
631
|
+
*/
|
|
632
|
+
protected async internalBuildTransaction(transaction: Psbt): Promise<boolean> {
|
|
633
|
+
if (transaction.data.inputs.length === 0) {
|
|
634
|
+
const inputs: PsbtInputExtended[] = this.getInputs();
|
|
635
|
+
const outputs: PsbtOutputExtended[] = this.getOutputs();
|
|
636
|
+
|
|
637
|
+
transaction.setMaximumFeeRate(this._maximumFeeRate);
|
|
638
|
+
transaction.addInputs(inputs);
|
|
639
|
+
|
|
640
|
+
for (let i = 0; i < this.updateInputs.length; i++) {
|
|
641
|
+
transaction.updateInput(i, this.updateInputs[i]);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
transaction.addOutputs(outputs);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
try {
|
|
648
|
+
await this.signInputs(transaction);
|
|
649
|
+
|
|
650
|
+
if (this.finalized) {
|
|
651
|
+
this.transactionFee = BigInt(transaction.getFee());
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return true;
|
|
655
|
+
} catch (e) {
|
|
656
|
+
const err: Error = e as Error;
|
|
657
|
+
|
|
658
|
+
this.error(
|
|
659
|
+
`[internalBuildTransaction] Something went wrong while getting building the transaction: ${err.stack}`,
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
}
|