@atomicfinance/bitcoin-js-wallet-provider 3.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # @atomicfinance/bitcoin-js-wallet-provider
2
+
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - a06082e: Create unified standalone `bitcoin-abstraction-layer` package
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [a06082e]
12
+ - @atomicfinance/bitcoin-wallet-provider@3.0.0
13
+ - @atomicfinance/types@3.0.0
14
+ - @atomicfinance/bitcoin-utils@3.0.0
15
+ - @atomicfinance/utils@3.0.0
@@ -0,0 +1,197 @@
1
+ /// <reference types="node" />
2
+ import Provider from '@atomicfinance/provider';
3
+ import { Address, bitcoin as bT, CreateMultisigResponse, finalizePSBTResponse, Input, Output, Transaction } from '@atomicfinance/types';
4
+ import { BIP32Interface } from 'bip32';
5
+ import { BitcoinNetwork } from 'bitcoin-networks';
6
+ import * as bitcoin from 'bitcoinjs-lib';
7
+ import { ECPairInterface } from 'bitcoinjs-lib';
8
+ declare type WalletProviderConstructor<T = Provider> = new (...args: any[]) => T;
9
+ interface BitcoinJsWalletProviderOptions {
10
+ network: BitcoinNetwork;
11
+ mnemonic: string;
12
+ baseDerivationPath: string;
13
+ addressType?: bT.AddressType;
14
+ }
15
+ declare const BitcoinJsWalletProvider_base: (new (...args: any[]) => {
16
+ _network: BitcoinNetwork;
17
+ _unusedAddressesBlacklist: {
18
+ [address: string]: true;
19
+ };
20
+ _maxAddressesToDerive: number;
21
+ _baseDerivationPath: string;
22
+ _addressType: bT.AddressType;
23
+ _derivationCache: {
24
+ [index: string]: Address;
25
+ };
26
+ baseDerivationNode(): Promise<BIP32Interface>;
27
+ _buildTransaction(targets: bT.OutputTarget[], feePerByte?: number, fixedInputs?: bT.Input[]): Promise<{
28
+ hex: string;
29
+ fee: number;
30
+ }>;
31
+ _buildSweepTransaction(externalChangeAddress: string, feePerByte?: number): Promise<{
32
+ hex: string;
33
+ fee: number;
34
+ }>;
35
+ signPSBT(data: string, inputs: bT.PsbtInputTarget[]): Promise<string>;
36
+ signBatchP2SHTransaction(inputs: [{
37
+ inputTxHex: string;
38
+ index: number;
39
+ vout: any;
40
+ outputScript: Buffer;
41
+ }], addresses: string, tx: any, lockTime?: number, segwit?: boolean): Promise<Buffer[]>;
42
+ getDerivationCache(): {
43
+ [index: string]: Address;
44
+ };
45
+ setDerivationCache(derivationCache: {
46
+ [index: string]: Address;
47
+ }): Promise<void>;
48
+ sendOptionsToOutputs(transactions: import("@atomicfinance/types").SendOptions[]): bT.OutputTarget[];
49
+ buildTransaction(output: bT.OutputTarget, feePerByte: number): Promise<{
50
+ hex: string;
51
+ fee: number;
52
+ }>;
53
+ buildBatchTransaction(outputs: bT.OutputTarget[]): Promise<{
54
+ hex: string;
55
+ fee: number;
56
+ }>;
57
+ _sendTransaction(transactions: bT.OutputTarget[], feePerByte?: number): Promise<Transaction<bT.Transaction>>;
58
+ sendTransaction(options: import("@atomicfinance/types").SendOptions): Promise<Transaction<bT.Transaction>>;
59
+ sendBatchTransaction(transactions: import("@atomicfinance/types").SendOptions[]): Promise<Transaction<bT.Transaction>>;
60
+ buildSweepTransaction(externalChangeAddress: string, feePerByte: number): Promise<{
61
+ hex: string;
62
+ fee: number;
63
+ }>;
64
+ sendSweepTransaction(externalChangeAddress: string | Address, feePerByte: number): Promise<Transaction<bT.Transaction>>;
65
+ getUnusedAddressesBlacklist(): {
66
+ [address: string]: true;
67
+ };
68
+ setUnusedAddressesBlacklist(unusedAddressesBlacklist: {
69
+ [address: string]: true;
70
+ }): void;
71
+ setMaxAddressesToDerive(maxAddressesToDerive: number): void;
72
+ getMaxAddressesToDerive(): number;
73
+ updateTransactionFee(tx: string | Transaction<bitcoin.Transaction>, newFeePerByte: number): Promise<Transaction<bT.Transaction>>;
74
+ getUnusedAddress(change?: boolean, numAddressPerCall?: number): Promise<any>;
75
+ _getUsedUnusedAddresses(numAddressPerCall: number, addressType: any): Promise<{
76
+ usedAddresses: any[];
77
+ unusedAddress: {
78
+ change: any;
79
+ nonChange: any;
80
+ };
81
+ firstUnusedAddress: any;
82
+ }>;
83
+ getWalletAddress(address: string): Promise<Address>;
84
+ getAddressFromPublicKey(publicKey: Buffer): string;
85
+ getPaymentVariantFromPublicKey(publicKey: Buffer): bitcoin.payments.Payment;
86
+ getDerivationPathAddress(path: string): Promise<Address>;
87
+ getAddresses(startingIndex?: number, numAddresses?: number, change?: boolean): Promise<Address[]>;
88
+ findAddress(addresses: string[], change?: boolean): Promise<Address>;
89
+ getUsedAddresses(numAddressPerCall?: number): Promise<any[]>;
90
+ withCachedUtxos(func: () => any): Promise<any>;
91
+ getTotalFee(opts: import("@atomicfinance/types").SendOptions, max: boolean): Promise<number>;
92
+ getTotalFees(transactions: import("@atomicfinance/types").SendOptions[], max: boolean): Promise<any>;
93
+ getInputsForAmount(_targets: bT.OutputTarget[], feePerByte?: number, fixedInputs?: bT.Input[], numAddressPerCall?: number, sweep?: boolean): Promise<{
94
+ inputs: bT.UTXO[];
95
+ change: any;
96
+ outputs: import("@atomicfinance/bitcoin-utils").CoinSelectTarget[];
97
+ fee: number;
98
+ }>;
99
+ client: import("@atomicfinance/types").IClient;
100
+ setClient(client?: import("@atomicfinance/types").IClient): void;
101
+ getMethod(method: string, requestor?: any): (...args: any[]) => any;
102
+ }) & WalletProviderConstructor<Provider>;
103
+ export default class BitcoinJsWalletProvider extends BitcoinJsWalletProvider_base {
104
+ _mnemonic: string;
105
+ _seedNode: BIP32Interface;
106
+ _baseDerivationNode: BIP32Interface;
107
+ constructor(options: BitcoinJsWalletProviderOptions);
108
+ seedNode(): Promise<BIP32Interface>;
109
+ baseDerivationNode(): Promise<BIP32Interface>;
110
+ keyPair(derivationPath: string): Promise<ECPairInterface>;
111
+ private _toWIF;
112
+ exportPrivateKey(): Promise<string>;
113
+ signMessage(message: string, from: string): Promise<string>;
114
+ _createMultisigPayment(m: number, pubkeys: string[]): bitcoin.Payment;
115
+ /**
116
+ * Creates a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
117
+ * https://developer.bitcoin.org/reference/rpc/createmultisig.html
118
+ * @param m the number of required signatures
119
+ * @param pubkeys n possible pubkeys in total
120
+ * @returns a json object containing the `address` and `redeemScript`
121
+ */
122
+ createMultisig(m: number, pubkeys: string[]): CreateMultisigResponse;
123
+ /**
124
+ * Creates a PSBT of a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
125
+ * https://developer.bitcoin.org/reference/rpc/createmultisig.html
126
+ * https://developer.bitcoin.org/reference/rpc/createpsbt.html
127
+ * @param m the number of required signatures
128
+ * @param pubkeys n possible pubkeys in total
129
+ * @param inputs the Inputs to the PSBT
130
+ * @param ouputs the Outputs to the PSBT
131
+ * @returns a base64 encoded psbt string
132
+ */
133
+ buildMultisigPSBT(m: number, pubkeys: string[], inputs: Input[], outputs: Output[]): string;
134
+ /**
135
+ * Update a PSBT with input information from our wallet and then sign inputs that we can sign for
136
+ * https://developer.bitcoin.org/reference/rpc/walletprocesspsbt.html
137
+ * @param psbt a base64 encoded psbt string (P2WSH only)
138
+ * @returns a base64 encoded signed psbt string
139
+ */
140
+ walletProcessPSBT(psbtString: string): Promise<string>;
141
+ /**
142
+ * Finalize the inputs of a PSBT. If the transaction is fully signed, it will
143
+ * produce a network serialized transaction which can be broadcast with sendrawtransaction
144
+ * https://developer.bitcoin.org/reference/rpc/finalizepsbt.html
145
+ * @param psbt a base64 encoded psbt string
146
+ * @returns a json object containing `psbt` in base64, `hex` for transaction and `complete` for if
147
+ * the transaction has a complete set of signatures
148
+ */
149
+ finalizePSBT(psbtString: string): finalizePSBTResponse;
150
+ sendSweepTransactionWithSetOutputs(externalChangeAddress: string, feePerByte: number, _outputs: Output[], fixedInputs: Input[]): Promise<Transaction<bT.Transaction>>;
151
+ buildSweepTransactionWithSetOutputs(externalChangeAddress: string, feePerByte: number, _outputs: Output[], fixedInputs: Input[]): Promise<{
152
+ hex: string;
153
+ fee: number;
154
+ }>;
155
+ _buildSweepTransactionWithSetOutputs(externalChangeAddress: string, feePerByte: number, _outputs: Output[], fixedInputs: Input[]): Promise<{
156
+ hex: string;
157
+ fee: number;
158
+ }>;
159
+ _getInputForAmountWithoutUtxoCheck(_outputs: Output[], _feePerByte: number, fixedInputs: Input[]): {
160
+ inputs: bT.UTXO[];
161
+ outputs: {
162
+ value: number;
163
+ id?: string;
164
+ }[];
165
+ fee: number;
166
+ change: {
167
+ value: number;
168
+ id?: string;
169
+ };
170
+ };
171
+ _buildTransactionWithoutUtxoCheck(outputs: Output[], feePerByte: number, fixedInputs: Input[]): Promise<{
172
+ hex: string;
173
+ fee: number;
174
+ }>;
175
+ _buildTransaction(targets: bT.OutputTarget[], feePerByte?: number, fixedInputs?: bT.Input[]): Promise<{
176
+ hex: string;
177
+ fee: number;
178
+ }>;
179
+ _buildSweepTransaction(externalChangeAddress: string, feePerByte: number): Promise<{
180
+ hex: string;
181
+ fee: number;
182
+ }>;
183
+ signPSBT(data: string, inputs: bT.PsbtInputTarget[]): Promise<string>;
184
+ signBatchP2SHTransaction(inputs: [
185
+ {
186
+ inputTxHex: string;
187
+ index: number;
188
+ vout: any;
189
+ outputScript: Buffer;
190
+ txInputIndex?: number;
191
+ }
192
+ ], addresses: string, tx: any, _lockTime?: number, segwit?: boolean): Promise<any[]>;
193
+ getScriptType(): "p2wpkh" | "p2pkh" | "p2sh-p2wpkh";
194
+ getConnectedNetwork(): Promise<BitcoinNetwork>;
195
+ isWalletAvailable(): Promise<boolean>;
196
+ }
197
+ export {};
@@ -0,0 +1,414 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ const bitcoin_utils_1 = require("@atomicfinance/bitcoin-utils");
26
+ const bitcoin_wallet_provider_1 = __importDefault(require("@atomicfinance/bitcoin-wallet-provider"));
27
+ const provider_1 = __importDefault(require("@atomicfinance/provider"));
28
+ const types_1 = require("@atomicfinance/types");
29
+ const assert_1 = __importDefault(require("assert"));
30
+ const bip32_1 = require("bip32");
31
+ const bip39_1 = require("bip39");
32
+ const bitcoin = __importStar(require("bitcoinjs-lib"));
33
+ const bitcoinjs_lib_1 = require("bitcoinjs-lib");
34
+ const bitcoinjs_message_1 = require("bitcoinjs-message");
35
+ const secp256k1_1 = __importDefault(require("secp256k1"));
36
+ const FEE_PER_BYTE_FALLBACK = 5;
37
+ class BitcoinJsWalletProvider extends bitcoin_wallet_provider_1.default(provider_1.default) {
38
+ constructor(options) {
39
+ const { network, mnemonic, baseDerivationPath, addressType = types_1.bitcoin.AddressType.BECH32, } = options;
40
+ super({ network, baseDerivationPath, addressType });
41
+ if (!mnemonic)
42
+ throw new Error('Mnemonic should not be empty');
43
+ this._mnemonic = mnemonic;
44
+ }
45
+ async seedNode() {
46
+ if (this._seedNode)
47
+ return this._seedNode;
48
+ const seed = await bip39_1.mnemonicToSeed(this._mnemonic);
49
+ this._seedNode = bip32_1.fromSeed(seed, this._network);
50
+ return this._seedNode;
51
+ }
52
+ async baseDerivationNode() {
53
+ if (this._baseDerivationNode)
54
+ return this._baseDerivationNode;
55
+ const baseNode = await this.seedNode();
56
+ this._baseDerivationNode = baseNode.derivePath(this._baseDerivationPath);
57
+ return this._baseDerivationNode;
58
+ }
59
+ async keyPair(derivationPath) {
60
+ const wif = await this._toWIF(derivationPath);
61
+ return bitcoinjs_lib_1.ECPair.fromWIF(wif, this._network);
62
+ }
63
+ async _toWIF(derivationPath) {
64
+ const node = await this.seedNode();
65
+ return node.derivePath(derivationPath).toWIF();
66
+ }
67
+ async exportPrivateKey() {
68
+ return this._toWIF(this._baseDerivationPath);
69
+ }
70
+ async signMessage(message, from) {
71
+ const address = await this.getWalletAddress(from);
72
+ const keyPair = await this.keyPair(address.derivationPath);
73
+ const signature = await bitcoinjs_message_1.signAsync(message, keyPair.privateKey, keyPair.compressed);
74
+ return signature.toString('hex');
75
+ }
76
+ _createMultisigPayment(m, pubkeys) {
77
+ if (m > pubkeys.length) {
78
+ throw new Error(`not enough keys supplied (got ${pubkeys.length} keys, but need at least ${m} to redeem)`);
79
+ }
80
+ // Create m-of-n multisig
81
+ const p2ms = bitcoin.payments.p2ms({
82
+ m: m,
83
+ pubkeys: pubkeys.map((key) => Buffer.from(key, 'hex')),
84
+ network: this._network,
85
+ });
86
+ // Create p2wsh for multisig
87
+ const p2wsh = bitcoin.payments.p2wsh({
88
+ redeem: p2ms,
89
+ network: this._network,
90
+ });
91
+ return p2wsh;
92
+ }
93
+ /**
94
+ * Creates a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
95
+ * https://developer.bitcoin.org/reference/rpc/createmultisig.html
96
+ * @param m the number of required signatures
97
+ * @param pubkeys n possible pubkeys in total
98
+ * @returns a json object containing the `address` and `redeemScript`
99
+ */
100
+ createMultisig(m, pubkeys) {
101
+ const p2wsh = this._createMultisigPayment(m, pubkeys);
102
+ return {
103
+ address: p2wsh.address,
104
+ redeemScript: p2wsh.redeem?.output?.toString('hex'),
105
+ };
106
+ }
107
+ /**
108
+ * Creates a PSBT of a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
109
+ * https://developer.bitcoin.org/reference/rpc/createmultisig.html
110
+ * https://developer.bitcoin.org/reference/rpc/createpsbt.html
111
+ * @param m the number of required signatures
112
+ * @param pubkeys n possible pubkeys in total
113
+ * @param inputs the Inputs to the PSBT
114
+ * @param ouputs the Outputs to the PSBT
115
+ * @returns a base64 encoded psbt string
116
+ */
117
+ buildMultisigPSBT(m, pubkeys, inputs, outputs) {
118
+ assert_1.default(inputs.length > 0, 'no inputs found');
119
+ assert_1.default(outputs.length > 0, 'no outputs found');
120
+ const p2wsh = this._createMultisigPayment(m, pubkeys);
121
+ // Verify pubkeyhash for all inputs matches the p2wsh hash
122
+ assert_1.default(inputs.every((input) => p2wsh.output.toString('hex') === input.scriptPubKey), 'address pubkeyhash does not match input scriptPubKey');
123
+ // creator
124
+ const psbt = new bitcoin.Psbt({ network: this._network });
125
+ // updater
126
+ inputs.forEach((input) => {
127
+ psbt.addInput({
128
+ hash: input.txid,
129
+ index: input.vout,
130
+ witnessUtxo: { script: p2wsh.output, value: input.value },
131
+ witnessScript: p2wsh.redeem.output,
132
+ });
133
+ });
134
+ outputs.forEach((output) => {
135
+ psbt.addOutput({
136
+ address: output.to,
137
+ value: output.value,
138
+ });
139
+ });
140
+ return psbt.toBase64();
141
+ }
142
+ /**
143
+ * Update a PSBT with input information from our wallet and then sign inputs that we can sign for
144
+ * https://developer.bitcoin.org/reference/rpc/walletprocesspsbt.html
145
+ * @param psbt a base64 encoded psbt string (P2WSH only)
146
+ * @returns a base64 encoded signed psbt string
147
+ */
148
+ async walletProcessPSBT(psbtString) {
149
+ const psbt = bitcoin.Psbt.fromBase64(psbtString);
150
+ await Promise.all(psbt.data.inputs.map(async (input, i) => {
151
+ assert_1.default(psbt.getInputType(i).slice(0, 5) === 'p2wsh', 'only accepts P2WSH inputs');
152
+ const scriptStack = bitcoin.script.decompile(input.witnessScript);
153
+ const pubkeys = scriptStack.filter((data) => Buffer.isBuffer(data) && secp256k1_1.default.publicKeyVerify(data));
154
+ await Promise.all(pubkeys.map(async (key) => {
155
+ // create address using pubkey
156
+ const { address: addressString } = bitcoin.payments.p2wpkh({
157
+ pubkey: key,
158
+ network: this._network,
159
+ });
160
+ // Retrieve address object from wallet using address
161
+ const address = await this.findAddress([addressString]);
162
+ // exit if address doesn't exist in wallet
163
+ if (!address)
164
+ return;
165
+ // derive keypair
166
+ const keyPair = await this.keyPair(address.derivationPath);
167
+ // sign PSBT using keypair
168
+ psbt.signInput(i, keyPair);
169
+ }));
170
+ }));
171
+ psbt.validateSignaturesOfAllInputs(); // ensure all signatures are valid!
172
+ return psbt.toBase64();
173
+ }
174
+ /**
175
+ * Finalize the inputs of a PSBT. If the transaction is fully signed, it will
176
+ * produce a network serialized transaction which can be broadcast with sendrawtransaction
177
+ * https://developer.bitcoin.org/reference/rpc/finalizepsbt.html
178
+ * @param psbt a base64 encoded psbt string
179
+ * @returns a json object containing `psbt` in base64, `hex` for transaction and `complete` for if
180
+ * the transaction has a complete set of signatures
181
+ */
182
+ finalizePSBT(psbtString) {
183
+ const psbt = bitcoin.Psbt.fromBase64(psbtString);
184
+ try {
185
+ psbt.validateSignaturesOfAllInputs(); // ensure all signatures are valid!
186
+ psbt.finalizeAllInputs();
187
+ }
188
+ catch (error) {
189
+ return {
190
+ psbt: psbt.toBase64(),
191
+ complete: false,
192
+ };
193
+ }
194
+ return {
195
+ psbt: psbt.toBase64(),
196
+ hex: psbt.extractTransaction().toHex(),
197
+ complete: true,
198
+ };
199
+ }
200
+ async sendSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs, fixedInputs) {
201
+ const { hex, fee } = await this._buildSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs, fixedInputs);
202
+ await this.getMethod('sendRawTransaction')(hex);
203
+ return bitcoin_utils_1.normalizeTransactionObject(bitcoin_utils_1.decodeRawTransaction(hex, this._network), fee);
204
+ }
205
+ async buildSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs, fixedInputs) {
206
+ return this._buildSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs, fixedInputs);
207
+ }
208
+ async _buildSweepTransactionWithSetOutputs(externalChangeAddress, feePerByte, _outputs = [], fixedInputs) {
209
+ const _feePerByte = feePerByte ||
210
+ (await this.getMethod('getFeePerByte')()) ||
211
+ FEE_PER_BYTE_FALLBACK;
212
+ const inputs = [];
213
+ const outputs = [];
214
+ try {
215
+ const inputsForAmount = await this.getInputsForAmount(_outputs, _feePerByte, fixedInputs, 100, true);
216
+ if (inputsForAmount.change) {
217
+ throw Error('There should not be any change for sweeping transaction');
218
+ }
219
+ inputs.push(...(inputsForAmount.inputs || []));
220
+ outputs.push(...(inputsForAmount.outputs || []));
221
+ }
222
+ catch (e) {
223
+ if (fixedInputs.length === 0) {
224
+ throw Error(`Inputs for amount doesn't exist and no fixedInputs provided`);
225
+ }
226
+ const inputsForAmount = await this._getInputForAmountWithoutUtxoCheck(_outputs, _feePerByte, fixedInputs);
227
+ inputs.push(...(inputsForAmount.inputs.map((utxo) => types_1.Input.fromUTXO(utxo)) || []));
228
+ outputs.push(...(inputsForAmount.outputs || []));
229
+ }
230
+ _outputs.forEach((output) => {
231
+ const spliceIndex = outputs.findIndex((sweepOutput) => output.value === sweepOutput.value);
232
+ outputs.splice(spliceIndex, 1);
233
+ });
234
+ _outputs.push({
235
+ to: externalChangeAddress,
236
+ value: outputs[0].value,
237
+ });
238
+ return this._buildTransactionWithoutUtxoCheck(_outputs, _feePerByte, inputs);
239
+ }
240
+ _getInputForAmountWithoutUtxoCheck(_outputs, _feePerByte, fixedInputs) {
241
+ const utxoBalance = fixedInputs.reduce((a, b) => a + (b['value'] || 0), 0);
242
+ const outputBalance = _outputs.reduce((a, b) => a + (b['value'] || 0), 0);
243
+ const amountToSend = utxoBalance -
244
+ _feePerByte * ((_outputs.length + 1) * 39 + fixedInputs.length * 153); // todo better calculation
245
+ const targets = _outputs.map((target, i) => ({
246
+ id: 'main',
247
+ value: target.value,
248
+ }));
249
+ if (amountToSend - outputBalance > 0) {
250
+ targets.push({ id: 'main', value: amountToSend - outputBalance });
251
+ }
252
+ return bitcoin_utils_1.selectCoins(fixedInputs, targets, Math.ceil(_feePerByte), fixedInputs);
253
+ }
254
+ async _buildTransactionWithoutUtxoCheck(outputs, feePerByte, fixedInputs) {
255
+ const network = this._network;
256
+ const { fee } = this._getInputForAmountWithoutUtxoCheck(outputs, feePerByte, fixedInputs);
257
+ const inputs = fixedInputs;
258
+ const txb = new bitcoin.TransactionBuilder(network);
259
+ for (const output of outputs) {
260
+ const to = output.to; // Allow for OP_RETURN
261
+ txb.addOutput(to, output.value);
262
+ }
263
+ const prevOutScriptType = 'p2wpkh';
264
+ for (let i = 0; i < inputs.length; i++) {
265
+ const wallet = await this.getWalletAddress(inputs[i].address);
266
+ const keyPair = await this.keyPair(wallet.derivationPath);
267
+ const paymentVariant = this.getPaymentVariantFromPublicKey(keyPair.publicKey);
268
+ txb.addInput(inputs[i].txid, inputs[i].vout, 0, paymentVariant.output);
269
+ }
270
+ for (let i = 0; i < inputs.length; i++) {
271
+ const wallet = await this.getWalletAddress(inputs[i].address);
272
+ const keyPair = await this.keyPair(wallet.derivationPath);
273
+ const paymentVariant = this.getPaymentVariantFromPublicKey(keyPair.publicKey);
274
+ const needsWitness = true;
275
+ const signParams = {
276
+ prevOutScriptType,
277
+ vin: i,
278
+ keyPair,
279
+ witnessValue: 0,
280
+ };
281
+ if (needsWitness) {
282
+ signParams.witnessValue = inputs[i].value;
283
+ }
284
+ txb.sign(signParams);
285
+ }
286
+ return { hex: txb.build().toHex(), fee };
287
+ }
288
+ async _buildTransaction(targets, feePerByte, fixedInputs) {
289
+ const network = this._network;
290
+ const unusedAddress = await this.getUnusedAddress(true);
291
+ const { inputs, change, fee } = await this.getInputsForAmount(targets, feePerByte, fixedInputs);
292
+ if (change) {
293
+ targets.push({
294
+ address: unusedAddress.address,
295
+ value: change.value,
296
+ });
297
+ }
298
+ const psbt = new bitcoinjs_lib_1.Psbt({ network });
299
+ const needsWitness = [
300
+ types_1.bitcoin.AddressType.BECH32,
301
+ types_1.bitcoin.AddressType.P2SH_SEGWIT,
302
+ ].includes(this._addressType);
303
+ for (let i = 0; i < inputs.length; i++) {
304
+ const wallet = await this.getWalletAddress(inputs[i].address);
305
+ const keyPair = await this.keyPair(wallet.derivationPath);
306
+ const paymentVariant = this.getPaymentVariantFromPublicKey(keyPair.publicKey);
307
+ const psbtInput = {
308
+ hash: inputs[i].txid,
309
+ index: inputs[i].vout,
310
+ sequence: 0,
311
+ };
312
+ if (needsWitness) {
313
+ psbtInput.witnessUtxo = {
314
+ script: paymentVariant.output,
315
+ value: inputs[i].value,
316
+ };
317
+ }
318
+ else {
319
+ const inputTxRaw = await this.getMethod('getRawTransactionByHash')(inputs[i].txid);
320
+ psbtInput.nonWitnessUtxo = Buffer.from(inputTxRaw, 'hex');
321
+ }
322
+ if (this._addressType === types_1.bitcoin.AddressType.P2SH_SEGWIT) {
323
+ psbtInput.redeemScript = paymentVariant.redeem.output;
324
+ }
325
+ psbt.addInput(psbtInput);
326
+ }
327
+ for (const output of targets) {
328
+ if (output.script) {
329
+ psbt.addOutput({
330
+ value: output.value,
331
+ script: output.script,
332
+ });
333
+ }
334
+ else {
335
+ psbt.addOutput({
336
+ value: output.value,
337
+ address: output.address,
338
+ });
339
+ }
340
+ }
341
+ for (let i = 0; i < inputs.length; i++) {
342
+ const wallet = await this.getWalletAddress(inputs[i].address);
343
+ const keyPair = await this.keyPair(wallet.derivationPath);
344
+ psbt.signInput(i, keyPair);
345
+ psbt.validateSignaturesOfInput(i);
346
+ }
347
+ psbt.finalizeAllInputs();
348
+ return { hex: psbt.extractTransaction().toHex(), fee };
349
+ }
350
+ async _buildSweepTransaction(externalChangeAddress, feePerByte) {
351
+ let _feePerByte = feePerByte || null;
352
+ if (!_feePerByte)
353
+ _feePerByte = await this.getMethod('getFeePerByte')();
354
+ const { inputs, outputs, change } = await this.getInputsForAmount([], _feePerByte, [], 100, true);
355
+ if (change) {
356
+ throw new Error('There should not be any change for sweeping transaction');
357
+ }
358
+ const _outputs = [
359
+ {
360
+ address: externalChangeAddress,
361
+ value: outputs[0].value,
362
+ },
363
+ ];
364
+ return this._buildTransaction(_outputs, feePerByte, inputs);
365
+ }
366
+ async signPSBT(data, inputs) {
367
+ const psbt = bitcoinjs_lib_1.Psbt.fromBase64(data, { network: this._network });
368
+ for (const input of inputs) {
369
+ const keyPair = await this.keyPair(input.derivationPath);
370
+ psbt.signInput(input.index, keyPair);
371
+ }
372
+ return psbt.toBase64();
373
+ }
374
+ async signBatchP2SHTransaction(inputs, addresses, tx, _lockTime, segwit) {
375
+ const keyPairs = [];
376
+ for (const address of addresses) {
377
+ const wallet = await this.getWalletAddress(address);
378
+ const keyPair = await this.keyPair(wallet.derivationPath);
379
+ keyPairs.push(keyPair);
380
+ }
381
+ const sigs = [];
382
+ for (let i = 0; i < inputs.length; i++) {
383
+ const index = inputs[i].txInputIndex
384
+ ? inputs[i].txInputIndex
385
+ : inputs[i].index;
386
+ let sigHash;
387
+ if (segwit) {
388
+ sigHash = tx.hashForWitnessV0(index, inputs[i].outputScript, inputs[i].vout.vSat, bitcoinjs_lib_1.Transaction.SIGHASH_ALL);
389
+ }
390
+ else {
391
+ sigHash = tx.hashForSignature(index, inputs[i].outputScript, bitcoinjs_lib_1.Transaction.SIGHASH_ALL);
392
+ }
393
+ const sig = bitcoinjs_lib_1.script.signature.encode(keyPairs[i].sign(sigHash), bitcoinjs_lib_1.Transaction.SIGHASH_ALL);
394
+ sigs.push(sig);
395
+ }
396
+ return sigs;
397
+ }
398
+ getScriptType() {
399
+ if (this._addressType === types_1.bitcoin.AddressType.LEGACY)
400
+ return 'p2pkh';
401
+ else if (this._addressType === types_1.bitcoin.AddressType.P2SH_SEGWIT)
402
+ return 'p2sh-p2wpkh';
403
+ else if (this._addressType === types_1.bitcoin.AddressType.BECH32)
404
+ return 'p2wpkh';
405
+ }
406
+ async getConnectedNetwork() {
407
+ return this._network;
408
+ }
409
+ async isWalletAvailable() {
410
+ return true;
411
+ }
412
+ }
413
+ exports.default = BitcoinJsWalletProvider;
414
+ //# sourceMappingURL=BitcoinJsWalletProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BitcoinJsWalletProvider.js","sourceRoot":"","sources":["../lib/BitcoinJsWalletProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import BitcoinJsWalletProvider from './BitcoinJsWalletProvider';
2
+ export { BitcoinJsWalletProvider };
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BitcoinJsWalletProvider = void 0;
7
+ const BitcoinJsWalletProvider_1 = __importDefault(require("./BitcoinJsWalletProvider"));
8
+ exports.BitcoinJsWalletProvider = BitcoinJsWalletProvider_1.default;
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;AAAA,wFAAgE;AAEvD,kCAFF,iCAAuB,CAEE"}
@@ -0,0 +1,651 @@
1
+ import {
2
+ decodeRawTransaction,
3
+ normalizeTransactionObject,
4
+ selectCoins,
5
+ } from '@atomicfinance/bitcoin-utils';
6
+ import BitcoinWalletProvider from '@atomicfinance/bitcoin-wallet-provider';
7
+ import Provider from '@atomicfinance/provider';
8
+ import {
9
+ Address,
10
+ bitcoin as bT,
11
+ CreateMultisigResponse,
12
+ finalizePSBTResponse,
13
+ Input,
14
+ Output,
15
+ Transaction,
16
+ } from '@atomicfinance/types';
17
+ import assert from 'assert';
18
+ import { BIP32Interface, fromSeed } from 'bip32';
19
+ import { mnemonicToSeed } from 'bip39';
20
+ import { BitcoinNetwork } from 'bitcoin-networks';
21
+ import * as bitcoin from 'bitcoinjs-lib';
22
+ import {
23
+ ECPair,
24
+ ECPairInterface,
25
+ Psbt,
26
+ script,
27
+ Transaction as BitcoinJsTransaction,
28
+ } from 'bitcoinjs-lib';
29
+ import { signAsync as signBitcoinMessage } from 'bitcoinjs-message';
30
+ import secp256k1 from 'secp256k1';
31
+
32
+ const FEE_PER_BYTE_FALLBACK = 5;
33
+
34
+ type WalletProviderConstructor<T = Provider> = new (...args: any[]) => T;
35
+
36
+ interface BitcoinJsWalletProviderOptions {
37
+ network: BitcoinNetwork;
38
+ mnemonic: string;
39
+ baseDerivationPath: string;
40
+ addressType?: bT.AddressType;
41
+ }
42
+
43
+ export default class BitcoinJsWalletProvider extends BitcoinWalletProvider(
44
+ Provider as WalletProviderConstructor,
45
+ ) {
46
+ _mnemonic: string;
47
+ _seedNode: BIP32Interface;
48
+ _baseDerivationNode: BIP32Interface;
49
+
50
+ constructor(options: BitcoinJsWalletProviderOptions) {
51
+ const {
52
+ network,
53
+ mnemonic,
54
+ baseDerivationPath,
55
+ addressType = bT.AddressType.BECH32,
56
+ } = options;
57
+ super({ network, baseDerivationPath, addressType });
58
+
59
+ if (!mnemonic) throw new Error('Mnemonic should not be empty');
60
+
61
+ this._mnemonic = mnemonic;
62
+ }
63
+
64
+ async seedNode() {
65
+ if (this._seedNode) return this._seedNode;
66
+
67
+ const seed = await mnemonicToSeed(this._mnemonic);
68
+ this._seedNode = fromSeed(seed, this._network);
69
+
70
+ return this._seedNode;
71
+ }
72
+
73
+ async baseDerivationNode() {
74
+ if (this._baseDerivationNode) return this._baseDerivationNode;
75
+
76
+ const baseNode = await this.seedNode();
77
+ this._baseDerivationNode = baseNode.derivePath(this._baseDerivationPath);
78
+
79
+ return this._baseDerivationNode;
80
+ }
81
+
82
+ async keyPair(derivationPath: string): Promise<ECPairInterface> {
83
+ const wif = await this._toWIF(derivationPath);
84
+ return ECPair.fromWIF(wif, this._network);
85
+ }
86
+
87
+ private async _toWIF(derivationPath: string): Promise<string> {
88
+ const node = await this.seedNode();
89
+ return node.derivePath(derivationPath).toWIF();
90
+ }
91
+
92
+ async exportPrivateKey() {
93
+ return this._toWIF(this._baseDerivationPath);
94
+ }
95
+
96
+ async signMessage(message: string, from: string) {
97
+ const address = await this.getWalletAddress(from);
98
+ const keyPair = await this.keyPair(address.derivationPath);
99
+ const signature = await signBitcoinMessage(
100
+ message,
101
+ keyPair.privateKey,
102
+ keyPair.compressed,
103
+ );
104
+ return signature.toString('hex');
105
+ }
106
+
107
+ _createMultisigPayment(m: number, pubkeys: string[]): bitcoin.Payment {
108
+ if (m > pubkeys.length) {
109
+ throw new Error(
110
+ `not enough keys supplied (got ${pubkeys.length} keys, but need at least ${m} to redeem)`,
111
+ );
112
+ }
113
+ // Create m-of-n multisig
114
+ const p2ms = bitcoin.payments.p2ms({
115
+ m: m,
116
+ pubkeys: pubkeys.map((key: string) => Buffer.from(key, 'hex')),
117
+ network: this._network,
118
+ });
119
+
120
+ // Create p2wsh for multisig
121
+ const p2wsh = bitcoin.payments.p2wsh({
122
+ redeem: p2ms,
123
+ network: this._network,
124
+ });
125
+
126
+ return p2wsh;
127
+ }
128
+
129
+ /**
130
+ * Creates a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
131
+ * https://developer.bitcoin.org/reference/rpc/createmultisig.html
132
+ * @param m the number of required signatures
133
+ * @param pubkeys n possible pubkeys in total
134
+ * @returns a json object containing the `address` and `redeemScript`
135
+ */
136
+ createMultisig(m: number, pubkeys: string[]): CreateMultisigResponse {
137
+ const p2wsh = this._createMultisigPayment(m, pubkeys);
138
+ return {
139
+ address: p2wsh.address,
140
+ redeemScript: p2wsh.redeem?.output?.toString('hex'),
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Creates a PSBT of a native-segwit multi-signature address (P2MS in P2WSH) with n signatures of m required keys
146
+ * https://developer.bitcoin.org/reference/rpc/createmultisig.html
147
+ * https://developer.bitcoin.org/reference/rpc/createpsbt.html
148
+ * @param m the number of required signatures
149
+ * @param pubkeys n possible pubkeys in total
150
+ * @param inputs the Inputs to the PSBT
151
+ * @param ouputs the Outputs to the PSBT
152
+ * @returns a base64 encoded psbt string
153
+ */
154
+ buildMultisigPSBT(
155
+ m: number,
156
+ pubkeys: string[],
157
+ inputs: Input[],
158
+ outputs: Output[],
159
+ ): string {
160
+ assert(inputs.length > 0, 'no inputs found');
161
+ assert(outputs.length > 0, 'no outputs found');
162
+
163
+ const p2wsh = this._createMultisigPayment(m, pubkeys);
164
+
165
+ // Verify pubkeyhash for all inputs matches the p2wsh hash
166
+ assert(
167
+ inputs.every(
168
+ (input: Input) => p2wsh.output.toString('hex') === input.scriptPubKey,
169
+ ),
170
+ 'address pubkeyhash does not match input scriptPubKey',
171
+ );
172
+
173
+ // creator
174
+ const psbt = new bitcoin.Psbt({ network: this._network });
175
+
176
+ // updater
177
+ inputs.forEach((input: Input) => {
178
+ psbt.addInput({
179
+ hash: input.txid,
180
+ index: input.vout,
181
+ witnessUtxo: { script: p2wsh.output, value: input.value },
182
+ witnessScript: p2wsh.redeem.output,
183
+ });
184
+ });
185
+
186
+ outputs.forEach((output: Output) => {
187
+ psbt.addOutput({
188
+ address: output.to,
189
+ value: output.value,
190
+ });
191
+ });
192
+
193
+ return psbt.toBase64();
194
+ }
195
+
196
+ /**
197
+ * Update a PSBT with input information from our wallet and then sign inputs that we can sign for
198
+ * https://developer.bitcoin.org/reference/rpc/walletprocesspsbt.html
199
+ * @param psbt a base64 encoded psbt string (P2WSH only)
200
+ * @returns a base64 encoded signed psbt string
201
+ */
202
+ async walletProcessPSBT(psbtString: string): Promise<string> {
203
+ const psbt = bitcoin.Psbt.fromBase64(psbtString);
204
+
205
+ await Promise.all(
206
+ psbt.data.inputs.map(async (input, i: number) => {
207
+ assert(
208
+ psbt.getInputType(i).slice(0, 5) === 'p2wsh',
209
+ 'only accepts P2WSH inputs',
210
+ );
211
+
212
+ const scriptStack = bitcoin.script.decompile(input.witnessScript);
213
+ const pubkeys = scriptStack.filter(
214
+ (data) => Buffer.isBuffer(data) && secp256k1.publicKeyVerify(data),
215
+ );
216
+
217
+ await Promise.all(
218
+ pubkeys.map(async (key) => {
219
+ // create address using pubkey
220
+ const { address: addressString } = bitcoin.payments.p2wpkh({
221
+ pubkey: key as Buffer,
222
+ network: this._network,
223
+ });
224
+
225
+ // Retrieve address object from wallet using address
226
+ const address: Address = await this.findAddress([addressString]);
227
+
228
+ // exit if address doesn't exist in wallet
229
+ if (!address) return;
230
+
231
+ // derive keypair
232
+ const keyPair = await this.keyPair(address.derivationPath);
233
+
234
+ // sign PSBT using keypair
235
+ psbt.signInput(i, keyPair);
236
+ }),
237
+ );
238
+ }),
239
+ );
240
+
241
+ psbt.validateSignaturesOfAllInputs(); // ensure all signatures are valid!
242
+ return psbt.toBase64();
243
+ }
244
+
245
+ /**
246
+ * Finalize the inputs of a PSBT. If the transaction is fully signed, it will
247
+ * produce a network serialized transaction which can be broadcast with sendrawtransaction
248
+ * https://developer.bitcoin.org/reference/rpc/finalizepsbt.html
249
+ * @param psbt a base64 encoded psbt string
250
+ * @returns a json object containing `psbt` in base64, `hex` for transaction and `complete` for if
251
+ * the transaction has a complete set of signatures
252
+ */
253
+ finalizePSBT(psbtString: string): finalizePSBTResponse {
254
+ const psbt = bitcoin.Psbt.fromBase64(psbtString);
255
+
256
+ try {
257
+ psbt.validateSignaturesOfAllInputs(); // ensure all signatures are valid!
258
+ psbt.finalizeAllInputs();
259
+ } catch (error) {
260
+ return {
261
+ psbt: psbt.toBase64(),
262
+ complete: false,
263
+ };
264
+ }
265
+ return {
266
+ psbt: psbt.toBase64(),
267
+ hex: psbt.extractTransaction().toHex(),
268
+ complete: true,
269
+ };
270
+ }
271
+
272
+ async sendSweepTransactionWithSetOutputs(
273
+ externalChangeAddress: string,
274
+ feePerByte: number,
275
+ _outputs: Output[],
276
+ fixedInputs: Input[],
277
+ ): Promise<Transaction<bT.Transaction>> {
278
+ const { hex, fee } = await this._buildSweepTransactionWithSetOutputs(
279
+ externalChangeAddress,
280
+ feePerByte,
281
+ _outputs,
282
+ fixedInputs,
283
+ );
284
+ await this.getMethod('sendRawTransaction')(hex);
285
+ return normalizeTransactionObject(
286
+ decodeRawTransaction(hex, this._network),
287
+ fee,
288
+ );
289
+ }
290
+
291
+ async buildSweepTransactionWithSetOutputs(
292
+ externalChangeAddress: string,
293
+ feePerByte: number,
294
+ _outputs: Output[],
295
+ fixedInputs: Input[],
296
+ ) {
297
+ return this._buildSweepTransactionWithSetOutputs(
298
+ externalChangeAddress,
299
+ feePerByte,
300
+ _outputs,
301
+ fixedInputs,
302
+ );
303
+ }
304
+
305
+ async _buildSweepTransactionWithSetOutputs(
306
+ externalChangeAddress: string,
307
+ feePerByte: number,
308
+ _outputs: Output[] = [],
309
+ fixedInputs: Input[],
310
+ ) {
311
+ const _feePerByte =
312
+ feePerByte ||
313
+ (await this.getMethod('getFeePerByte')()) ||
314
+ FEE_PER_BYTE_FALLBACK;
315
+ const inputs: Input[] = [];
316
+ const outputs: Output[] = [];
317
+ try {
318
+ const inputsForAmount = await this.getInputsForAmount(
319
+ _outputs,
320
+ _feePerByte,
321
+ (fixedInputs as unknown) as bT.Input[],
322
+ 100,
323
+ true,
324
+ );
325
+ if (inputsForAmount.change) {
326
+ throw Error('There should not be any change for sweeping transaction');
327
+ }
328
+ inputs.push(...((inputsForAmount.inputs as Input[]) || []));
329
+ outputs.push(...(inputsForAmount.outputs || []));
330
+ } catch (e) {
331
+ if (fixedInputs.length === 0) {
332
+ throw Error(
333
+ `Inputs for amount doesn't exist and no fixedInputs provided`,
334
+ );
335
+ }
336
+
337
+ const inputsForAmount = await this._getInputForAmountWithoutUtxoCheck(
338
+ _outputs,
339
+ _feePerByte,
340
+ fixedInputs,
341
+ );
342
+ inputs.push(
343
+ ...(inputsForAmount.inputs.map((utxo) => Input.fromUTXO(utxo)) || []),
344
+ );
345
+ outputs.push(...(inputsForAmount.outputs || []));
346
+ }
347
+ _outputs.forEach((output) => {
348
+ const spliceIndex = outputs.findIndex(
349
+ (sweepOutput: Output) => output.value === sweepOutput.value,
350
+ );
351
+ outputs.splice(spliceIndex, 1);
352
+ });
353
+ _outputs.push({
354
+ to: externalChangeAddress,
355
+ value: outputs[0].value,
356
+ });
357
+ return this._buildTransactionWithoutUtxoCheck(
358
+ _outputs,
359
+ _feePerByte,
360
+ inputs,
361
+ );
362
+ }
363
+
364
+ _getInputForAmountWithoutUtxoCheck(
365
+ _outputs: Output[],
366
+ _feePerByte: number,
367
+ fixedInputs: Input[],
368
+ ): {
369
+ inputs: bT.UTXO[];
370
+ outputs: { value: number; id?: string }[];
371
+ fee: number;
372
+ change: { value: number; id?: string };
373
+ } {
374
+ const utxoBalance = fixedInputs.reduce((a, b) => a + (b['value'] || 0), 0);
375
+ const outputBalance = _outputs.reduce((a, b) => a + (b['value'] || 0), 0);
376
+ const amountToSend =
377
+ utxoBalance -
378
+ _feePerByte * ((_outputs.length + 1) * 39 + fixedInputs.length * 153); // todo better calculation
379
+
380
+ const targets = _outputs.map((target, i) => ({
381
+ id: 'main',
382
+ value: target.value,
383
+ }));
384
+ if (amountToSend - outputBalance > 0) {
385
+ targets.push({ id: 'main', value: amountToSend - outputBalance });
386
+ }
387
+
388
+ return selectCoins(
389
+ fixedInputs,
390
+ targets,
391
+ Math.ceil(_feePerByte),
392
+ fixedInputs,
393
+ );
394
+ }
395
+
396
+ async _buildTransactionWithoutUtxoCheck(
397
+ outputs: Output[],
398
+ feePerByte: number,
399
+ fixedInputs: Input[],
400
+ ) {
401
+ const network = this._network;
402
+
403
+ const { fee } = this._getInputForAmountWithoutUtxoCheck(
404
+ outputs,
405
+ feePerByte,
406
+ fixedInputs,
407
+ );
408
+ const inputs = fixedInputs;
409
+
410
+ const txb = new bitcoin.TransactionBuilder(network);
411
+
412
+ for (const output of outputs) {
413
+ const to = output.to; // Allow for OP_RETURN
414
+ txb.addOutput(to, output.value);
415
+ }
416
+
417
+ const prevOutScriptType = 'p2wpkh';
418
+
419
+ for (let i = 0; i < inputs.length; i++) {
420
+ const wallet = await this.getWalletAddress(inputs[i].address);
421
+ const keyPair = await this.keyPair(wallet.derivationPath);
422
+ const paymentVariant = this.getPaymentVariantFromPublicKey(
423
+ keyPair.publicKey,
424
+ );
425
+
426
+ txb.addInput(inputs[i].txid, inputs[i].vout, 0, paymentVariant.output);
427
+ }
428
+
429
+ for (let i = 0; i < inputs.length; i++) {
430
+ const wallet = await this.getWalletAddress(inputs[i].address);
431
+ const keyPair = await this.keyPair(wallet.derivationPath);
432
+ const paymentVariant = this.getPaymentVariantFromPublicKey(
433
+ keyPair.publicKey,
434
+ );
435
+ const needsWitness = true;
436
+
437
+ const signParams = {
438
+ prevOutScriptType,
439
+ vin: i,
440
+ keyPair,
441
+ witnessValue: 0,
442
+ };
443
+
444
+ if (needsWitness) {
445
+ signParams.witnessValue = inputs[i].value;
446
+ }
447
+
448
+ txb.sign(signParams);
449
+ }
450
+
451
+ return { hex: txb.build().toHex(), fee };
452
+ }
453
+
454
+ async _buildTransaction(
455
+ targets: bT.OutputTarget[],
456
+ feePerByte?: number,
457
+ fixedInputs?: bT.Input[],
458
+ ) {
459
+ const network = this._network;
460
+
461
+ const unusedAddress = await this.getUnusedAddress(true);
462
+ const { inputs, change, fee } = await this.getInputsForAmount(
463
+ targets,
464
+ feePerByte,
465
+ fixedInputs,
466
+ );
467
+
468
+ if (change) {
469
+ targets.push({
470
+ address: unusedAddress.address,
471
+ value: change.value,
472
+ });
473
+ }
474
+
475
+ const psbt = new Psbt({ network });
476
+
477
+ const needsWitness = [
478
+ bT.AddressType.BECH32,
479
+ bT.AddressType.P2SH_SEGWIT,
480
+ ].includes(this._addressType);
481
+
482
+ for (let i = 0; i < inputs.length; i++) {
483
+ const wallet = await this.getWalletAddress(inputs[i].address);
484
+ const keyPair = await this.keyPair(wallet.derivationPath);
485
+ const paymentVariant = this.getPaymentVariantFromPublicKey(
486
+ keyPair.publicKey,
487
+ );
488
+
489
+ const psbtInput: any = {
490
+ hash: inputs[i].txid,
491
+ index: inputs[i].vout,
492
+ sequence: 0,
493
+ };
494
+
495
+ if (needsWitness) {
496
+ psbtInput.witnessUtxo = {
497
+ script: paymentVariant.output,
498
+ value: inputs[i].value,
499
+ };
500
+ } else {
501
+ const inputTxRaw = await this.getMethod('getRawTransactionByHash')(
502
+ inputs[i].txid,
503
+ );
504
+ psbtInput.nonWitnessUtxo = Buffer.from(inputTxRaw, 'hex');
505
+ }
506
+
507
+ if (this._addressType === bT.AddressType.P2SH_SEGWIT) {
508
+ psbtInput.redeemScript = paymentVariant.redeem.output;
509
+ }
510
+
511
+ psbt.addInput(psbtInput);
512
+ }
513
+
514
+ for (const output of targets) {
515
+ if (output.script) {
516
+ psbt.addOutput({
517
+ value: output.value,
518
+ script: output.script,
519
+ });
520
+ } else {
521
+ psbt.addOutput({
522
+ value: output.value,
523
+ address: output.address,
524
+ });
525
+ }
526
+ }
527
+
528
+ for (let i = 0; i < inputs.length; i++) {
529
+ const wallet = await this.getWalletAddress(inputs[i].address);
530
+ const keyPair = await this.keyPair(wallet.derivationPath);
531
+ psbt.signInput(i, keyPair);
532
+ psbt.validateSignaturesOfInput(i);
533
+ }
534
+
535
+ psbt.finalizeAllInputs();
536
+
537
+ return { hex: psbt.extractTransaction().toHex(), fee };
538
+ }
539
+
540
+ async _buildSweepTransaction(
541
+ externalChangeAddress: string,
542
+ feePerByte: number,
543
+ ) {
544
+ let _feePerByte = feePerByte || null;
545
+ if (!_feePerByte) _feePerByte = await this.getMethod('getFeePerByte')();
546
+
547
+ const { inputs, outputs, change } = await this.getInputsForAmount(
548
+ [],
549
+ _feePerByte,
550
+ [],
551
+ 100,
552
+ true,
553
+ );
554
+
555
+ if (change) {
556
+ throw new Error(
557
+ 'There should not be any change for sweeping transaction',
558
+ );
559
+ }
560
+
561
+ const _outputs = [
562
+ {
563
+ address: externalChangeAddress,
564
+ value: outputs[0].value,
565
+ },
566
+ ];
567
+
568
+ return this._buildTransaction(
569
+ _outputs,
570
+ feePerByte,
571
+ (inputs as unknown) as bT.Input[],
572
+ );
573
+ }
574
+
575
+ async signPSBT(data: string, inputs: bT.PsbtInputTarget[]) {
576
+ const psbt = Psbt.fromBase64(data, { network: this._network });
577
+ for (const input of inputs) {
578
+ const keyPair = await this.keyPair(input.derivationPath);
579
+ psbt.signInput(input.index, keyPair);
580
+ }
581
+ return psbt.toBase64();
582
+ }
583
+
584
+ async signBatchP2SHTransaction(
585
+ inputs: [
586
+ {
587
+ inputTxHex: string;
588
+ index: number;
589
+ vout: any;
590
+ outputScript: Buffer;
591
+ txInputIndex?: number;
592
+ },
593
+ ],
594
+ addresses: string,
595
+ tx: any,
596
+ _lockTime?: number,
597
+ segwit?: boolean,
598
+ ) {
599
+ const keyPairs = [];
600
+ for (const address of addresses) {
601
+ const wallet = await this.getWalletAddress(address);
602
+ const keyPair = await this.keyPair(wallet.derivationPath);
603
+ keyPairs.push(keyPair);
604
+ }
605
+
606
+ const sigs = [];
607
+ for (let i = 0; i < inputs.length; i++) {
608
+ const index = inputs[i].txInputIndex
609
+ ? inputs[i].txInputIndex
610
+ : inputs[i].index;
611
+ let sigHash;
612
+ if (segwit) {
613
+ sigHash = tx.hashForWitnessV0(
614
+ index,
615
+ inputs[i].outputScript,
616
+ inputs[i].vout.vSat,
617
+ BitcoinJsTransaction.SIGHASH_ALL,
618
+ );
619
+ } else {
620
+ sigHash = tx.hashForSignature(
621
+ index,
622
+ inputs[i].outputScript,
623
+ BitcoinJsTransaction.SIGHASH_ALL,
624
+ );
625
+ }
626
+
627
+ const sig = script.signature.encode(
628
+ keyPairs[i].sign(sigHash),
629
+ BitcoinJsTransaction.SIGHASH_ALL,
630
+ );
631
+ sigs.push(sig);
632
+ }
633
+
634
+ return sigs;
635
+ }
636
+
637
+ getScriptType() {
638
+ if (this._addressType === bT.AddressType.LEGACY) return 'p2pkh';
639
+ else if (this._addressType === bT.AddressType.P2SH_SEGWIT)
640
+ return 'p2sh-p2wpkh';
641
+ else if (this._addressType === bT.AddressType.BECH32) return 'p2wpkh';
642
+ }
643
+
644
+ async getConnectedNetwork() {
645
+ return this._network;
646
+ }
647
+
648
+ async isWalletAvailable() {
649
+ return true;
650
+ }
651
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import BitcoinJsWalletProvider from './BitcoinJsWalletProvider';
2
+
3
+ export { BitcoinJsWalletProvider };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@atomicfinance/bitcoin-js-wallet-provider",
3
+ "version": "3.0.0",
4
+ "description": "",
5
+ "module": "dist/index.js",
6
+ "main": "dist/index.js",
7
+ "directories": {
8
+ "dist": "dist",
9
+ "src": "lib"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "lib"
14
+ ],
15
+ "scripts": {
16
+ "build": "../../node_modules/.bin/tsc --project tsconfig.json",
17
+ "lint": "../../node_modules/.bin/eslint --ignore-path ../../.eslintignore -c ../../.eslintrc.js .",
18
+ "lint:fix": "../../node_modules/.bin/eslint --fix --ignore-path ../../.eslintignore -c ../../.eslintrc.js .",
19
+ "test": "../../node_modules/.bin/nyc --reporter=lcov --reporter=text --extension=.ts ../../node_modules/.bin/mocha --recursive \"tests/**/*.test.*\""
20
+ },
21
+ "author": "Atomic Finance <info@atomic.finance>",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": ">=14"
25
+ },
26
+ "dependencies": {
27
+ "@atomicfinance/bitcoin-utils": "^3.0.0",
28
+ "@atomicfinance/bitcoin-wallet-provider": "^3.0.0",
29
+ "@atomicfinance/types": "^3.0.0",
30
+ "@atomicfinance/utils": "^3.0.0",
31
+ "@babel/runtime": "^7.12.1",
32
+ "bip32": "^2.0.6",
33
+ "bip39": "^3.0.2",
34
+ "bitcoin-networks": "^1.0.0",
35
+ "bitcoinjs-lib": "^5.2.0",
36
+ "bitcoinjs-message": "^2.1.0",
37
+ "secp256k1": "^4.0.3"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "sideEffects": false
43
+ }