@atomicfinance/bitcoin-node-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,17 @@
1
+ # @atomicfinance/bitcoin-node-wallet-provider
2
+
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 2edf70f: Port over bitcoin RPC related providers
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [2edf70f]
12
+ - Updated dependencies [a06082e]
13
+ - @atomicfinance/jsonrpc-provider@3.0.0
14
+ - @atomicfinance/types@3.0.0
15
+ - @atomicfinance/bitcoin-utils@3.0.0
16
+ - @atomicfinance/crypto@3.0.0
17
+ - @atomicfinance/utils@3.0.0
@@ -0,0 +1,46 @@
1
+ /// <reference types="node" />
2
+ import { JsonRpcProvider } from '@atomicfinance/jsonrpc-provider';
3
+ import Provider from '@atomicfinance/provider';
4
+ import { Address, bitcoin, SendOptions, Transaction } from '@atomicfinance/types';
5
+ import { BitcoinNetwork } from 'bitcoin-networks';
6
+ interface ProviderOptions {
7
+ uri: string;
8
+ username?: string;
9
+ password?: string;
10
+ network: BitcoinNetwork;
11
+ addressType?: bitcoin.AddressType;
12
+ }
13
+ export default class BitcoinNodeWalletProvider extends Provider {
14
+ _addressType: bitcoin.AddressType;
15
+ _network: BitcoinNetwork;
16
+ _rpc: JsonRpcProvider;
17
+ _addressInfoCache: {
18
+ [key: string]: Address;
19
+ };
20
+ constructor(opts: ProviderOptions);
21
+ signMessage(message: string, from: string): Promise<string>;
22
+ withTxFee(func: () => Promise<Transaction<bitcoin.Transaction>>, feePerByte: number): Promise<Transaction<bitcoin.Transaction>>;
23
+ _sendTransaction(options: SendOptions): Promise<Transaction<bitcoin.Transaction>>;
24
+ sendTransaction(options: SendOptions): Promise<Transaction<bitcoin.Transaction>>;
25
+ updateTransactionFee(tx: Transaction<bitcoin.Transaction>, newFeePerByte: number): Promise<Transaction<bitcoin.Transaction>>;
26
+ signPSBT(data: string, inputs: bitcoin.PsbtInputTarget[]): Promise<string>;
27
+ signBatchP2SHTransaction(inputs: [
28
+ {
29
+ inputTxHex: string;
30
+ index: number;
31
+ vout: any;
32
+ outputScript: Buffer;
33
+ }
34
+ ], addresses: string, tx: any, locktime: number, segwit?: boolean): Promise<any[]>;
35
+ dumpPrivKey(address: string): Promise<string>;
36
+ getNewAddress(addressType: bitcoin.AddressType, label?: string): Promise<Address>;
37
+ getAddressInfo(address: string): Promise<Address>;
38
+ getAddresses(): Promise<Address[]>;
39
+ getUnusedAddress(): Promise<Address>;
40
+ getUsedAddresses(): Promise<Address[]>;
41
+ getWalletAddress(address: string): Promise<Address>;
42
+ isWalletAvailable(): Promise<boolean>;
43
+ getConnectedNetwork(): Promise<BitcoinNetwork>;
44
+ generateSecret(message: string): Promise<any>;
45
+ }
46
+ export {};
@@ -0,0 +1,173 @@
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
+ const bitcoin_utils_1 = require("@atomicfinance/bitcoin-utils");
7
+ const crypto_1 = require("@atomicfinance/crypto");
8
+ const jsonrpc_provider_1 = require("@atomicfinance/jsonrpc-provider");
9
+ const provider_1 = __importDefault(require("@atomicfinance/provider"));
10
+ const types_1 = require("@atomicfinance/types");
11
+ const bitcoin_networks_1 = require("bitcoin-networks");
12
+ const bitcoinjs_lib_1 = require("bitcoinjs-lib");
13
+ const lodash_1 = require("lodash");
14
+ const BIP70_CHAIN_TO_NETWORK = {
15
+ main: bitcoin_networks_1.BitcoinNetworks.bitcoin,
16
+ test: bitcoin_networks_1.BitcoinNetworks.bitcoin_testnet,
17
+ regtest: bitcoin_networks_1.BitcoinNetworks.bitcoin_regtest,
18
+ };
19
+ class BitcoinNodeWalletProvider extends provider_1.default {
20
+ constructor(opts) {
21
+ super();
22
+ const { uri, username, password, network, addressType = types_1.bitcoin.AddressType.BECH32, } = opts;
23
+ const addressTypes = Object.values(types_1.bitcoin.AddressType);
24
+ if (!addressTypes.includes(addressType)) {
25
+ throw new Error(`addressType must be one of ${addressTypes.join(',')}`);
26
+ }
27
+ this._addressType = addressType;
28
+ this._network = network;
29
+ this._rpc = new jsonrpc_provider_1.JsonRpcProvider(uri, username, password);
30
+ this._addressInfoCache = {};
31
+ }
32
+ async signMessage(message, from) {
33
+ return this._rpc
34
+ .jsonrpc('signmessage', from, message)
35
+ .then((result) => Buffer.from(result, 'base64').toString('hex'));
36
+ }
37
+ async withTxFee(func, feePerByte) {
38
+ const feePerKB = new types_1.BigNumber(feePerByte).div(1e8).times(1000).toNumber();
39
+ const originalTxFee = (await this._rpc.jsonrpc('getwalletinfo'))
40
+ .paytxfee;
41
+ await this._rpc.jsonrpc('settxfee', feePerKB);
42
+ const result = await func();
43
+ await this._rpc.jsonrpc('settxfee', originalTxFee);
44
+ return result;
45
+ }
46
+ async _sendTransaction(options) {
47
+ const value = new types_1.BigNumber(options.value).dividedBy(1e8).toNumber();
48
+ const hash = await this._rpc.jsonrpc('sendtoaddress', options.to, value, '', '', false, true);
49
+ const transaction = await this._rpc.jsonrpc('gettransaction', hash, true);
50
+ const fee = new types_1.BigNumber(transaction.fee).abs().times(1e8).toNumber();
51
+ return bitcoin_utils_1.normalizeTransactionObject(bitcoin_utils_1.decodeRawTransaction(transaction.hex, this._network), fee);
52
+ }
53
+ async sendTransaction(options) {
54
+ return options.fee
55
+ ? this.withTxFee(async () => this._sendTransaction(options), options.fee)
56
+ : this._sendTransaction(options);
57
+ }
58
+ async updateTransactionFee(tx, newFeePerByte) {
59
+ const txHash = lodash_1.isString(tx) ? tx : tx.hash;
60
+ return this.withTxFee(async () => {
61
+ const result = await this._rpc.jsonrpc('bumpfee', txHash);
62
+ const transaction = await this._rpc.jsonrpc('gettransaction', result.txid, true);
63
+ const fee = new types_1.BigNumber(transaction.fee).abs().times(1e8).toNumber();
64
+ return bitcoin_utils_1.normalizeTransactionObject(bitcoin_utils_1.decodeRawTransaction(transaction.hex, this._network), fee);
65
+ }, newFeePerByte);
66
+ }
67
+ async signPSBT(data, inputs) {
68
+ const psbt = bitcoinjs_lib_1.Psbt.fromBase64(data, { network: this._network });
69
+ for (const input of inputs) {
70
+ const usedAddresses = await this.getUsedAddresses();
71
+ const address = usedAddresses.find((address) => address.derivationPath === input.derivationPath);
72
+ const wif = await this.dumpPrivKey(address.address);
73
+ const keyPair = bitcoinjs_lib_1.ECPair.fromWIF(wif, this._network);
74
+ psbt.signInput(input.index, keyPair);
75
+ }
76
+ return psbt.toBase64();
77
+ }
78
+ async signBatchP2SHTransaction(inputs, addresses, tx, locktime, segwit = false) {
79
+ const wallets = [];
80
+ for (const address of addresses) {
81
+ const wif = await this.dumpPrivKey(address);
82
+ const wallet = bitcoinjs_lib_1.ECPair.fromWIF(wif, this._network);
83
+ wallets.push(wallet);
84
+ }
85
+ const sigs = [];
86
+ for (let i = 0; i < inputs.length; i++) {
87
+ let sigHash;
88
+ if (segwit) {
89
+ sigHash = tx.hashForWitnessV0(inputs[i].index, inputs[i].outputScript, inputs[i].vout.vSat, bitcoinjs_lib_1.Transaction.SIGHASH_ALL); // AMOUNT NEEDS TO BE PREVOUT AMOUNT
90
+ }
91
+ else {
92
+ sigHash = tx.hashForSignature(inputs[i].index, inputs[i].outputScript, bitcoinjs_lib_1.Transaction.SIGHASH_ALL);
93
+ }
94
+ const sig = bitcoinjs_lib_1.script.signature.encode(wallets[i].sign(sigHash), bitcoinjs_lib_1.Transaction.SIGHASH_ALL);
95
+ sigs.push(sig);
96
+ }
97
+ return sigs;
98
+ }
99
+ async dumpPrivKey(address) {
100
+ return this._rpc.jsonrpc('dumpprivkey', address);
101
+ }
102
+ async getNewAddress(addressType, label = '') {
103
+ const params = addressType ? [label, addressType] : [label];
104
+ const newAddress = await this._rpc.jsonrpc('getnewaddress', ...params);
105
+ if (!newAddress)
106
+ return null;
107
+ return this.getAddressInfo(newAddress);
108
+ }
109
+ async getAddressInfo(address) {
110
+ if (address in this._addressInfoCache) {
111
+ return this._addressInfoCache[address];
112
+ }
113
+ const addressInfo = await this._rpc.jsonrpc('getaddressinfo', address);
114
+ let publicKey, derivationPath;
115
+ if (!addressInfo.iswatchonly) {
116
+ publicKey = addressInfo.pubkey;
117
+ derivationPath = addressInfo.hdkeypath;
118
+ }
119
+ const addressObject = new types_1.Address({ address, publicKey, derivationPath });
120
+ this._addressInfoCache[address] = addressObject;
121
+ return addressObject;
122
+ }
123
+ async getAddresses() {
124
+ return this.getUsedAddresses();
125
+ }
126
+ async getUnusedAddress() {
127
+ return this.getNewAddress(this._addressType);
128
+ }
129
+ async getUsedAddresses() {
130
+ const usedAddresses = await this._rpc.jsonrpc('listaddressgroupings');
131
+ const emptyAddresses = await this._rpc.jsonrpc('listreceivedbyaddress', 0, true, false);
132
+ const addrs = lodash_1.uniq([
133
+ ...lodash_1.flatten(usedAddresses).map((addr) => addr[0]),
134
+ ...emptyAddresses.map((a) => a.address),
135
+ ]);
136
+ const addressObjects = await Promise.all(addrs.map((address) => this.getAddressInfo(address)));
137
+ return addressObjects;
138
+ }
139
+ async getWalletAddress(address) {
140
+ return this.getAddressInfo(address);
141
+ }
142
+ async isWalletAvailable() {
143
+ try {
144
+ await this._rpc.jsonrpc('getwalletinfo');
145
+ return true;
146
+ }
147
+ catch (e) {
148
+ return false;
149
+ }
150
+ }
151
+ async getConnectedNetwork() {
152
+ const blockchainInfo = await this._rpc.jsonrpc('getblockchaininfo');
153
+ const chain = blockchainInfo.chain;
154
+ return BIP70_CHAIN_TO_NETWORK[chain];
155
+ }
156
+ async generateSecret(message) {
157
+ const secretAddressLabel = 'secretAddress';
158
+ let address;
159
+ try {
160
+ const labelAddresses = await this._rpc.jsonrpc('getaddressesbylabel', secretAddressLabel);
161
+ address = Object.keys(labelAddresses)[0];
162
+ }
163
+ catch (e) {
164
+ // Label does not exist
165
+ address = (await this.getNewAddress(types_1.bitcoin.AddressType.LEGACY, secretAddressLabel)).address; // Signing only possible with legacy addresses
166
+ }
167
+ const signedMessage = await this.signMessage(message, address);
168
+ const secret = crypto_1.sha256(signedMessage);
169
+ return secret;
170
+ }
171
+ }
172
+ exports.default = BitcoinNodeWalletProvider;
173
+ //# sourceMappingURL=BitcoinNodeWalletProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BitcoinNodeWalletProvider.js","sourceRoot":"","sources":["../lib/BitcoinNodeWalletProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import BitcoinNodeWalletProvider from './BitcoinNodeWalletProvider';
2
+ export { BitcoinNodeWalletProvider };
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.BitcoinNodeWalletProvider = void 0;
7
+ const BitcoinNodeWalletProvider_1 = __importDefault(require("./BitcoinNodeWalletProvider"));
8
+ exports.BitcoinNodeWalletProvider = BitcoinNodeWalletProvider_1.default;
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;AAAA,4FAAmE;AAE1D,oCAFF,mCAAyB,CAEE"}
@@ -0,0 +1,303 @@
1
+ import {
2
+ decodeRawTransaction,
3
+ normalizeTransactionObject,
4
+ } from '@atomicfinance/bitcoin-utils';
5
+ import { sha256 } from '@atomicfinance/crypto';
6
+ import { JsonRpcProvider } from '@atomicfinance/jsonrpc-provider';
7
+ import Provider from '@atomicfinance/provider';
8
+ import {
9
+ Address,
10
+ BigNumber,
11
+ bitcoin,
12
+ BitcoinJsonRpcTypes,
13
+ SendOptions,
14
+ Transaction,
15
+ } from '@atomicfinance/types';
16
+ import { BitcoinNetwork, BitcoinNetworks } from 'bitcoin-networks';
17
+ import {
18
+ ECPair,
19
+ Psbt,
20
+ script,
21
+ Transaction as BitcoinJsTransaction,
22
+ } from 'bitcoinjs-lib';
23
+ import { flatten, isString, uniq } from 'lodash';
24
+
25
+ const BIP70_CHAIN_TO_NETWORK: { [index: string]: BitcoinNetwork } = {
26
+ main: BitcoinNetworks.bitcoin,
27
+ test: BitcoinNetworks.bitcoin_testnet,
28
+ regtest: BitcoinNetworks.bitcoin_regtest,
29
+ };
30
+
31
+ interface ProviderOptions {
32
+ // RPC URI
33
+ uri: string;
34
+ // Authentication username
35
+ username?: string;
36
+ // Authentication password
37
+ password?: string;
38
+ // Bitcoin network
39
+ network: BitcoinNetwork;
40
+ // Address type. Default: bech32
41
+ addressType?: bitcoin.AddressType;
42
+ }
43
+
44
+ export default class BitcoinNodeWalletProvider extends Provider {
45
+ _addressType: bitcoin.AddressType;
46
+ _network: BitcoinNetwork;
47
+ _rpc: JsonRpcProvider;
48
+ _addressInfoCache: { [key: string]: Address };
49
+
50
+ constructor(opts: ProviderOptions) {
51
+ super();
52
+
53
+ const {
54
+ uri,
55
+ username,
56
+ password,
57
+ network,
58
+ addressType = bitcoin.AddressType.BECH32,
59
+ } = opts;
60
+ const addressTypes = Object.values(bitcoin.AddressType);
61
+ if (!addressTypes.includes(addressType)) {
62
+ throw new Error(`addressType must be one of ${addressTypes.join(',')}`);
63
+ }
64
+ this._addressType = addressType;
65
+ this._network = network;
66
+ this._rpc = new JsonRpcProvider(uri, username, password);
67
+ this._addressInfoCache = {};
68
+ }
69
+
70
+ async signMessage(message: string, from: string) {
71
+ return this._rpc
72
+ .jsonrpc('signmessage', from, message)
73
+ .then((result: string) => Buffer.from(result, 'base64').toString('hex'));
74
+ }
75
+
76
+ async withTxFee(
77
+ func: () => Promise<Transaction<bitcoin.Transaction>>,
78
+ feePerByte: number,
79
+ ) {
80
+ const feePerKB = new BigNumber(feePerByte).div(1e8).times(1000).toNumber();
81
+ const originalTxFee: number = (await this._rpc.jsonrpc('getwalletinfo'))
82
+ .paytxfee;
83
+ await this._rpc.jsonrpc('settxfee', feePerKB);
84
+
85
+ const result = await func();
86
+
87
+ await this._rpc.jsonrpc('settxfee', originalTxFee);
88
+
89
+ return result;
90
+ }
91
+
92
+ async _sendTransaction(options: SendOptions) {
93
+ const value = new BigNumber(options.value).dividedBy(1e8).toNumber();
94
+ const hash = await this._rpc.jsonrpc(
95
+ 'sendtoaddress',
96
+ options.to,
97
+ value,
98
+ '',
99
+ '',
100
+ false,
101
+ true,
102
+ );
103
+ const transaction = await this._rpc.jsonrpc('gettransaction', hash, true);
104
+ const fee = new BigNumber(transaction.fee).abs().times(1e8).toNumber();
105
+ return normalizeTransactionObject(
106
+ decodeRawTransaction(transaction.hex, this._network),
107
+ fee,
108
+ );
109
+ }
110
+
111
+ async sendTransaction(options: SendOptions) {
112
+ return options.fee
113
+ ? this.withTxFee(
114
+ async () => this._sendTransaction(options),
115
+ options.fee as number,
116
+ )
117
+ : this._sendTransaction(options);
118
+ }
119
+
120
+ async updateTransactionFee(
121
+ tx: Transaction<bitcoin.Transaction>,
122
+ newFeePerByte: number,
123
+ ) {
124
+ const txHash = isString(tx) ? tx : tx.hash;
125
+ return this.withTxFee(async () => {
126
+ const result = await this._rpc.jsonrpc('bumpfee', txHash);
127
+ const transaction = await this._rpc.jsonrpc(
128
+ 'gettransaction',
129
+ result.txid,
130
+ true,
131
+ );
132
+ const fee = new BigNumber(transaction.fee).abs().times(1e8).toNumber();
133
+ return normalizeTransactionObject(
134
+ decodeRawTransaction(transaction.hex, this._network),
135
+ fee,
136
+ );
137
+ }, newFeePerByte);
138
+ }
139
+
140
+ async signPSBT(data: string, inputs: bitcoin.PsbtInputTarget[]) {
141
+ const psbt = Psbt.fromBase64(data, { network: this._network });
142
+
143
+ for (const input of inputs) {
144
+ const usedAddresses = await this.getUsedAddresses();
145
+ const address = usedAddresses.find(
146
+ (address) => address.derivationPath === input.derivationPath,
147
+ );
148
+ const wif = await this.dumpPrivKey(address.address);
149
+ const keyPair = ECPair.fromWIF(wif, this._network);
150
+ psbt.signInput(input.index, keyPair);
151
+ }
152
+
153
+ return psbt.toBase64();
154
+ }
155
+
156
+ async signBatchP2SHTransaction(
157
+ inputs: [
158
+ { inputTxHex: string; index: number; vout: any; outputScript: Buffer },
159
+ ],
160
+ addresses: string,
161
+ tx: any,
162
+ locktime: number,
163
+ segwit = false,
164
+ ) {
165
+ const wallets = [];
166
+ for (const address of addresses) {
167
+ const wif = await this.dumpPrivKey(address);
168
+ const wallet = ECPair.fromWIF(wif, this._network);
169
+ wallets.push(wallet);
170
+ }
171
+
172
+ const sigs = [];
173
+ for (let i = 0; i < inputs.length; i++) {
174
+ let sigHash;
175
+ if (segwit) {
176
+ sigHash = tx.hashForWitnessV0(
177
+ inputs[i].index,
178
+ inputs[i].outputScript,
179
+ inputs[i].vout.vSat,
180
+ BitcoinJsTransaction.SIGHASH_ALL,
181
+ ); // AMOUNT NEEDS TO BE PREVOUT AMOUNT
182
+ } else {
183
+ sigHash = tx.hashForSignature(
184
+ inputs[i].index,
185
+ inputs[i].outputScript,
186
+ BitcoinJsTransaction.SIGHASH_ALL,
187
+ );
188
+ }
189
+
190
+ const sig = script.signature.encode(
191
+ wallets[i].sign(sigHash),
192
+ BitcoinJsTransaction.SIGHASH_ALL,
193
+ );
194
+ sigs.push(sig);
195
+ }
196
+
197
+ return sigs;
198
+ }
199
+
200
+ async dumpPrivKey(address: string): Promise<string> {
201
+ return this._rpc.jsonrpc('dumpprivkey', address);
202
+ }
203
+
204
+ async getNewAddress(addressType: bitcoin.AddressType, label = '') {
205
+ const params = addressType ? [label, addressType] : [label];
206
+ const newAddress = await this._rpc.jsonrpc('getnewaddress', ...params);
207
+
208
+ if (!newAddress) return null;
209
+
210
+ return this.getAddressInfo(newAddress);
211
+ }
212
+
213
+ async getAddressInfo(address: string): Promise<Address> {
214
+ if (address in this._addressInfoCache) {
215
+ return this._addressInfoCache[address];
216
+ }
217
+
218
+ const addressInfo: BitcoinJsonRpcTypes.AddressInfo = await this._rpc.jsonrpc(
219
+ 'getaddressinfo',
220
+ address,
221
+ );
222
+
223
+ let publicKey, derivationPath;
224
+
225
+ if (!addressInfo.iswatchonly) {
226
+ publicKey = addressInfo.pubkey;
227
+ derivationPath = addressInfo.hdkeypath;
228
+ }
229
+ const addressObject = new Address({ address, publicKey, derivationPath });
230
+ this._addressInfoCache[address] = addressObject;
231
+ return addressObject;
232
+ }
233
+
234
+ async getAddresses() {
235
+ return this.getUsedAddresses();
236
+ }
237
+
238
+ async getUnusedAddress() {
239
+ return this.getNewAddress(this._addressType);
240
+ }
241
+
242
+ async getUsedAddresses() {
243
+ const usedAddresses: BitcoinJsonRpcTypes.AddressGrouping[] = await this._rpc.jsonrpc(
244
+ 'listaddressgroupings',
245
+ );
246
+ const emptyAddresses: BitcoinJsonRpcTypes.ReceivedByAddress[] = await this._rpc.jsonrpc(
247
+ 'listreceivedbyaddress',
248
+ 0,
249
+ true,
250
+ false,
251
+ );
252
+
253
+ const addrs = uniq([
254
+ ...flatten(usedAddresses).map((addr) => addr[0]),
255
+ ...emptyAddresses.map((a) => a.address),
256
+ ]);
257
+
258
+ const addressObjects = await Promise.all(
259
+ addrs.map((address) => this.getAddressInfo(address)),
260
+ );
261
+
262
+ return addressObjects;
263
+ }
264
+
265
+ async getWalletAddress(address: string) {
266
+ return this.getAddressInfo(address);
267
+ }
268
+
269
+ async isWalletAvailable() {
270
+ try {
271
+ await this._rpc.jsonrpc('getwalletinfo');
272
+ return true;
273
+ } catch (e) {
274
+ return false;
275
+ }
276
+ }
277
+
278
+ async getConnectedNetwork() {
279
+ const blockchainInfo = await this._rpc.jsonrpc('getblockchaininfo');
280
+ const chain = blockchainInfo.chain;
281
+ return BIP70_CHAIN_TO_NETWORK[chain];
282
+ }
283
+
284
+ async generateSecret(message: string) {
285
+ const secretAddressLabel = 'secretAddress';
286
+ let address;
287
+ try {
288
+ const labelAddresses = await this._rpc.jsonrpc(
289
+ 'getaddressesbylabel',
290
+ secretAddressLabel,
291
+ );
292
+ address = Object.keys(labelAddresses)[0];
293
+ } catch (e) {
294
+ // Label does not exist
295
+ address = (
296
+ await this.getNewAddress(bitcoin.AddressType.LEGACY, secretAddressLabel)
297
+ ).address; // Signing only possible with legacy addresses
298
+ }
299
+ const signedMessage = await this.signMessage(message, address);
300
+ const secret = sha256(signedMessage);
301
+ return secret;
302
+ }
303
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import BitcoinNodeWalletProvider from './BitcoinNodeWalletProvider'
2
+
3
+ export { BitcoinNodeWalletProvider }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@atomicfinance/bitcoin-node-wallet-provider",
3
+ "version": "3.0.0",
4
+ "description": "",
5
+ "module": "dist/index.js",
6
+ "main": "dist/index.js",
7
+ "types": "dist/lib/index.d.ts",
8
+ "directories": {
9
+ "dist": "dist",
10
+ "src": "lib"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "lib"
15
+ ],
16
+ "scripts": {
17
+ "build": "../../node_modules/.bin/tsc --project tsconfig.json",
18
+ "lint": "../../node_modules/.bin/eslint --ignore-path ../../.eslintignore -c ../../.eslintrc.js .",
19
+ "lint:fix": "../../node_modules/.bin/eslint --fix --ignore-path ../../.eslintignore -c ../../.eslintrc.js .",
20
+ "test": "../../node_modules/.bin/nyc --reporter=lcov --reporter=text --extension=.ts ../../node_modules/.bin/mocha --recursive \"tests/**/*.test.*\""
21
+ },
22
+ "author": "Atomic Finance <info@atomic.finance>",
23
+ "license": "MIT",
24
+ "engines": {
25
+ "node": ">=14"
26
+ },
27
+ "dependencies": {
28
+ "@atomicfinance/bitcoin-utils": "^3.0.0",
29
+ "@atomicfinance/crypto": "^3.0.0",
30
+ "@atomicfinance/jsonrpc-provider": "^3.0.0",
31
+ "@atomicfinance/types": "^3.0.0",
32
+ "@atomicfinance/utils": "^3.0.0",
33
+ "@babel/runtime": "^7.12.1",
34
+ "bignumber.js": "^9.0.0",
35
+ "bitcoin-networks": "^1.0.0",
36
+ "bitcoinjs-lib": "^5.2.0",
37
+ "lodash": "^4.17.20"
38
+ },
39
+ "homepage": "https://github.com/atomicfinance/chainabstractionlayer#readme",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/atomicfinance/chainabstractionlayer.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/atomicfinance/chainabstractionlayer/issues"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "devDependencies": {
51
+ "@types/lodash": "^4.14.168"
52
+ },
53
+ "sideEffects": false
54
+ }