@airgap/bitcoin 0.13.45-beta.1 → 0.13.45-beta.3
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/package.json +5 -5
- package/v0/index.js +10 -10
- package/v0/index.js.map +1 -1
- package/v0/protocol/BitcoinAddress.js +8 -9
- package/v0/protocol/BitcoinAddress.js.map +1 -1
- package/v0/protocol/BitcoinCryptoClient.js +16 -85
- package/v0/protocol/BitcoinCryptoClient.js.map +1 -1
- package/v0/protocol/BitcoinProtocol.js +590 -987
- package/v0/protocol/BitcoinProtocol.js.map +1 -1
- package/v0/protocol/BitcoinProtocolOptions.js +45 -111
- package/v0/protocol/BitcoinProtocolOptions.js.map +1 -1
- package/v0/protocol/BitcoinSegwitAddress.js +12 -29
- package/v0/protocol/BitcoinSegwitAddress.js.map +1 -1
- package/v0/protocol/BitcoinSegwitProtocol.js +348 -483
- package/v0/protocol/BitcoinSegwitProtocol.js.map +1 -1
- package/v0/protocol/BitcoinTestnetProtocol.js +28 -36
- package/v0/protocol/BitcoinTestnetProtocol.js.map +1 -1
- package/v0/serializer/validators/transaction-validator.js +22 -30
- package/v0/serializer/validators/transaction-validator.js.map +1 -1
- package/v0/serializer/validators/validators.js +23 -23
- package/v0/serializer/validators/validators.js.map +1 -1
- package/v1/block-explorer/BlockCypherBlockExplorer.js +12 -61
- package/v1/block-explorer/BlockCypherBlockExplorer.js.map +1 -1
- package/v1/data/BitcoinAddress.js +9 -10
- package/v1/data/BitcoinAddress.js.map +1 -1
- package/v1/data/BitcoinLegacyAddress.js +11 -12
- package/v1/data/BitcoinLegacyAddress.js.map +1 -1
- package/v1/data/BitcoinSegwitAddress.js +11 -12
- package/v1/data/BitcoinSegwitAddress.js.map +1 -1
- package/v1/data/BitcoinTaprootAddress.js +31 -22
- package/v1/data/BitcoinTaprootAddress.js.map +1 -1
- package/v1/index.js +11 -11
- package/v1/index.js.map +1 -1
- package/v1/module/BitcoinModule.d.ts +1 -1
- package/v1/module/BitcoinModule.js +44 -102
- package/v1/module/BitcoinModule.js.map +1 -1
- package/v1/module.js +2 -3
- package/v1/module.js.map +1 -1
- package/v1/protocol/BitcoinCryptoClient.js +22 -90
- package/v1/protocol/BitcoinCryptoClient.js.map +1 -1
- package/v1/protocol/BitcoinLegacyProtocol.js +520 -796
- package/v1/protocol/BitcoinLegacyProtocol.js.map +1 -1
- package/v1/protocol/BitcoinProtocol.js +735 -1169
- package/v1/protocol/BitcoinProtocol.js.map +1 -1
- package/v1/protocol/BitcoinSegwitProtocol.js +542 -796
- package/v1/protocol/BitcoinSegwitProtocol.js.map +1 -1
- package/v1/protocol/BitcoinTaprootProtocol.js +688 -1000
- package/v1/protocol/BitcoinTaprootProtocol.js.map +1 -1
- package/v1/protocol/BitcoinTestnetProtocol.js +14 -33
- package/v1/protocol/BitcoinTestnetProtocol.js.map +1 -1
- package/v1/serializer/v3/schemas/converter/transaction-converter.js +29 -52
- package/v1/serializer/v3/schemas/converter/transaction-converter.js.map +1 -1
- package/v1/serializer/v3/serializer-companion.js +98 -165
- package/v1/serializer/v3/serializer-companion.js.map +1 -1
- package/v1/serializer/v3/validators/transaction-validator.js +13 -16
- package/v1/serializer/v3/validators/transaction-validator.js.map +1 -1
- package/v1/serializer/v3/validators/validators.js +122 -213
- package/v1/serializer/v3/validators/validators.js.map +1 -1
- package/v1/types/crypto.d.ts +1 -1
- package/v1/types/key.d.ts +6 -6
- package/v1/types/protocol.d.ts +2 -2
- package/v1/types/transaction.d.ts +3 -3
- package/v1/utils/common.js +4 -6
- package/v1/utils/common.js.map +1 -1
- package/v1/utils/key.d.ts +5 -6
- package/v1/utils/key.js +38 -39
- package/v1/utils/key.js.map +1 -1
- package/v1/utils/network.js +3 -4
- package/v1/utils/network.js.map +1 -1
- package/v1/utils/protocol.js +25 -19
- package/v1/utils/protocol.js.map +1 -1
- package/v1/utils/signature.js +4 -5
- package/v1/utils/signature.js.map +1 -1
|
@@ -15,77 +15,41 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
36
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
37
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
38
|
-
function step(op) {
|
|
39
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
40
|
-
while (_) try {
|
|
41
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
42
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
43
|
-
switch (op[0]) {
|
|
44
|
-
case 0: case 1: t = op; break;
|
|
45
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
46
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
47
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
48
|
-
default:
|
|
49
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
50
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
51
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
52
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
53
|
-
if (t[2]) _.ops.pop();
|
|
54
|
-
_.trys.pop(); continue;
|
|
55
|
-
}
|
|
56
|
-
op = body.call(thisArg, _);
|
|
57
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
58
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
62
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
63
|
-
if (ar || !(i in from)) {
|
|
64
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
65
|
-
ar[i] = from[i];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
69
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
70
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
71
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
72
37
|
};
|
|
73
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
74
39
|
exports.BitcoinProtocol = void 0;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (options === void 0) { options = new BitcoinProtocolOptions_1.BitcoinProtocolOptions(); }
|
|
40
|
+
const index_1 = __importDefault(require("@airgap/coinlib-core/dependencies/src/axios-0.19.0/index"));
|
|
41
|
+
const bignumber_1 = __importDefault(require("@airgap/coinlib-core/dependencies/src/bignumber.js-9.0.0/bignumber"));
|
|
42
|
+
const index_2 = require("@airgap/coinlib-core/dependencies/src/bip39-2.5.0/index");
|
|
43
|
+
const bitcoinJSMessage = __importStar(require("@airgap/coinlib-core/dependencies/src/bitcoinjs-message-2.1.1/index"));
|
|
44
|
+
const errors_1 = require("@airgap/coinlib-core/errors");
|
|
45
|
+
const coinlib_error_1 = require("@airgap/coinlib-core/errors/coinlib-error");
|
|
46
|
+
const ProtocolSymbols_1 = require("@airgap/coinlib-core/utils/ProtocolSymbols");
|
|
47
|
+
const BitcoinAddress_1 = require("./BitcoinAddress");
|
|
48
|
+
const BitcoinCryptoClient_1 = require("./BitcoinCryptoClient");
|
|
49
|
+
const BitcoinProtocolOptions_1 = require("./BitcoinProtocolOptions");
|
|
50
|
+
const DUST_AMOUNT = 50;
|
|
51
|
+
class BitcoinProtocol {
|
|
52
|
+
constructor(options = new BitcoinProtocolOptions_1.BitcoinProtocolOptions()) {
|
|
89
53
|
this.options = options;
|
|
90
54
|
this.symbol = 'BTC';
|
|
91
55
|
this.name = 'Bitcoin (Legacy)';
|
|
@@ -114,957 +78,596 @@ var BitcoinProtocol = /** @class */ (function () {
|
|
|
114
78
|
}
|
|
115
79
|
];
|
|
116
80
|
this.supportsHD = true;
|
|
117
|
-
this.standardDerivationPath =
|
|
81
|
+
this.standardDerivationPath = `m/44'/0'/0'`;
|
|
118
82
|
this.addressIsCaseSensitive = true;
|
|
119
83
|
this.addressValidationPattern = '^(?:[13]{1}[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-z0-9]{39,59})$';
|
|
120
84
|
this.addressPlaceholder = '1ABC...';
|
|
121
85
|
this.cryptoClient = new BitcoinCryptoClient_1.BitcoinCryptoClient(this, bitcoinJSMessage);
|
|
122
86
|
}
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return __generator(this, function (_a) {
|
|
253
|
-
secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
254
|
-
return [2 /*return*/, this.getPublicKeyFromHexSecret(secret, derivationPath)];
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
};
|
|
258
|
-
BitcoinProtocol.prototype.getPrivateKeyFromMnemonic = function (mnemonic, derivationPath, password) {
|
|
259
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
260
|
-
var secret;
|
|
261
|
-
return __generator(this, function (_a) {
|
|
262
|
-
secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
263
|
-
return [2 /*return*/, this.getPrivateKeyFromHexSecret(secret, derivationPath)];
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
};
|
|
267
|
-
BitcoinProtocol.prototype.getExtendedPublicKeyFromMnemonic = function (mnemonic, derivationPath, password) {
|
|
268
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
269
|
-
var secret;
|
|
270
|
-
return __generator(this, function (_a) {
|
|
271
|
-
secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
272
|
-
return [2 /*return*/, this.getExtendedPublicKeyFromHexSecret(secret, derivationPath)];
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
};
|
|
276
|
-
BitcoinProtocol.prototype.getExtendedPrivateKeyFromMnemonic = function (mnemonic, derivationPath, password) {
|
|
277
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
278
|
-
var secret;
|
|
279
|
-
return __generator(this, function (_a) {
|
|
280
|
-
secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
281
|
-
return [2 /*return*/, this.getExtendedPrivateKeyFromHexSecret(secret, derivationPath)];
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
};
|
|
285
|
-
BitcoinProtocol.prototype.getPublicKeyFromHexSecret = function (secret, derivationPath) {
|
|
286
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
287
|
-
var bitcoinNode;
|
|
288
|
-
return __generator(this, function (_a) {
|
|
289
|
-
bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network);
|
|
290
|
-
return [2 /*return*/, bitcoinNode.derivePath(derivationPath).neutered().toBase58()];
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
};
|
|
294
|
-
BitcoinProtocol.prototype.getPrivateKeyFromHexSecret = function (secret, derivationPath) {
|
|
295
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
296
|
-
var bitcoinNode;
|
|
297
|
-
return __generator(this, function (_a) {
|
|
298
|
-
bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network);
|
|
299
|
-
return [2 /*return*/, bitcoinNode.derivePath(derivationPath).keyPair.d.toBuffer(32).toString('hex')];
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
};
|
|
303
|
-
BitcoinProtocol.prototype.getExtendedPublicKeyFromHexSecret = function (secret, derivationPath) {
|
|
304
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
305
|
-
return __generator(this, function (_a) {
|
|
306
|
-
return [2 /*return*/, this.getPublicKeyFromHexSecret(secret, derivationPath)];
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
};
|
|
310
|
-
BitcoinProtocol.prototype.getExtendedPrivateKeyFromHexSecret = function (secret, derivationPath) {
|
|
311
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
312
|
-
var bitcoinNode;
|
|
313
|
-
return __generator(this, function (_a) {
|
|
314
|
-
bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network);
|
|
315
|
-
return [2 /*return*/, bitcoinNode.derivePath(derivationPath).toBase58()];
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
};
|
|
319
|
-
BitcoinProtocol.prototype.getAddressFromPublicKey = function (publicKey, cursor) {
|
|
320
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
321
|
-
var node, address;
|
|
322
|
-
return __generator(this, function (_a) {
|
|
323
|
-
node = this.options.config.bitcoinJSLib.HDNode.fromBase58(publicKey, this.options.network.extras.network);
|
|
324
|
-
address = BitcoinAddress_1.BitcoinAddress.from(node);
|
|
325
|
-
return [2 /*return*/, {
|
|
326
|
-
address: address.asString(),
|
|
327
|
-
cursor: { hasNext: false }
|
|
328
|
-
}];
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
};
|
|
332
|
-
BitcoinProtocol.prototype.getAddressesFromPublicKey = function (publicKey, cursor) {
|
|
333
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
334
|
-
return __generator(this, function (_a) {
|
|
335
|
-
switch (_a.label) {
|
|
336
|
-
case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey, cursor)];
|
|
337
|
-
case 1: return [2 /*return*/, [_a.sent()]];
|
|
87
|
+
async getSymbol() {
|
|
88
|
+
return this.symbol;
|
|
89
|
+
}
|
|
90
|
+
async getName() {
|
|
91
|
+
return this.name;
|
|
92
|
+
}
|
|
93
|
+
async getMarketSymbol() {
|
|
94
|
+
return this.marketSymbol;
|
|
95
|
+
}
|
|
96
|
+
async getAssetSymbol() {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
async getFeeSymbol() {
|
|
100
|
+
return this.feeSymbol;
|
|
101
|
+
}
|
|
102
|
+
async getFeeDefaults() {
|
|
103
|
+
return this.feeDefaults;
|
|
104
|
+
}
|
|
105
|
+
async getDecimals() {
|
|
106
|
+
return this.decimals;
|
|
107
|
+
}
|
|
108
|
+
async getFeeDecimals() {
|
|
109
|
+
return this.feeDecimals;
|
|
110
|
+
}
|
|
111
|
+
async getIdentifier() {
|
|
112
|
+
return this.identifier;
|
|
113
|
+
}
|
|
114
|
+
async getUnits() {
|
|
115
|
+
return this.units;
|
|
116
|
+
}
|
|
117
|
+
async getSupportsHD() {
|
|
118
|
+
return this.supportsHD;
|
|
119
|
+
}
|
|
120
|
+
async getStandardDerivationPath() {
|
|
121
|
+
return this.standardDerivationPath;
|
|
122
|
+
}
|
|
123
|
+
async getAddressIsCaseSensitive() {
|
|
124
|
+
return this.addressIsCaseSensitive;
|
|
125
|
+
}
|
|
126
|
+
async getAddressValidationPattern() {
|
|
127
|
+
return this.addressValidationPattern;
|
|
128
|
+
}
|
|
129
|
+
async getAddressPlaceholder() {
|
|
130
|
+
return this.addressPlaceholder;
|
|
131
|
+
}
|
|
132
|
+
async getOptions() {
|
|
133
|
+
return this.options;
|
|
134
|
+
}
|
|
135
|
+
async getBlockExplorerLinkForAddress(address) {
|
|
136
|
+
return this.options.network.blockExplorer.getAddressLink(address);
|
|
137
|
+
}
|
|
138
|
+
async getBlockExplorerLinkForTxId(txId) {
|
|
139
|
+
return this.options.network.blockExplorer.getTransactionLink(txId);
|
|
140
|
+
}
|
|
141
|
+
async getPublicKeyFromMnemonic(mnemonic, derivationPath, password) {
|
|
142
|
+
const secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
143
|
+
return this.getPublicKeyFromHexSecret(secret, derivationPath);
|
|
144
|
+
}
|
|
145
|
+
async getPrivateKeyFromMnemonic(mnemonic, derivationPath, password) {
|
|
146
|
+
const secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
147
|
+
return this.getPrivateKeyFromHexSecret(secret, derivationPath);
|
|
148
|
+
}
|
|
149
|
+
async getExtendedPublicKeyFromMnemonic(mnemonic, derivationPath, password) {
|
|
150
|
+
const secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
151
|
+
return this.getExtendedPublicKeyFromHexSecret(secret, derivationPath);
|
|
152
|
+
}
|
|
153
|
+
async getExtendedPrivateKeyFromMnemonic(mnemonic, derivationPath, password) {
|
|
154
|
+
const secret = (0, index_2.mnemonicToSeed)(mnemonic, password);
|
|
155
|
+
return this.getExtendedPrivateKeyFromHexSecret(secret, derivationPath);
|
|
156
|
+
}
|
|
157
|
+
async getPublicKeyFromHexSecret(secret, derivationPath) {
|
|
158
|
+
const bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network);
|
|
159
|
+
return bitcoinNode.derivePath(derivationPath).neutered().toBase58();
|
|
160
|
+
}
|
|
161
|
+
async getPrivateKeyFromHexSecret(secret, derivationPath) {
|
|
162
|
+
const bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network);
|
|
163
|
+
return bitcoinNode.derivePath(derivationPath).keyPair.d.toBuffer(32).toString('hex');
|
|
164
|
+
}
|
|
165
|
+
async getExtendedPublicKeyFromHexSecret(secret, derivationPath) {
|
|
166
|
+
return this.getPublicKeyFromHexSecret(secret, derivationPath);
|
|
167
|
+
}
|
|
168
|
+
async getExtendedPrivateKeyFromHexSecret(secret, derivationPath) {
|
|
169
|
+
const bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network);
|
|
170
|
+
return bitcoinNode.derivePath(derivationPath).toBase58();
|
|
171
|
+
}
|
|
172
|
+
async getAddressFromPublicKey(publicKey, cursor) {
|
|
173
|
+
// broadcaster knows this (both broadcaster and signer)
|
|
174
|
+
const node = this.options.config.bitcoinJSLib.HDNode.fromBase58(publicKey, this.options.network.extras.network);
|
|
175
|
+
const address = BitcoinAddress_1.BitcoinAddress.from(node);
|
|
176
|
+
return {
|
|
177
|
+
address: address.asString(),
|
|
178
|
+
cursor: { hasNext: false }
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async getAddressesFromPublicKey(publicKey, cursor) {
|
|
182
|
+
return [await this.getAddressFromPublicKey(publicKey, cursor)];
|
|
183
|
+
}
|
|
184
|
+
async getAddressFromExtendedPublicKey(extendedPublicKey, visibilityDerivationIndex, addressDerivationIndex) {
|
|
185
|
+
// broadcaster knows this (both broadcaster and signer)
|
|
186
|
+
const node = this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPublicKey, this.options.network.extras.network);
|
|
187
|
+
const address = BitcoinAddress_1.BitcoinAddress.from(node, visibilityDerivationIndex, addressDerivationIndex);
|
|
188
|
+
return {
|
|
189
|
+
address: address.asString(),
|
|
190
|
+
cursor: { hasNext: false }
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async getAddressesFromExtendedPublicKey(extendedPublicKey, visibilityDerivationIndex, addressCount, offset) {
|
|
194
|
+
// broadcaster knows this (both broadcaster and signer)
|
|
195
|
+
const node = this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPublicKey, this.options.network.extras.network);
|
|
196
|
+
const generatorArray = Array.from(new Array(addressCount), (_, i) => i + offset);
|
|
197
|
+
return Promise.all(generatorArray.map((x) => {
|
|
198
|
+
const address = BitcoinAddress_1.BitcoinAddress.from(node, visibilityDerivationIndex, x);
|
|
199
|
+
return {
|
|
200
|
+
address: address.asString(),
|
|
201
|
+
cursor: { hasNext: false }
|
|
202
|
+
};
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
async signWithPrivateKey(privateKey, transaction) {
|
|
206
|
+
const transactionBuilder = new this.options.config.bitcoinJSLib.TransactionBuilder(this.options.network.extras.network);
|
|
207
|
+
for (const input of transaction.ins) {
|
|
208
|
+
transactionBuilder.addInput(input.txId, input.vout);
|
|
209
|
+
}
|
|
210
|
+
for (const output of transaction.outs) {
|
|
211
|
+
if (output.isChange) {
|
|
212
|
+
// 1. `getAddressFromPublicKey` expects a base58 encoded key
|
|
213
|
+
const generatedChangeAddress = (await this.getAddressFromPublicKey(privateKey)).address;
|
|
214
|
+
if (generatedChangeAddress !== output.recipient) {
|
|
215
|
+
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
338
216
|
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
return {
|
|
364
|
-
address: address.asString(),
|
|
365
|
-
cursor: { hasNext: false }
|
|
366
|
-
};
|
|
367
|
-
}))];
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
};
|
|
371
|
-
BitcoinProtocol.prototype.signWithPrivateKey = function (privateKey, transaction) {
|
|
372
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
373
|
-
var transactionBuilder, _i, _a, input, _b, _c, output, generatedChangeAddress, i;
|
|
374
|
-
return __generator(this, function (_d) {
|
|
375
|
-
switch (_d.label) {
|
|
376
|
-
case 0:
|
|
377
|
-
transactionBuilder = new this.options.config.bitcoinJSLib.TransactionBuilder(this.options.network.extras.network);
|
|
378
|
-
for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) {
|
|
379
|
-
input = _a[_i];
|
|
380
|
-
transactionBuilder.addInput(input.txId, input.vout);
|
|
381
|
-
}
|
|
382
|
-
_b = 0, _c = transaction.outs;
|
|
383
|
-
_d.label = 1;
|
|
384
|
-
case 1:
|
|
385
|
-
if (!(_b < _c.length)) return [3 /*break*/, 5];
|
|
386
|
-
output = _c[_b];
|
|
387
|
-
if (!output.isChange) return [3 /*break*/, 3];
|
|
388
|
-
return [4 /*yield*/, this.getAddressFromPublicKey(privateKey)];
|
|
389
|
-
case 2:
|
|
390
|
-
generatedChangeAddress = (_d.sent()).address;
|
|
391
|
-
if (generatedChangeAddress !== output.recipient) {
|
|
392
|
-
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
393
|
-
}
|
|
394
|
-
_d.label = 3;
|
|
395
|
-
case 3:
|
|
396
|
-
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
397
|
-
_d.label = 4;
|
|
398
|
-
case 4:
|
|
399
|
-
_b++;
|
|
400
|
-
return [3 /*break*/, 1];
|
|
401
|
-
case 5:
|
|
402
|
-
for (i = 0; i < transaction.ins.length; i++) {
|
|
403
|
-
// 2. `privateKey` is used as a hex string
|
|
404
|
-
transactionBuilder.sign(i, Buffer.from(privateKey, 'hex'));
|
|
405
|
-
}
|
|
406
|
-
// TODO: given 1 & 2, check if it works
|
|
407
|
-
return [2 /*return*/, transactionBuilder.build().toHex()];
|
|
217
|
+
}
|
|
218
|
+
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
219
|
+
}
|
|
220
|
+
for (let i = 0; i < transaction.ins.length; i++) {
|
|
221
|
+
// 2. `privateKey` is used as a hex string
|
|
222
|
+
transactionBuilder.sign(i, Buffer.from(privateKey, 'hex'));
|
|
223
|
+
}
|
|
224
|
+
// TODO: given 1 & 2, check if it works
|
|
225
|
+
return transactionBuilder.build().toHex();
|
|
226
|
+
}
|
|
227
|
+
async signWithExtendedPrivateKey(extendedPrivateKey, transaction) {
|
|
228
|
+
const transactionBuilder = new this.options.config.bitcoinJSLib.TransactionBuilder(this.options.network.extras.network);
|
|
229
|
+
const node = this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPrivateKey, this.options.network.extras.network);
|
|
230
|
+
for (const input of transaction.ins) {
|
|
231
|
+
transactionBuilder.addInput(input.txId, input.vout);
|
|
232
|
+
}
|
|
233
|
+
const changeAddressBatchSize = 10;
|
|
234
|
+
const changeAddressMaxAddresses = 500;
|
|
235
|
+
for (const output of transaction.outs) {
|
|
236
|
+
let changeAddressIsValid = false;
|
|
237
|
+
if (output.isChange) {
|
|
238
|
+
if (output.derivationPath) {
|
|
239
|
+
const generatedChangeAddress = (await this.getAddressesFromExtendedPublicKey(extendedPrivateKey, 1, 1, parseInt(output.derivationPath, 10))).map((address) => address.address);
|
|
240
|
+
changeAddressIsValid = generatedChangeAddress.includes(output.recipient);
|
|
408
241
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
BitcoinProtocol.prototype.signWithExtendedPrivateKey = function (extendedPrivateKey, transaction) {
|
|
413
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
414
|
-
var transactionBuilder, node, _i, _a, input, changeAddressBatchSize, changeAddressMaxAddresses, _b, _c, output, changeAddressIsValid, generatedChangeAddress, x, addresses, i;
|
|
415
|
-
return __generator(this, function (_d) {
|
|
416
|
-
switch (_d.label) {
|
|
417
|
-
case 0:
|
|
418
|
-
transactionBuilder = new this.options.config.bitcoinJSLib.TransactionBuilder(this.options.network.extras.network);
|
|
419
|
-
node = this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPrivateKey, this.options.network.extras.network);
|
|
420
|
-
for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) {
|
|
421
|
-
input = _a[_i];
|
|
422
|
-
transactionBuilder.addInput(input.txId, input.vout);
|
|
423
|
-
}
|
|
424
|
-
changeAddressBatchSize = 10;
|
|
425
|
-
changeAddressMaxAddresses = 500;
|
|
426
|
-
_b = 0, _c = transaction.outs;
|
|
427
|
-
_d.label = 1;
|
|
428
|
-
case 1:
|
|
429
|
-
if (!(_b < _c.length)) return [3 /*break*/, 10];
|
|
430
|
-
output = _c[_b];
|
|
431
|
-
changeAddressIsValid = false;
|
|
432
|
-
if (!output.isChange) return [3 /*break*/, 8];
|
|
433
|
-
if (!output.derivationPath) return [3 /*break*/, 3];
|
|
434
|
-
return [4 /*yield*/, this.getAddressesFromExtendedPublicKey(extendedPrivateKey, 1, 1, parseInt(output.derivationPath, 10))];
|
|
435
|
-
case 2:
|
|
436
|
-
generatedChangeAddress = (_d.sent()).map(function (address) { return address.address; });
|
|
437
|
-
changeAddressIsValid = generatedChangeAddress.includes(output.recipient);
|
|
438
|
-
return [3 /*break*/, 7];
|
|
439
|
-
case 3:
|
|
440
|
-
x = 0;
|
|
441
|
-
_d.label = 4;
|
|
442
|
-
case 4:
|
|
443
|
-
if (!(x < changeAddressMaxAddresses)) return [3 /*break*/, 7];
|
|
444
|
-
return [4 /*yield*/, this.getAddressesFromExtendedPublicKey(extendedPrivateKey, 1, changeAddressBatchSize, x)];
|
|
445
|
-
case 5:
|
|
446
|
-
addresses = (_d.sent()).map(function (address) { return address.address; });
|
|
242
|
+
else {
|
|
243
|
+
for (let x = 0; x < changeAddressMaxAddresses; x += changeAddressBatchSize) {
|
|
244
|
+
const addresses = (await this.getAddressesFromExtendedPublicKey(extendedPrivateKey, 1, changeAddressBatchSize, x)).map((address) => address.address);
|
|
447
245
|
if (addresses.indexOf(output.recipient) >= 0) {
|
|
448
246
|
changeAddressIsValid = true;
|
|
449
247
|
x = changeAddressMaxAddresses;
|
|
450
248
|
}
|
|
451
|
-
_d.label = 6;
|
|
452
|
-
case 6:
|
|
453
|
-
x += changeAddressBatchSize;
|
|
454
|
-
return [3 /*break*/, 4];
|
|
455
|
-
case 7:
|
|
456
|
-
if (!changeAddressIsValid) {
|
|
457
|
-
throw new errors_1.InvalidValueError(coinlib_error_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
458
|
-
}
|
|
459
|
-
_d.label = 8;
|
|
460
|
-
case 8:
|
|
461
|
-
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
462
|
-
_d.label = 9;
|
|
463
|
-
case 9:
|
|
464
|
-
_b++;
|
|
465
|
-
return [3 /*break*/, 1];
|
|
466
|
-
case 10:
|
|
467
|
-
for (i = 0; i < transaction.ins.length; i++) {
|
|
468
|
-
transactionBuilder.sign(i, node.derivePath(transaction.ins[i].derivationPath));
|
|
469
|
-
}
|
|
470
|
-
return [2 /*return*/, transactionBuilder.build().toHex()];
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
});
|
|
474
|
-
};
|
|
475
|
-
BitcoinProtocol.prototype.getTransactionDetails = function (unsignedTx) {
|
|
476
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
477
|
-
var transaction, feeCalculator, _i, _a, txIn, _b, _c, txOut, warnings;
|
|
478
|
-
return __generator(this, function (_d) {
|
|
479
|
-
transaction = unsignedTx.transaction;
|
|
480
|
-
feeCalculator = new bignumber_1.default(0);
|
|
481
|
-
for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) {
|
|
482
|
-
txIn = _a[_i];
|
|
483
|
-
feeCalculator = feeCalculator.plus(new bignumber_1.default(txIn.value));
|
|
484
|
-
}
|
|
485
|
-
for (_b = 0, _c = transaction.outs; _b < _c.length; _b++) {
|
|
486
|
-
txOut = _c[_b];
|
|
487
|
-
feeCalculator = feeCalculator.minus(new bignumber_1.default(txOut.value));
|
|
488
|
-
}
|
|
489
|
-
warnings = [];
|
|
490
|
-
return [2 /*return*/, [
|
|
491
|
-
{
|
|
492
|
-
from: transaction.ins.map(function (obj) { return obj.address; }),
|
|
493
|
-
to: transaction.outs.filter(function (obj) { return !obj.isChange; }).map(function (obj) { return obj.recipient; }),
|
|
494
|
-
amount: transaction.outs
|
|
495
|
-
.filter(function (obj) { return !obj.isChange; })
|
|
496
|
-
.map(function (obj) { return new bignumber_1.default(obj.value); })
|
|
497
|
-
.reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); })
|
|
498
|
-
.toString(10),
|
|
499
|
-
fee: feeCalculator.toString(10),
|
|
500
|
-
protocolIdentifier: this.identifier,
|
|
501
|
-
network: this.options.network,
|
|
502
|
-
isInbound: false,
|
|
503
|
-
transactionDetails: unsignedTx.transaction,
|
|
504
|
-
warnings: warnings
|
|
505
|
-
}
|
|
506
|
-
]];
|
|
507
|
-
});
|
|
508
|
-
});
|
|
509
|
-
};
|
|
510
|
-
BitcoinProtocol.prototype.getTransactionDetailsFromSigned = function (signedTx) {
|
|
511
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
512
|
-
var tx, bitcoinTx;
|
|
513
|
-
var _this = this;
|
|
514
|
-
return __generator(this, function (_a) {
|
|
515
|
-
tx = {
|
|
516
|
-
to: [],
|
|
517
|
-
from: signedTx.from,
|
|
518
|
-
amount: signedTx.amount,
|
|
519
|
-
fee: signedTx.fee,
|
|
520
|
-
protocolIdentifier: this.identifier,
|
|
521
|
-
network: this.options.network,
|
|
522
|
-
isInbound: false,
|
|
523
|
-
transactionDetails: signedTx.transaction
|
|
524
|
-
};
|
|
525
|
-
bitcoinTx = this.options.config.bitcoinJSLib.Transaction.fromHex(signedTx.transaction);
|
|
526
|
-
bitcoinTx.outs.forEach(function (output) {
|
|
527
|
-
var address = _this.options.config.bitcoinJSLib.address.fromOutputScript(output.script, _this.options.network.extras.network);
|
|
528
|
-
// only works if one output is target and rest is change, but this way we can filter out change addresses
|
|
529
|
-
if (new bignumber_1.default(output.value).isEqualTo(signedTx.amount)) {
|
|
530
|
-
tx.to.push(address);
|
|
531
249
|
}
|
|
532
|
-
});
|
|
533
|
-
return [2 /*return*/, [tx]];
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
};
|
|
537
|
-
BitcoinProtocol.prototype.getBalanceOfAddresses = function (addresses) {
|
|
538
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
539
|
-
var valueAccumulator, _i, addresses_1, address, data;
|
|
540
|
-
return __generator(this, function (_a) {
|
|
541
|
-
switch (_a.label) {
|
|
542
|
-
case 0:
|
|
543
|
-
valueAccumulator = new bignumber_1.default(0);
|
|
544
|
-
_i = 0, addresses_1 = addresses;
|
|
545
|
-
_a.label = 1;
|
|
546
|
-
case 1:
|
|
547
|
-
if (!(_i < addresses_1.length)) return [3 /*break*/, 4];
|
|
548
|
-
address = addresses_1[_i];
|
|
549
|
-
return [4 /*yield*/, index_1.default.get("".concat(this.options.network.extras.indexerApi, "/api/v2/address/").concat(address, "?details=basic"), {
|
|
550
|
-
responseType: 'json'
|
|
551
|
-
})];
|
|
552
|
-
case 2:
|
|
553
|
-
data = (_a.sent()).data;
|
|
554
|
-
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(data.balance));
|
|
555
|
-
_a.label = 3;
|
|
556
|
-
case 3:
|
|
557
|
-
_i++;
|
|
558
|
-
return [3 /*break*/, 1];
|
|
559
|
-
case 4: return [2 /*return*/, valueAccumulator.toString(10)];
|
|
560
250
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
};
|
|
564
|
-
BitcoinProtocol.prototype.getBalanceOfPublicKey = function (publicKey) {
|
|
565
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
566
|
-
var address;
|
|
567
|
-
return __generator(this, function (_a) {
|
|
568
|
-
switch (_a.label) {
|
|
569
|
-
case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)];
|
|
570
|
-
case 1:
|
|
571
|
-
address = _a.sent();
|
|
572
|
-
return [2 /*return*/, this.getBalanceOfAddresses([address.address])];
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
});
|
|
576
|
-
};
|
|
577
|
-
BitcoinProtocol.prototype.getBalanceOfExtendedPublicKey = function (extendedPublicKey, offset) {
|
|
578
|
-
if (offset === void 0) { offset = 0; }
|
|
579
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
580
|
-
var data;
|
|
581
|
-
return __generator(this, function (_a) {
|
|
582
|
-
switch (_a.label) {
|
|
583
|
-
case 0: return [4 /*yield*/, index_1.default.get("".concat(this.options.network.extras.indexerApi, "/api/v2/xpub/").concat(extendedPublicKey, "?pageSize=1"), {
|
|
584
|
-
responseType: 'json'
|
|
585
|
-
})];
|
|
586
|
-
case 1:
|
|
587
|
-
data = (_a.sent()).data;
|
|
588
|
-
return [2 /*return*/, data.balance];
|
|
251
|
+
if (!changeAddressIsValid) {
|
|
252
|
+
throw new errors_1.InvalidValueError(coinlib_error_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
589
253
|
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
254
|
+
}
|
|
255
|
+
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
256
|
+
}
|
|
257
|
+
for (let i = 0; i < transaction.ins.length; i++) {
|
|
258
|
+
transactionBuilder.sign(i, node.derivePath(transaction.ins[i].derivationPath));
|
|
259
|
+
}
|
|
260
|
+
return transactionBuilder.build().toHex();
|
|
261
|
+
}
|
|
262
|
+
async getTransactionDetails(unsignedTx) {
|
|
263
|
+
// out of public information (both broadcaster and signer)
|
|
264
|
+
const transaction = unsignedTx.transaction;
|
|
265
|
+
let feeCalculator = new bignumber_1.default(0);
|
|
266
|
+
for (const txIn of transaction.ins) {
|
|
267
|
+
feeCalculator = feeCalculator.plus(new bignumber_1.default(txIn.value));
|
|
268
|
+
}
|
|
269
|
+
for (const txOut of transaction.outs) {
|
|
270
|
+
feeCalculator = feeCalculator.minus(new bignumber_1.default(txOut.value));
|
|
271
|
+
}
|
|
272
|
+
const warnings = [];
|
|
273
|
+
return [
|
|
274
|
+
{
|
|
275
|
+
from: transaction.ins.map((obj) => obj.address),
|
|
276
|
+
to: transaction.outs.filter((obj) => !obj.isChange).map((obj) => obj.recipient),
|
|
277
|
+
amount: transaction.outs
|
|
278
|
+
.filter((obj) => !obj.isChange)
|
|
279
|
+
.map((obj) => new bignumber_1.default(obj.value))
|
|
280
|
+
.reduce((accumulator, currentValue) => accumulator.plus(currentValue))
|
|
281
|
+
.toString(10),
|
|
282
|
+
fee: feeCalculator.toString(10),
|
|
283
|
+
protocolIdentifier: this.identifier,
|
|
284
|
+
network: this.options.network,
|
|
285
|
+
isInbound: false,
|
|
286
|
+
transactionDetails: unsignedTx.transaction,
|
|
287
|
+
warnings
|
|
288
|
+
}
|
|
289
|
+
];
|
|
290
|
+
}
|
|
291
|
+
async getTransactionDetailsFromSigned(signedTx) {
|
|
292
|
+
const tx = {
|
|
293
|
+
to: [],
|
|
294
|
+
from: signedTx.from,
|
|
295
|
+
amount: signedTx.amount,
|
|
296
|
+
fee: signedTx.fee,
|
|
297
|
+
protocolIdentifier: this.identifier,
|
|
298
|
+
network: this.options.network,
|
|
299
|
+
isInbound: false,
|
|
300
|
+
transactionDetails: signedTx.transaction
|
|
301
|
+
};
|
|
302
|
+
const bitcoinTx = this.options.config.bitcoinJSLib.Transaction.fromHex(signedTx.transaction);
|
|
303
|
+
bitcoinTx.outs.forEach((output) => {
|
|
304
|
+
const address = this.options.config.bitcoinJSLib.address.fromOutputScript(output.script, this.options.network.extras.network);
|
|
305
|
+
// only works if one output is target and rest is change, but this way we can filter out change addresses
|
|
306
|
+
if (new bignumber_1.default(output.value).isEqualTo(signedTx.amount)) {
|
|
307
|
+
tx.to.push(address);
|
|
308
|
+
}
|
|
612
309
|
});
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
310
|
+
return [tx];
|
|
311
|
+
}
|
|
312
|
+
async getBalanceOfAddresses(addresses) {
|
|
313
|
+
let valueAccumulator = new bignumber_1.default(0);
|
|
314
|
+
// The API doesn't support batch checking of balances, so we have to do it manually
|
|
315
|
+
for (const address of addresses) {
|
|
316
|
+
const { data } = await index_1.default.get(`${this.options.network.extras.indexerApi}/api/v2/address/${address}?details=basic`, {
|
|
317
|
+
responseType: 'json'
|
|
318
|
+
});
|
|
319
|
+
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(data.balance));
|
|
320
|
+
}
|
|
321
|
+
return valueAccumulator.toString(10);
|
|
322
|
+
}
|
|
323
|
+
async getBalanceOfPublicKey(publicKey) {
|
|
324
|
+
const address = await this.getAddressFromPublicKey(publicKey);
|
|
325
|
+
return this.getBalanceOfAddresses([address.address]);
|
|
326
|
+
}
|
|
327
|
+
async getBalanceOfExtendedPublicKey(extendedPublicKey, offset = 0) {
|
|
328
|
+
const { data } = await index_1.default.get(`${this.options.network.extras.indexerApi}/api/v2/xpub/${extendedPublicKey}?pageSize=1`, {
|
|
329
|
+
responseType: 'json'
|
|
619
330
|
});
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
331
|
+
return data.balance;
|
|
332
|
+
}
|
|
333
|
+
async getBalanceOfPublicKeyForSubProtocols(publicKey, subProtocols) {
|
|
334
|
+
throw Promise.reject('get balance of sub protocols not supported');
|
|
335
|
+
}
|
|
336
|
+
async getAvailableBalanceOfAddresses(addresses) {
|
|
337
|
+
return this.getBalanceOfAddresses(addresses);
|
|
338
|
+
}
|
|
339
|
+
async estimateMaxTransactionValueFromExtendedPublicKey(extendedPublicKey, recipients, fee) {
|
|
340
|
+
return this.getBalanceOfExtendedPublicKey(extendedPublicKey);
|
|
341
|
+
}
|
|
342
|
+
async estimateMaxTransactionValueFromPublicKey(publicKey, recipients, fee) {
|
|
343
|
+
return this.getBalanceOfPublicKey(publicKey);
|
|
344
|
+
}
|
|
345
|
+
async estimateFeeDefaultsFromExtendedPublicKey(publicKey, recipients, values, data) {
|
|
346
|
+
const result = (await index_1.default.get(`${this.options.network.extras.indexerApi}/api/v2/estimatefee/5`)).data.result;
|
|
347
|
+
const estimatedFee = new bignumber_1.default(result).shiftedBy(this.feeDecimals);
|
|
348
|
+
if (estimatedFee.isZero()) {
|
|
349
|
+
return this.feeDefaults;
|
|
350
|
+
}
|
|
351
|
+
const feeStepFactor = new bignumber_1.default(0.5);
|
|
352
|
+
const mediumFee = estimatedFee;
|
|
353
|
+
const lowFee = mediumFee.minus(mediumFee.times(feeStepFactor)).integerValue(bignumber_1.default.ROUND_FLOOR);
|
|
354
|
+
const highFee = mediumFee.plus(mediumFee.times(feeStepFactor)).integerValue(bignumber_1.default.ROUND_FLOOR);
|
|
355
|
+
return {
|
|
356
|
+
low: lowFee.shiftedBy(-this.feeDecimals).toFixed(),
|
|
357
|
+
medium: mediumFee.shiftedBy(-this.feeDecimals).toFixed(),
|
|
358
|
+
high: highFee.shiftedBy(-this.feeDecimals).toFixed()
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async estimateFeeDefaultsFromPublicKey(publicKey, recipients, values, data) {
|
|
362
|
+
return Promise.reject('estimating fee defaults using non extended public key not implemented');
|
|
363
|
+
}
|
|
364
|
+
async prepareTransactionFromExtendedPublicKey(extendedPublicKey, offset, recipients, values, fee, extras) {
|
|
365
|
+
const wrappedValues = values.map((value) => new bignumber_1.default(value));
|
|
366
|
+
const wrappedFee = new bignumber_1.default(fee);
|
|
367
|
+
const transaction = {
|
|
368
|
+
ins: [],
|
|
369
|
+
outs: []
|
|
370
|
+
};
|
|
371
|
+
if (recipients.length !== wrappedValues.length) {
|
|
372
|
+
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'recipients do not match values');
|
|
373
|
+
}
|
|
374
|
+
const { data: utxos } = await index_1.default
|
|
375
|
+
.get(`${this.options.network.extras.indexerApi}/api/v2/utxo/${extendedPublicKey}?confirmed=true`, {
|
|
376
|
+
responseType: 'json'
|
|
377
|
+
})
|
|
378
|
+
.catch((error) => {
|
|
379
|
+
throw new errors_1.NetworkError(coinlib_error_1.Domain.BITCOIN, error);
|
|
380
|
+
});
|
|
381
|
+
if (utxos.length <= 0) {
|
|
382
|
+
throw new errors_1.BalanceError(coinlib_error_1.Domain.BITCOIN, 'not enough balance'); // no transactions found on those addresses, probably won't find anything in the next ones
|
|
383
|
+
}
|
|
384
|
+
const totalRequiredBalance = wrappedValues
|
|
385
|
+
.reduce((accumulator, currentValue) => accumulator.plus(currentValue))
|
|
386
|
+
.plus(wrappedFee);
|
|
387
|
+
let valueAccumulator = new bignumber_1.default(0);
|
|
388
|
+
const getPathIndexes = (path) => {
|
|
389
|
+
const result = path
|
|
390
|
+
.split('/')
|
|
391
|
+
.slice(-2)
|
|
392
|
+
.map((item) => parseInt(item, 10))
|
|
393
|
+
.filter((item) => !isNaN(item));
|
|
394
|
+
if (result.length !== 2) {
|
|
395
|
+
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'Unexpected path format');
|
|
396
|
+
}
|
|
397
|
+
return [result[0], result[1]];
|
|
398
|
+
};
|
|
399
|
+
for (const utxo of utxos) {
|
|
400
|
+
valueAccumulator = valueAccumulator.plus(utxo.value);
|
|
401
|
+
const indexes = getPathIndexes(utxo.path);
|
|
402
|
+
const derivedAddress = await this.getAddressFromExtendedPublicKey(extendedPublicKey, indexes[0], indexes[1]);
|
|
403
|
+
if (derivedAddress.address === utxo.address) {
|
|
404
|
+
transaction.ins.push({
|
|
405
|
+
txId: utxo.txid,
|
|
406
|
+
value: new bignumber_1.default(utxo.value).toString(10),
|
|
407
|
+
vout: utxo.vout,
|
|
408
|
+
address: utxo.address,
|
|
409
|
+
derivationPath: indexes.join('/')
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
throw new errors_1.InvalidValueError(coinlib_error_1.Domain.BITCOIN, `Invalid address ${JSON.stringify(utxo.address)} returned from API`);
|
|
414
|
+
}
|
|
415
|
+
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (valueAccumulator.isLessThan(totalRequiredBalance)) {
|
|
420
|
+
throw new errors_1.BalanceError(coinlib_error_1.Domain.BITCOIN, 'not enough balance');
|
|
421
|
+
}
|
|
422
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
423
|
+
transaction.outs.push({
|
|
424
|
+
recipient: recipients[i],
|
|
425
|
+
isChange: false,
|
|
426
|
+
value: wrappedValues[i].toString(10),
|
|
427
|
+
derivationPath: '' // TODO: Remove this as soon as our serializer supports optional properties
|
|
428
|
+
});
|
|
429
|
+
valueAccumulator = valueAccumulator.minus(wrappedValues[i]);
|
|
430
|
+
}
|
|
431
|
+
const lastUsedInternalAddress = Math.max(-1, ...utxos
|
|
432
|
+
.map((utxo) => getPathIndexes(utxo.path))
|
|
433
|
+
.filter((indexes) => indexes[0] === 1)
|
|
434
|
+
.map((indexes) => indexes[1]));
|
|
435
|
+
// If the change is considered dust, the transaction will fail.
|
|
436
|
+
// Dust is a variable value around 300-600 satoshis, depending on the configuration.
|
|
437
|
+
// We set a low fee here to not block any transactions, but it might still fail due to "dust".
|
|
438
|
+
const changeValue = valueAccumulator.minus(wrappedFee);
|
|
439
|
+
if (changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) {
|
|
440
|
+
const changeAddressIndex = lastUsedInternalAddress + 1;
|
|
441
|
+
const derivedAddress = await this.getAddressFromExtendedPublicKey(extendedPublicKey, 1, changeAddressIndex);
|
|
442
|
+
transaction.outs.push({
|
|
443
|
+
recipient: derivedAddress.address,
|
|
444
|
+
isChange: true,
|
|
445
|
+
value: changeValue.toString(10),
|
|
446
|
+
derivationPath: changeAddressIndex.toString()
|
|
643
447
|
});
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
448
|
+
}
|
|
449
|
+
return transaction;
|
|
450
|
+
}
|
|
451
|
+
async prepareTransactionFromPublicKey(publicKey, recipients, values, fee) {
|
|
452
|
+
const wrappedValues = values.map((value) => new bignumber_1.default(value));
|
|
453
|
+
const wrappedFee = new bignumber_1.default(fee);
|
|
454
|
+
const transaction = {
|
|
455
|
+
ins: [],
|
|
456
|
+
outs: []
|
|
457
|
+
};
|
|
458
|
+
if (recipients.length !== wrappedValues.length) {
|
|
459
|
+
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'Recipient and value length does not match.');
|
|
460
|
+
}
|
|
461
|
+
const address = (await this.getAddressFromPublicKey(publicKey)).address;
|
|
462
|
+
const { data: utxos } = await index_1.default.get(`${this.options.network.extras.indexerApi}/api/v2/utxo/${address}`, {
|
|
463
|
+
responseType: 'json'
|
|
464
|
+
});
|
|
465
|
+
const totalRequiredBalance = wrappedValues
|
|
466
|
+
.reduce((accumulator, currentValue) => accumulator.plus(currentValue))
|
|
467
|
+
.plus(wrappedFee);
|
|
468
|
+
let valueAccumulator = new bignumber_1.default(0);
|
|
469
|
+
for (const utxo of utxos) {
|
|
470
|
+
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(utxo.value));
|
|
471
|
+
transaction.ins.push({
|
|
472
|
+
txId: utxo.txid,
|
|
473
|
+
value: new bignumber_1.default(utxo.value).toString(10),
|
|
474
|
+
vout: utxo.vout,
|
|
475
|
+
address
|
|
476
|
+
});
|
|
477
|
+
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (valueAccumulator.isLessThan(totalRequiredBalance)) {
|
|
482
|
+
throw new errors_1.BalanceError(coinlib_error_1.Domain.BITCOIN, `not enough balance, having ${valueAccumulator.toFixed()} of ${totalRequiredBalance.toFixed()}`);
|
|
483
|
+
}
|
|
484
|
+
// tx.addInput(utxo.txid, utxo.vout)
|
|
485
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
486
|
+
transaction.outs.push({
|
|
487
|
+
recipient: recipients[i],
|
|
488
|
+
isChange: false,
|
|
489
|
+
value: wrappedValues[i].toString(10)
|
|
490
|
+
});
|
|
491
|
+
valueAccumulator = valueAccumulator.minus(wrappedValues[i]);
|
|
492
|
+
// tx.addOutput(recipients[i], values[i])
|
|
493
|
+
}
|
|
494
|
+
// If the change is considered dust, the transaction will fail.
|
|
495
|
+
// Dust is a variable value around 300-600 satoshis, depending on the configuration.
|
|
496
|
+
// We set a low fee here to not block any transactions, but it might still fail due to "dust".
|
|
497
|
+
const changeValue = valueAccumulator.minus(wrappedFee);
|
|
498
|
+
if (changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) {
|
|
499
|
+
transaction.outs.push({
|
|
500
|
+
recipient: address,
|
|
501
|
+
isChange: true,
|
|
502
|
+
value: changeValue.toString(10)
|
|
650
503
|
});
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
totalRequiredBalance = wrappedValues
|
|
681
|
-
.reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); })
|
|
682
|
-
.plus(wrappedFee);
|
|
683
|
-
valueAccumulator = new bignumber_1.default(0);
|
|
684
|
-
getPathIndexes = function (path) {
|
|
685
|
-
var result = path
|
|
686
|
-
.split('/')
|
|
687
|
-
.slice(-2)
|
|
688
|
-
.map(function (item) { return parseInt(item, 10); })
|
|
689
|
-
.filter(function (item) { return !isNaN(item); });
|
|
690
|
-
if (result.length !== 2) {
|
|
691
|
-
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'Unexpected path format');
|
|
692
|
-
}
|
|
693
|
-
return [result[0], result[1]];
|
|
694
|
-
};
|
|
695
|
-
_i = 0, utxos_1 = utxos;
|
|
696
|
-
_a.label = 2;
|
|
697
|
-
case 2:
|
|
698
|
-
if (!(_i < utxos_1.length)) return [3 /*break*/, 5];
|
|
699
|
-
utxo = utxos_1[_i];
|
|
700
|
-
valueAccumulator = valueAccumulator.plus(utxo.value);
|
|
701
|
-
indexes = getPathIndexes(utxo.path);
|
|
702
|
-
return [4 /*yield*/, this.getAddressFromExtendedPublicKey(extendedPublicKey, indexes[0], indexes[1])];
|
|
703
|
-
case 3:
|
|
704
|
-
derivedAddress = _a.sent();
|
|
705
|
-
if (derivedAddress.address === utxo.address) {
|
|
706
|
-
transaction.ins.push({
|
|
707
|
-
txId: utxo.txid,
|
|
708
|
-
value: new bignumber_1.default(utxo.value).toString(10),
|
|
709
|
-
vout: utxo.vout,
|
|
710
|
-
address: utxo.address,
|
|
711
|
-
derivationPath: indexes.join('/')
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
throw new errors_1.InvalidValueError(coinlib_error_1.Domain.BITCOIN, "Invalid address ".concat(JSON.stringify(utxo.address), " returned from API"));
|
|
716
|
-
}
|
|
717
|
-
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
718
|
-
return [3 /*break*/, 5];
|
|
719
|
-
}
|
|
720
|
-
_a.label = 4;
|
|
721
|
-
case 4:
|
|
722
|
-
_i++;
|
|
723
|
-
return [3 /*break*/, 2];
|
|
724
|
-
case 5:
|
|
725
|
-
if (valueAccumulator.isLessThan(totalRequiredBalance)) {
|
|
726
|
-
throw new errors_1.BalanceError(coinlib_error_1.Domain.BITCOIN, 'not enough balance');
|
|
727
|
-
}
|
|
728
|
-
for (i = 0; i < recipients.length; i++) {
|
|
729
|
-
transaction.outs.push({
|
|
730
|
-
recipient: recipients[i],
|
|
731
|
-
isChange: false,
|
|
732
|
-
value: wrappedValues[i].toString(10),
|
|
733
|
-
derivationPath: '' // TODO: Remove this as soon as our serializer supports optional properties
|
|
734
|
-
});
|
|
735
|
-
valueAccumulator = valueAccumulator.minus(wrappedValues[i]);
|
|
736
|
-
}
|
|
737
|
-
lastUsedInternalAddress = Math.max.apply(Math, __spreadArray([-1], utxos
|
|
738
|
-
.map(function (utxo) { return getPathIndexes(utxo.path); })
|
|
739
|
-
.filter(function (indexes) { return indexes[0] === 1; })
|
|
740
|
-
.map(function (indexes) { return indexes[1]; }), false));
|
|
741
|
-
changeValue = valueAccumulator.minus(wrappedFee);
|
|
742
|
-
if (!changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) return [3 /*break*/, 7];
|
|
743
|
-
changeAddressIndex = lastUsedInternalAddress + 1;
|
|
744
|
-
return [4 /*yield*/, this.getAddressFromExtendedPublicKey(extendedPublicKey, 1, changeAddressIndex)];
|
|
745
|
-
case 6:
|
|
746
|
-
derivedAddress = _a.sent();
|
|
747
|
-
transaction.outs.push({
|
|
748
|
-
recipient: derivedAddress.address,
|
|
749
|
-
isChange: true,
|
|
750
|
-
value: changeValue.toString(10),
|
|
751
|
-
derivationPath: changeAddressIndex.toString()
|
|
752
|
-
});
|
|
753
|
-
_a.label = 7;
|
|
754
|
-
case 7: return [2 /*return*/, transaction];
|
|
504
|
+
}
|
|
505
|
+
return transaction;
|
|
506
|
+
}
|
|
507
|
+
async broadcastTransaction(rawTransaction) {
|
|
508
|
+
const { data } = await index_1.default.post(this.options.network.extras.indexerApi + '/api/v2/sendtx/', rawTransaction);
|
|
509
|
+
return data.result;
|
|
510
|
+
}
|
|
511
|
+
async getTransactionsFromExtendedPublicKey(extendedPublicKey, limit, cursor, addressOffset = 0) {
|
|
512
|
+
const page = cursor?.page ?? 1;
|
|
513
|
+
const { data } = await index_1.default.get(this.options.network.extras.indexerApi +
|
|
514
|
+
'/api/v2/xpub/' +
|
|
515
|
+
extendedPublicKey +
|
|
516
|
+
`?details=txs&tokens=used&pageSize=${limit}&page=${page}`, {
|
|
517
|
+
responseType: 'json'
|
|
518
|
+
});
|
|
519
|
+
const ourAddresses = (data.tokens || []).filter((token) => token.type === 'XPUBAddress').map((token) => token.name);
|
|
520
|
+
const airGapTransactions = [];
|
|
521
|
+
if (data.page == page) {
|
|
522
|
+
for (const transaction of data.transactions || []) {
|
|
523
|
+
const tempAirGapTransactionFrom = [];
|
|
524
|
+
const tempAirGapTransactionTo = [];
|
|
525
|
+
let tempAirGapTransactionIsInbound = true;
|
|
526
|
+
let amount = new bignumber_1.default(0);
|
|
527
|
+
for (const vin of transaction.vin) {
|
|
528
|
+
if (this.containsSome(vin.addresses, ourAddresses)) {
|
|
529
|
+
tempAirGapTransactionIsInbound = false;
|
|
530
|
+
}
|
|
531
|
+
tempAirGapTransactionFrom.push(...vin.addresses);
|
|
532
|
+
amount = amount.plus(vin.value);
|
|
755
533
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
switch (_a.label) {
|
|
764
|
-
case 0:
|
|
765
|
-
wrappedValues = values.map(function (value) { return new bignumber_1.default(value); });
|
|
766
|
-
wrappedFee = new bignumber_1.default(fee);
|
|
767
|
-
transaction = {
|
|
768
|
-
ins: [],
|
|
769
|
-
outs: []
|
|
770
|
-
};
|
|
771
|
-
if (recipients.length !== wrappedValues.length) {
|
|
772
|
-
throw new errors_1.ConditionViolationError(coinlib_error_1.Domain.BITCOIN, 'Recipient and value length does not match.');
|
|
534
|
+
for (const vout of transaction.vout) {
|
|
535
|
+
if (vout.addresses) {
|
|
536
|
+
tempAirGapTransactionTo.push(...vout.addresses);
|
|
537
|
+
// If receiving address is our address, and transaction is outbound => our change
|
|
538
|
+
if (this.containsSome(vout.addresses, ourAddresses) && !tempAirGapTransactionIsInbound) {
|
|
539
|
+
// remove only if related to this address
|
|
540
|
+
amount = amount.minus(vout.value);
|
|
773
541
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
return [4 /*yield*/, index_1.default.get("".concat(this.options.network.extras.indexerApi, "/api/v2/utxo/").concat(address), {
|
|
778
|
-
responseType: 'json'
|
|
779
|
-
})];
|
|
780
|
-
case 2:
|
|
781
|
-
utxos = (_a.sent()).data;
|
|
782
|
-
totalRequiredBalance = wrappedValues
|
|
783
|
-
.reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); })
|
|
784
|
-
.plus(wrappedFee);
|
|
785
|
-
valueAccumulator = new bignumber_1.default(0);
|
|
786
|
-
for (_i = 0, utxos_2 = utxos; _i < utxos_2.length; _i++) {
|
|
787
|
-
utxo = utxos_2[_i];
|
|
788
|
-
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(utxo.value));
|
|
789
|
-
transaction.ins.push({
|
|
790
|
-
txId: utxo.txid,
|
|
791
|
-
value: new bignumber_1.default(utxo.value).toString(10),
|
|
792
|
-
vout: utxo.vout,
|
|
793
|
-
address: address
|
|
794
|
-
});
|
|
795
|
-
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
796
|
-
break;
|
|
797
|
-
}
|
|
542
|
+
// If receiving address is not ours, and transaction isbound => senders change
|
|
543
|
+
if (!this.containsSome(vout.addresses, ourAddresses) && tempAirGapTransactionIsInbound) {
|
|
544
|
+
amount = amount.minus(vout.value);
|
|
798
545
|
}
|
|
799
|
-
|
|
800
|
-
throw new errors_1.BalanceError(coinlib_error_1.Domain.BITCOIN, "not enough balance, having ".concat(valueAccumulator.toFixed(), " of ").concat(totalRequiredBalance.toFixed()));
|
|
801
|
-
}
|
|
802
|
-
// tx.addInput(utxo.txid, utxo.vout)
|
|
803
|
-
for (i = 0; i < recipients.length; i++) {
|
|
804
|
-
transaction.outs.push({
|
|
805
|
-
recipient: recipients[i],
|
|
806
|
-
isChange: false,
|
|
807
|
-
value: wrappedValues[i].toString(10)
|
|
808
|
-
});
|
|
809
|
-
valueAccumulator = valueAccumulator.minus(wrappedValues[i]);
|
|
810
|
-
// tx.addOutput(recipients[i], values[i])
|
|
811
|
-
}
|
|
812
|
-
changeValue = valueAccumulator.minus(wrappedFee);
|
|
813
|
-
if (changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) {
|
|
814
|
-
transaction.outs.push({
|
|
815
|
-
recipient: address,
|
|
816
|
-
isChange: true,
|
|
817
|
-
value: changeValue.toString(10)
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
return [2 /*return*/, transaction];
|
|
546
|
+
}
|
|
821
547
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
548
|
+
// deduct fee from amount
|
|
549
|
+
amount = amount.minus(transaction.fees);
|
|
550
|
+
const airGapTransaction = {
|
|
551
|
+
hash: transaction.txid,
|
|
552
|
+
from: tempAirGapTransactionFrom,
|
|
553
|
+
to: tempAirGapTransactionTo,
|
|
554
|
+
isInbound: tempAirGapTransactionIsInbound,
|
|
555
|
+
amount: amount.toString(10),
|
|
556
|
+
fee: new bignumber_1.default(transaction.fees).toString(10),
|
|
557
|
+
blockHeight: transaction.blockHeight.toString(),
|
|
558
|
+
protocolIdentifier: this.identifier,
|
|
559
|
+
network: this.options.network,
|
|
560
|
+
timestamp: transaction.blockTime
|
|
561
|
+
};
|
|
562
|
+
airGapTransactions.push(airGapTransaction);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
transactions: airGapTransactions,
|
|
567
|
+
cursor: {
|
|
568
|
+
page: cursor ? cursor.page + 1 : 2
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
async getTransactionsFromPublicKey(publicKey, limit, cursor) {
|
|
573
|
+
const address = await this.getAddressFromPublicKey(publicKey);
|
|
574
|
+
return this.getTransactionsFromAddresses([address.address], limit, cursor);
|
|
575
|
+
}
|
|
576
|
+
async getTransactionsFromAddresses(addresses, limit, cursor) {
|
|
577
|
+
const airGapTransactions = [];
|
|
578
|
+
const page = cursor?.page ?? 1;
|
|
579
|
+
const url = `${this.options.network.extras.indexerApi}/api/v2/address/${addresses[0]}?page=${page}&pageSize=${limit}&details=txs`;
|
|
580
|
+
const { data } = await index_1.default.get(url, {
|
|
581
|
+
responseType: 'json'
|
|
582
|
+
});
|
|
583
|
+
if (data.page == page) {
|
|
584
|
+
for (const transaction of data.transactions || []) {
|
|
585
|
+
const tempAirGapTransactionFrom = [];
|
|
586
|
+
const tempAirGapTransactionTo = [];
|
|
587
|
+
let tempAirGapTransactionIsInbound = true;
|
|
588
|
+
let amount = new bignumber_1.default(0);
|
|
589
|
+
for (const vin of transaction.vin) {
|
|
590
|
+
if (vin.addresses && this.containsSome(vin.addresses, addresses)) {
|
|
591
|
+
tempAirGapTransactionIsInbound = false;
|
|
592
|
+
}
|
|
593
|
+
tempAirGapTransactionFrom.push(...vin.addresses);
|
|
594
|
+
amount = vin.value ? amount.plus(vin.value) : amount;
|
|
834
595
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
var page, data, ourAddresses, airGapTransactions, _i, _b, transaction, tempAirGapTransactionFrom, tempAirGapTransactionTo, tempAirGapTransactionIsInbound, amount, _c, _d, vin, _e, _f, vout, airGapTransaction;
|
|
843
|
-
return __generator(this, function (_g) {
|
|
844
|
-
switch (_g.label) {
|
|
845
|
-
case 0:
|
|
846
|
-
page = (_a = cursor === null || cursor === void 0 ? void 0 : cursor.page) !== null && _a !== void 0 ? _a : 1;
|
|
847
|
-
return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi +
|
|
848
|
-
'/api/v2/xpub/' +
|
|
849
|
-
extendedPublicKey +
|
|
850
|
-
"?details=txs&tokens=used&pageSize=".concat(limit, "&page=").concat(page), {
|
|
851
|
-
responseType: 'json'
|
|
852
|
-
})];
|
|
853
|
-
case 1:
|
|
854
|
-
data = (_g.sent()).data;
|
|
855
|
-
ourAddresses = (data.tokens || []).filter(function (token) { return token.type === 'XPUBAddress'; }).map(function (token) { return token.name; });
|
|
856
|
-
airGapTransactions = [];
|
|
857
|
-
if (data.page == page) {
|
|
858
|
-
for (_i = 0, _b = data.transactions || []; _i < _b.length; _i++) {
|
|
859
|
-
transaction = _b[_i];
|
|
860
|
-
tempAirGapTransactionFrom = [];
|
|
861
|
-
tempAirGapTransactionTo = [];
|
|
862
|
-
tempAirGapTransactionIsInbound = true;
|
|
863
|
-
amount = new bignumber_1.default(0);
|
|
864
|
-
for (_c = 0, _d = transaction.vin; _c < _d.length; _c++) {
|
|
865
|
-
vin = _d[_c];
|
|
866
|
-
if (this.containsSome(vin.addresses, ourAddresses)) {
|
|
867
|
-
tempAirGapTransactionIsInbound = false;
|
|
868
|
-
}
|
|
869
|
-
tempAirGapTransactionFrom.push.apply(tempAirGapTransactionFrom, vin.addresses);
|
|
870
|
-
amount = amount.plus(vin.value);
|
|
871
|
-
}
|
|
872
|
-
for (_e = 0, _f = transaction.vout; _e < _f.length; _e++) {
|
|
873
|
-
vout = _f[_e];
|
|
874
|
-
if (vout.addresses) {
|
|
875
|
-
tempAirGapTransactionTo.push.apply(tempAirGapTransactionTo, vout.addresses);
|
|
876
|
-
// If receiving address is our address, and transaction is outbound => our change
|
|
877
|
-
if (this.containsSome(vout.addresses, ourAddresses) && !tempAirGapTransactionIsInbound) {
|
|
878
|
-
// remove only if related to this address
|
|
879
|
-
amount = amount.minus(vout.value);
|
|
880
|
-
}
|
|
881
|
-
// If receiving address is not ours, and transaction isbound => senders change
|
|
882
|
-
if (!this.containsSome(vout.addresses, ourAddresses) && tempAirGapTransactionIsInbound) {
|
|
883
|
-
amount = amount.minus(vout.value);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
// deduct fee from amount
|
|
888
|
-
amount = amount.minus(transaction.fees);
|
|
889
|
-
airGapTransaction = {
|
|
890
|
-
hash: transaction.txid,
|
|
891
|
-
from: tempAirGapTransactionFrom,
|
|
892
|
-
to: tempAirGapTransactionTo,
|
|
893
|
-
isInbound: tempAirGapTransactionIsInbound,
|
|
894
|
-
amount: amount.toString(10),
|
|
895
|
-
fee: new bignumber_1.default(transaction.fees).toString(10),
|
|
896
|
-
blockHeight: transaction.blockHeight.toString(),
|
|
897
|
-
protocolIdentifier: this.identifier,
|
|
898
|
-
network: this.options.network,
|
|
899
|
-
timestamp: transaction.blockTime
|
|
900
|
-
};
|
|
901
|
-
airGapTransactions.push(airGapTransaction);
|
|
902
|
-
}
|
|
596
|
+
for (const vout of transaction.vout) {
|
|
597
|
+
if (vout.addresses) {
|
|
598
|
+
tempAirGapTransactionTo.push(...vout.addresses);
|
|
599
|
+
// If receiving address is our address, and transaction is outbound => our change
|
|
600
|
+
if (this.containsSome(vout.addresses, addresses) && !tempAirGapTransactionIsInbound) {
|
|
601
|
+
// remove only if related to this address
|
|
602
|
+
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
903
603
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
page: cursor ? cursor.page + 1 : 2
|
|
908
|
-
}
|
|
909
|
-
}];
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
});
|
|
913
|
-
};
|
|
914
|
-
BitcoinProtocol.prototype.getTransactionsFromPublicKey = function (publicKey, limit, cursor) {
|
|
915
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
916
|
-
var address;
|
|
917
|
-
return __generator(this, function (_a) {
|
|
918
|
-
switch (_a.label) {
|
|
919
|
-
case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)];
|
|
920
|
-
case 1:
|
|
921
|
-
address = _a.sent();
|
|
922
|
-
return [2 /*return*/, this.getTransactionsFromAddresses([address.address], limit, cursor)];
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
});
|
|
926
|
-
};
|
|
927
|
-
BitcoinProtocol.prototype.getTransactionsFromAddresses = function (addresses, limit, cursor) {
|
|
928
|
-
var _a;
|
|
929
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
930
|
-
var airGapTransactions, page, url, data, _i, _b, transaction, tempAirGapTransactionFrom, tempAirGapTransactionTo, tempAirGapTransactionIsInbound, amount, _c, _d, vin, _e, _f, vout, airGapTransaction;
|
|
931
|
-
return __generator(this, function (_g) {
|
|
932
|
-
switch (_g.label) {
|
|
933
|
-
case 0:
|
|
934
|
-
airGapTransactions = [];
|
|
935
|
-
page = (_a = cursor === null || cursor === void 0 ? void 0 : cursor.page) !== null && _a !== void 0 ? _a : 1;
|
|
936
|
-
url = "".concat(this.options.network.extras.indexerApi, "/api/v2/address/").concat(addresses[0], "?page=").concat(page, "&pageSize=").concat(limit, "&details=txs");
|
|
937
|
-
return [4 /*yield*/, index_1.default.get(url, {
|
|
938
|
-
responseType: 'json'
|
|
939
|
-
})];
|
|
940
|
-
case 1:
|
|
941
|
-
data = (_g.sent()).data;
|
|
942
|
-
if (data.page == page) {
|
|
943
|
-
for (_i = 0, _b = data.transactions || []; _i < _b.length; _i++) {
|
|
944
|
-
transaction = _b[_i];
|
|
945
|
-
tempAirGapTransactionFrom = [];
|
|
946
|
-
tempAirGapTransactionTo = [];
|
|
947
|
-
tempAirGapTransactionIsInbound = true;
|
|
948
|
-
amount = new bignumber_1.default(0);
|
|
949
|
-
for (_c = 0, _d = transaction.vin; _c < _d.length; _c++) {
|
|
950
|
-
vin = _d[_c];
|
|
951
|
-
if (vin.addresses && this.containsSome(vin.addresses, addresses)) {
|
|
952
|
-
tempAirGapTransactionIsInbound = false;
|
|
953
|
-
}
|
|
954
|
-
tempAirGapTransactionFrom.push.apply(tempAirGapTransactionFrom, vin.addresses);
|
|
955
|
-
amount = vin.value ? amount.plus(vin.value) : amount;
|
|
956
|
-
}
|
|
957
|
-
for (_e = 0, _f = transaction.vout; _e < _f.length; _e++) {
|
|
958
|
-
vout = _f[_e];
|
|
959
|
-
if (vout.addresses) {
|
|
960
|
-
tempAirGapTransactionTo.push.apply(tempAirGapTransactionTo, vout.addresses);
|
|
961
|
-
// If receiving address is our address, and transaction is outbound => our change
|
|
962
|
-
if (this.containsSome(vout.addresses, addresses) && !tempAirGapTransactionIsInbound) {
|
|
963
|
-
// remove only if related to this address
|
|
964
|
-
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
965
|
-
}
|
|
966
|
-
// If receiving address is not ours, and transaction isbound => senders change
|
|
967
|
-
if (!this.containsSome(vout.addresses, addresses) && tempAirGapTransactionIsInbound) {
|
|
968
|
-
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
// deduct fee from amount
|
|
973
|
-
amount = amount.minus(new bignumber_1.default(transaction.fees));
|
|
974
|
-
airGapTransaction = {
|
|
975
|
-
hash: transaction.txid,
|
|
976
|
-
from: tempAirGapTransactionFrom,
|
|
977
|
-
to: tempAirGapTransactionTo,
|
|
978
|
-
isInbound: tempAirGapTransactionIsInbound,
|
|
979
|
-
amount: amount.toString(10),
|
|
980
|
-
fee: new bignumber_1.default(transaction.fees).toString(10),
|
|
981
|
-
blockHeight: transaction.blockHeight.toString(),
|
|
982
|
-
protocolIdentifier: this.identifier,
|
|
983
|
-
network: this.options.network,
|
|
984
|
-
timestamp: transaction.blockTime
|
|
985
|
-
};
|
|
986
|
-
airGapTransactions.push(airGapTransaction);
|
|
987
|
-
}
|
|
604
|
+
// If receiving address is not ours, and transaction isbound => senders change
|
|
605
|
+
if (!this.containsSome(vout.addresses, addresses) && tempAirGapTransactionIsInbound) {
|
|
606
|
+
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
988
607
|
}
|
|
989
|
-
|
|
990
|
-
transactions: airGapTransactions,
|
|
991
|
-
cursor: {
|
|
992
|
-
page: cursor ? cursor.page + 1 : 2
|
|
993
|
-
}
|
|
994
|
-
}];
|
|
608
|
+
}
|
|
995
609
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
610
|
+
// deduct fee from amount
|
|
611
|
+
amount = amount.minus(new bignumber_1.default(transaction.fees));
|
|
612
|
+
const airGapTransaction = {
|
|
613
|
+
hash: transaction.txid,
|
|
614
|
+
from: tempAirGapTransactionFrom,
|
|
615
|
+
to: tempAirGapTransactionTo,
|
|
616
|
+
isInbound: tempAirGapTransactionIsInbound,
|
|
617
|
+
amount: amount.toString(10),
|
|
618
|
+
fee: new bignumber_1.default(transaction.fees).toString(10),
|
|
619
|
+
blockHeight: transaction.blockHeight.toString(),
|
|
620
|
+
protocolIdentifier: this.identifier,
|
|
621
|
+
network: this.options.network,
|
|
622
|
+
timestamp: transaction.blockTime
|
|
623
|
+
};
|
|
624
|
+
airGapTransactions.push(airGapTransaction);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
transactions: airGapTransactions,
|
|
629
|
+
cursor: {
|
|
630
|
+
page: cursor ? cursor.page + 1 : 2
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
containsSome(needles, haystack) {
|
|
635
|
+
for (const needle of needles) {
|
|
1002
636
|
if (haystack.indexOf(needle) > -1) {
|
|
1003
637
|
return true;
|
|
1004
638
|
}
|
|
1005
639
|
}
|
|
1006
640
|
return false;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
return
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
return
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
childPrivateKey = this.options.config.bitcoinJSLib.HDNode.fromBase58(keypair.privateKey, this.options.network.extras.network)
|
|
1039
|
-
.derive(0)
|
|
1040
|
-
.derive(0)
|
|
1041
|
-
.keyPair.d.toBuffer(32);
|
|
1042
|
-
return [2 /*return*/, this.cryptoClient.decryptAsymmetric(message, { publicKey: '', privateKey: childPrivateKey.toString('hex') })];
|
|
1043
|
-
});
|
|
1044
|
-
});
|
|
1045
|
-
};
|
|
1046
|
-
BitcoinProtocol.prototype.encryptAES = function (message, privateKey) {
|
|
1047
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
1048
|
-
return __generator(this, function (_a) {
|
|
1049
|
-
return [2 /*return*/, this.cryptoClient.encryptAES(message, privateKey)];
|
|
1050
|
-
});
|
|
1051
|
-
});
|
|
1052
|
-
};
|
|
1053
|
-
BitcoinProtocol.prototype.decryptAES = function (message, privateKey) {
|
|
1054
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
1055
|
-
return __generator(this, function (_a) {
|
|
1056
|
-
return [2 /*return*/, this.cryptoClient.decryptAES(message, privateKey)];
|
|
1057
|
-
});
|
|
1058
|
-
});
|
|
1059
|
-
};
|
|
1060
|
-
BitcoinProtocol.prototype.getTransactionStatuses = function (transactionHashes) {
|
|
1061
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
1062
|
-
return __generator(this, function (_a) {
|
|
1063
|
-
return [2 /*return*/, Promise.reject('Transaction status not implemented')];
|
|
1064
|
-
});
|
|
1065
|
-
});
|
|
1066
|
-
};
|
|
1067
|
-
return BitcoinProtocol;
|
|
1068
|
-
}());
|
|
641
|
+
}
|
|
642
|
+
async signMessage(message, keypair) {
|
|
643
|
+
return this.cryptoClient.signMessage(message, keypair);
|
|
644
|
+
}
|
|
645
|
+
async verifyMessage(message, signature, publicKey) {
|
|
646
|
+
return this.cryptoClient.verifyMessage(message, signature, publicKey);
|
|
647
|
+
}
|
|
648
|
+
async encryptAsymmetric(message, publicKey) {
|
|
649
|
+
const childPublicKey = this.options.config.bitcoinJSLib.HDNode.fromBase58(publicKey, this.options.network.extras.network)
|
|
650
|
+
.derive(0)
|
|
651
|
+
.derive(0)
|
|
652
|
+
.getPublicKeyBuffer();
|
|
653
|
+
return this.cryptoClient.encryptAsymmetric(message, childPublicKey);
|
|
654
|
+
}
|
|
655
|
+
async decryptAsymmetric(message, keypair) {
|
|
656
|
+
const childPrivateKey = this.options.config.bitcoinJSLib.HDNode.fromBase58(keypair.privateKey, this.options.network.extras.network)
|
|
657
|
+
.derive(0)
|
|
658
|
+
.derive(0)
|
|
659
|
+
.keyPair.d.toBuffer(32);
|
|
660
|
+
return this.cryptoClient.decryptAsymmetric(message, { publicKey: '', privateKey: childPrivateKey.toString('hex') });
|
|
661
|
+
}
|
|
662
|
+
async encryptAES(message, privateKey) {
|
|
663
|
+
return this.cryptoClient.encryptAES(message, privateKey);
|
|
664
|
+
}
|
|
665
|
+
async decryptAES(message, privateKey) {
|
|
666
|
+
return this.cryptoClient.decryptAES(message, privateKey);
|
|
667
|
+
}
|
|
668
|
+
async getTransactionStatuses(transactionHashes) {
|
|
669
|
+
return Promise.reject('Transaction status not implemented');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
1069
672
|
exports.BitcoinProtocol = BitcoinProtocol;
|
|
1070
673
|
//# sourceMappingURL=BitcoinProtocol.js.map
|