@airgap/bitcoin 0.13.45-beta.2 → 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
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
3
|
if (k2 === undefined) k2 = k;
|
|
15
4
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -26,85 +15,49 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
26
15
|
}) : function(o, v) {
|
|
27
16
|
o["default"] = v;
|
|
28
17
|
});
|
|
29
|
-
var __importStar = (this && this.__importStar) || function (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
47
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
48
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
49
|
-
function step(op) {
|
|
50
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
51
|
-
while (_) try {
|
|
52
|
-
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;
|
|
53
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
54
|
-
switch (op[0]) {
|
|
55
|
-
case 0: case 1: t = op; break;
|
|
56
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
57
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
58
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
59
|
-
default:
|
|
60
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
61
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
62
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
63
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
64
|
-
if (t[2]) _.ops.pop();
|
|
65
|
-
_.trys.pop(); continue;
|
|
66
|
-
}
|
|
67
|
-
op = body.call(thisArg, _);
|
|
68
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
69
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
73
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
74
|
-
if (ar || !(i in from)) {
|
|
75
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
76
|
-
ar[i] = from[i];
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
80
|
-
};
|
|
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
|
+
})();
|
|
81
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
82
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
83
37
|
};
|
|
84
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
85
|
-
exports.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
39
|
+
exports.BITCOIN_MAINNET_PROTOCOL_NETWORK = exports.BitcoinProtocolImpl = void 0;
|
|
40
|
+
exports.createBitcoinProtocol = createBitcoinProtocol;
|
|
41
|
+
exports.createBitcoinProtocolOptions = createBitcoinProtocolOptions;
|
|
42
|
+
const coinlib_core_1 = require("@airgap/coinlib-core");
|
|
43
|
+
const index_1 = __importDefault(require("@airgap/coinlib-core/dependencies/src/axios-0.19.0/index"));
|
|
44
|
+
const BigInteger = __importStar(require("@airgap/coinlib-core/dependencies/src/bigi-1.4.2"));
|
|
45
|
+
const bignumber_1 = __importDefault(require("@airgap/coinlib-core/dependencies/src/bignumber.js-9.0.0/bignumber"));
|
|
46
|
+
const bitcoinMessage = __importStar(require("@airgap/coinlib-core/dependencies/src/bitcoinjs-message-2.1.1/index"));
|
|
47
|
+
const BitGo = __importStar(require("@airgap/coinlib-core/dependencies/src/bitgo-utxo-lib-5d91049fd7a988382df81c8260e244ee56d57aac/src/index"));
|
|
48
|
+
const errors_1 = require("@airgap/coinlib-core/errors");
|
|
49
|
+
const crypto_1 = require("@airgap/crypto");
|
|
50
|
+
const module_kit_1 = require("@airgap/module-kit");
|
|
51
|
+
const BitcoinAddress_1 = require("../data/BitcoinAddress");
|
|
52
|
+
const common_1 = require("../utils/common");
|
|
53
|
+
const key_1 = require("../utils/key");
|
|
54
|
+
const network_1 = require("../utils/network");
|
|
55
|
+
const signature_1 = require("../utils/signature");
|
|
56
|
+
const BitcoinCryptoClient_1 = require("./BitcoinCryptoClient");
|
|
101
57
|
// Implementation
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (options === void 0) { options = {}; }
|
|
106
|
-
if (bitcoinJS === void 0) { bitcoinJS = BitGo; }
|
|
107
|
-
if (bitcoinJSMessage === void 0) { bitcoinJSMessage = bitcoinMessage; }
|
|
58
|
+
const DUST_AMOUNT = 50;
|
|
59
|
+
class BitcoinProtocolImpl {
|
|
60
|
+
constructor(options = {}, keyConfiguration, bitcoinJS = BitGo, bitcoinJSMessage = bitcoinMessage) {
|
|
108
61
|
this._isBitcoinProtocol = true;
|
|
109
62
|
// Common
|
|
110
63
|
this.units = {
|
|
@@ -135,7 +88,7 @@ var BitcoinProtocolImpl = /** @class */ (function () {
|
|
|
135
88
|
defaults: this.feeDefaults
|
|
136
89
|
},
|
|
137
90
|
account: {
|
|
138
|
-
standardDerivationPath:
|
|
91
|
+
standardDerivationPath: `m/44'/0'/0'`,
|
|
139
92
|
address: {
|
|
140
93
|
isCaseSensitive: true,
|
|
141
94
|
placeholder: '1ABC...',
|
|
@@ -156,7 +109,7 @@ var BitcoinProtocolImpl = /** @class */ (function () {
|
|
|
156
109
|
}
|
|
157
110
|
};
|
|
158
111
|
this.cryptoClient = new BitcoinCryptoClient_1.BitcoinCryptoClient(this, this.bitcoinJS);
|
|
159
|
-
this.keyConfiguration = keyConfiguration
|
|
112
|
+
this.keyConfiguration = keyConfiguration ?? {
|
|
160
113
|
xpriv: {
|
|
161
114
|
type: 'xprv'
|
|
162
115
|
},
|
|
@@ -165,1134 +118,748 @@ var BitcoinProtocolImpl = /** @class */ (function () {
|
|
|
165
118
|
}
|
|
166
119
|
};
|
|
167
120
|
}
|
|
168
|
-
|
|
169
|
-
return
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
121
|
+
async getMetadata() {
|
|
122
|
+
return this.metadata;
|
|
123
|
+
}
|
|
124
|
+
async getAddressFromPublicKey(publicKey) {
|
|
125
|
+
switch (publicKey.type) {
|
|
126
|
+
case 'pub':
|
|
127
|
+
return this.getAddressFromNonExtendedPublicKey(publicKey);
|
|
128
|
+
case 'xpub':
|
|
129
|
+
return this.getAddressFromExtendedPublicKey(publicKey);
|
|
130
|
+
default:
|
|
131
|
+
(0, coinlib_core_1.assertNever)(publicKey);
|
|
132
|
+
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Public key type is not supported.');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async getAddressFromNonExtendedPublicKey(publicKey) {
|
|
136
|
+
const hexPublicKey = (0, key_1.convertPublicKey)(publicKey, 'hex');
|
|
137
|
+
const keyPair = this.bitcoinJS.lib.ECPair.fromPublicKeyBuffer(Buffer.from(hexPublicKey.value, 'hex'), this.bitcoinJS.config.network);
|
|
138
|
+
return BitcoinAddress_1.BitcoinAddress.fromECPair(keyPair).asString();
|
|
139
|
+
}
|
|
140
|
+
async getAddressFromExtendedPublicKey(extendedPublicKey) {
|
|
141
|
+
const encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
142
|
+
const node = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedPublicKey.value, this.bitcoinJS.config.network);
|
|
143
|
+
return BitcoinAddress_1.BitcoinAddress.fromHDNode(node).asString();
|
|
144
|
+
}
|
|
145
|
+
async deriveFromExtendedPublicKey(extendedPublicKey, visibilityIndex, addressIndex) {
|
|
146
|
+
const encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
147
|
+
const childPublicKey = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedPublicKey.value, this.bitcoinJS.config.network)
|
|
148
|
+
.derive(visibilityIndex)
|
|
149
|
+
.derive(addressIndex)
|
|
150
|
+
.getPublicKeyBuffer();
|
|
151
|
+
return (0, module_kit_1.newPublicKey)(childPublicKey.toString('hex'), 'hex');
|
|
152
|
+
}
|
|
153
|
+
async getDetailsFromTransaction(transaction, _publicKey) {
|
|
154
|
+
switch (transaction.type) {
|
|
155
|
+
case 'signed':
|
|
156
|
+
return this.getDetailsFromSignedTransaction(transaction);
|
|
157
|
+
case 'unsigned':
|
|
158
|
+
return this.getDetailsFromUnsignedTransaction(transaction, _publicKey);
|
|
159
|
+
default:
|
|
160
|
+
(0, coinlib_core_1.assertNever)(transaction);
|
|
161
|
+
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Unsupported transaction type.');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async getDetailsFromSignedTransaction(transaction) {
|
|
165
|
+
const tx = {
|
|
166
|
+
from: transaction.from,
|
|
167
|
+
to: [],
|
|
168
|
+
isInbound: false,
|
|
169
|
+
amount: (0, module_kit_1.newAmount)(transaction.amount, 'blockchain'),
|
|
170
|
+
fee: (0, module_kit_1.newAmount)(transaction.fee, 'blockchain'),
|
|
171
|
+
network: this.options.network
|
|
172
|
+
};
|
|
173
|
+
const bitcoinTx = this.bitcoinJS.lib.Transaction.fromHex(transaction.transaction);
|
|
174
|
+
bitcoinTx.outs.forEach((output) => {
|
|
175
|
+
const address = this.bitcoinJS.lib.address.fromOutputScript(output.script, this.bitcoinJS.config.network);
|
|
176
|
+
// only works if one output is target and rest is change, but this way we can filter out change addresses
|
|
177
|
+
// if (new BigNumber(output.value).isEqualTo(transaction.amount)) {
|
|
178
|
+
tx.to.push(address);
|
|
179
|
+
// }
|
|
180
|
+
});
|
|
181
|
+
return [tx];
|
|
182
|
+
}
|
|
183
|
+
async getDetailsFromUnsignedTransaction(transaction, publickey) {
|
|
184
|
+
let fee = new bignumber_1.default(0);
|
|
185
|
+
for (const txIn of transaction.ins) {
|
|
186
|
+
fee = fee.plus(new bignumber_1.default(txIn.value));
|
|
187
|
+
}
|
|
188
|
+
for (const txOut of transaction.outs) {
|
|
189
|
+
fee = fee.minus(new bignumber_1.default(txOut.value));
|
|
190
|
+
}
|
|
191
|
+
const uiAlerts = [];
|
|
192
|
+
const changeAddressDatas = await Promise.all(transaction.outs.map(async (obj) => {
|
|
193
|
+
let isChangeAddress = obj.isChange ? obj.isChange : false;
|
|
194
|
+
let isOwned = false;
|
|
195
|
+
let addressIndex = 0;
|
|
196
|
+
const address = obj.recipient;
|
|
197
|
+
const amount = obj.value;
|
|
198
|
+
let ourGeneratedAddress;
|
|
199
|
+
if (isChangeAddress) {
|
|
200
|
+
const splitPath = obj.derivationPath.split('/');
|
|
201
|
+
addressIndex = +splitPath[splitPath.length - 1];
|
|
202
|
+
if (publickey.type === 'xpub') {
|
|
203
|
+
const ourPublickey = await this.deriveFromExtendedPublicKey(publickey, 1, addressIndex);
|
|
204
|
+
ourGeneratedAddress = await this.getAddressFromPublicKey(ourPublickey);
|
|
186
205
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
});
|
|
190
|
-
};
|
|
191
|
-
BitcoinProtocolImpl.prototype.getAddressFromNonExtendedPublicKey = function (publicKey) {
|
|
192
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
193
|
-
var hexPublicKey, keyPair;
|
|
194
|
-
return __generator(this, function (_a) {
|
|
195
|
-
hexPublicKey = (0, key_1.convertPublicKey)(publicKey, 'hex');
|
|
196
|
-
keyPair = this.bitcoinJS.lib.ECPair.fromPublicKeyBuffer(Buffer.from(hexPublicKey.value, 'hex'), this.bitcoinJS.config.network);
|
|
197
|
-
return [2 /*return*/, BitcoinAddress_1.BitcoinAddress.fromECPair(keyPair).asString()];
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
};
|
|
201
|
-
BitcoinProtocolImpl.prototype.getAddressFromExtendedPublicKey = function (extendedPublicKey) {
|
|
202
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
203
|
-
var encodedExtendedPublicKey, node;
|
|
204
|
-
return __generator(this, function (_a) {
|
|
205
|
-
encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
206
|
-
node = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedPublicKey.value, this.bitcoinJS.config.network);
|
|
207
|
-
return [2 /*return*/, BitcoinAddress_1.BitcoinAddress.fromHDNode(node).asString()];
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
};
|
|
211
|
-
BitcoinProtocolImpl.prototype.deriveFromExtendedPublicKey = function (extendedPublicKey, visibilityIndex, addressIndex) {
|
|
212
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
213
|
-
var encodedExtendedPublicKey, childPublicKey;
|
|
214
|
-
return __generator(this, function (_a) {
|
|
215
|
-
encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
216
|
-
childPublicKey = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedPublicKey.value, this.bitcoinJS.config.network)
|
|
217
|
-
.derive(visibilityIndex)
|
|
218
|
-
.derive(addressIndex)
|
|
219
|
-
.getPublicKeyBuffer();
|
|
220
|
-
return [2 /*return*/, (0, module_kit_1.newPublicKey)(childPublicKey.toString('hex'), 'hex')];
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
};
|
|
224
|
-
BitcoinProtocolImpl.prototype.getDetailsFromTransaction = function (transaction, _publicKey) {
|
|
225
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
226
|
-
return __generator(this, function (_a) {
|
|
227
|
-
switch (transaction.type) {
|
|
228
|
-
case 'signed':
|
|
229
|
-
return [2 /*return*/, this.getDetailsFromSignedTransaction(transaction)];
|
|
230
|
-
case 'unsigned':
|
|
231
|
-
return [2 /*return*/, this.getDetailsFromUnsignedTransaction(transaction, _publicKey)];
|
|
232
|
-
default:
|
|
233
|
-
(0, coinlib_core_1.assertNever)(transaction);
|
|
234
|
-
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Unsupported transaction type.');
|
|
206
|
+
else {
|
|
207
|
+
ourGeneratedAddress = await this.getAddressFromNonExtendedPublicKey(publickey);
|
|
235
208
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
to: [],
|
|
248
|
-
isInbound: false,
|
|
249
|
-
amount: (0, module_kit_1.newAmount)(transaction.amount, 'blockchain'),
|
|
250
|
-
fee: (0, module_kit_1.newAmount)(transaction.fee, 'blockchain'),
|
|
251
|
-
network: this.options.network
|
|
252
|
-
};
|
|
253
|
-
bitcoinTx = this.bitcoinJS.lib.Transaction.fromHex(transaction.transaction);
|
|
254
|
-
bitcoinTx.outs.forEach(function (output) {
|
|
255
|
-
var address = _this.bitcoinJS.lib.address.fromOutputScript(output.script, _this.bitcoinJS.config.network);
|
|
256
|
-
// only works if one output is target and rest is change, but this way we can filter out change addresses
|
|
257
|
-
// if (new BigNumber(output.value).isEqualTo(transaction.amount)) {
|
|
258
|
-
tx.to.push(address);
|
|
259
|
-
// }
|
|
260
|
-
});
|
|
261
|
-
return [2 /*return*/, [tx]];
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
};
|
|
265
|
-
BitcoinProtocolImpl.prototype.getDetailsFromUnsignedTransaction = function (transaction, publickey) {
|
|
266
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
267
|
-
var fee, _i, _a, txIn, _b, _c, txOut, uiAlerts, changeAddressDatas, changeAddressInfo;
|
|
268
|
-
var _this = this;
|
|
269
|
-
return __generator(this, function (_d) {
|
|
270
|
-
switch (_d.label) {
|
|
271
|
-
case 0:
|
|
272
|
-
fee = new bignumber_1.default(0);
|
|
273
|
-
for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) {
|
|
274
|
-
txIn = _a[_i];
|
|
275
|
-
fee = fee.plus(new bignumber_1.default(txIn.value));
|
|
276
|
-
}
|
|
277
|
-
for (_b = 0, _c = transaction.outs; _b < _c.length; _b++) {
|
|
278
|
-
txOut = _c[_b];
|
|
279
|
-
fee = fee.minus(new bignumber_1.default(txOut.value));
|
|
209
|
+
if (ourGeneratedAddress === address) {
|
|
210
|
+
isOwned = true;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
for (let x = 0; x < 1000; x++) {
|
|
214
|
+
const ourPublickey = await this.deriveFromExtendedPublicKey(publickey, 1, x);
|
|
215
|
+
ourGeneratedAddress = await this.getAddressFromPublicKey(ourPublickey);
|
|
216
|
+
if (ourGeneratedAddress === address) {
|
|
217
|
+
isOwned = true;
|
|
218
|
+
addressIndex = x;
|
|
219
|
+
break;
|
|
280
220
|
}
|
|
281
|
-
|
|
282
|
-
return [4 /*yield*/, Promise.all(transaction.outs.map(function (obj) { return __awaiter(_this, void 0, void 0, function () {
|
|
283
|
-
var isChangeAddress, isOwned, addressIndex, address, amount, ourGeneratedAddress, splitPath, ourPublickey, x, ourPublickey;
|
|
284
|
-
return __generator(this, function (_a) {
|
|
285
|
-
switch (_a.label) {
|
|
286
|
-
case 0:
|
|
287
|
-
isChangeAddress = obj.isChange ? obj.isChange : false;
|
|
288
|
-
isOwned = false;
|
|
289
|
-
addressIndex = 0;
|
|
290
|
-
address = obj.recipient;
|
|
291
|
-
amount = obj.value;
|
|
292
|
-
if (!isChangeAddress) return [3 /*break*/, 11];
|
|
293
|
-
splitPath = obj.derivationPath.split('/');
|
|
294
|
-
addressIndex = +splitPath[splitPath.length - 1];
|
|
295
|
-
if (!(publickey.type === 'xpub')) return [3 /*break*/, 3];
|
|
296
|
-
return [4 /*yield*/, this.deriveFromExtendedPublicKey(publickey, 1, addressIndex)];
|
|
297
|
-
case 1:
|
|
298
|
-
ourPublickey = _a.sent();
|
|
299
|
-
return [4 /*yield*/, this.getAddressFromPublicKey(ourPublickey)];
|
|
300
|
-
case 2:
|
|
301
|
-
ourGeneratedAddress = _a.sent();
|
|
302
|
-
return [3 /*break*/, 5];
|
|
303
|
-
case 3: return [4 /*yield*/, this.getAddressFromNonExtendedPublicKey(publickey)];
|
|
304
|
-
case 4:
|
|
305
|
-
ourGeneratedAddress = _a.sent();
|
|
306
|
-
_a.label = 5;
|
|
307
|
-
case 5:
|
|
308
|
-
if (!(ourGeneratedAddress === address)) return [3 /*break*/, 6];
|
|
309
|
-
isOwned = true;
|
|
310
|
-
return [3 /*break*/, 11];
|
|
311
|
-
case 6:
|
|
312
|
-
x = 0;
|
|
313
|
-
_a.label = 7;
|
|
314
|
-
case 7:
|
|
315
|
-
if (!(x < 1000)) return [3 /*break*/, 11];
|
|
316
|
-
return [4 /*yield*/, this.deriveFromExtendedPublicKey(publickey, 1, x)];
|
|
317
|
-
case 8:
|
|
318
|
-
ourPublickey = _a.sent();
|
|
319
|
-
return [4 /*yield*/, this.getAddressFromPublicKey(ourPublickey)];
|
|
320
|
-
case 9:
|
|
321
|
-
ourGeneratedAddress = _a.sent();
|
|
322
|
-
if (ourGeneratedAddress === address) {
|
|
323
|
-
isOwned = true;
|
|
324
|
-
addressIndex = x;
|
|
325
|
-
return [3 /*break*/, 11];
|
|
326
|
-
}
|
|
327
|
-
_a.label = 10;
|
|
328
|
-
case 10:
|
|
329
|
-
x++;
|
|
330
|
-
return [3 /*break*/, 7];
|
|
331
|
-
case 11:
|
|
332
|
-
if (isChangeAddress && isOwned) {
|
|
333
|
-
uiAlerts.push({
|
|
334
|
-
type: 'success',
|
|
335
|
-
title: {
|
|
336
|
-
type: 'plain',
|
|
337
|
-
value: ''
|
|
338
|
-
},
|
|
339
|
-
description: {
|
|
340
|
-
type: 'plain',
|
|
341
|
-
value: 'Note: your change address has been verified'
|
|
342
|
-
},
|
|
343
|
-
icon: undefined,
|
|
344
|
-
actions: undefined
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
else if (isChangeAddress && !isOwned) {
|
|
348
|
-
uiAlerts.push({
|
|
349
|
-
type: 'warning',
|
|
350
|
-
title: {
|
|
351
|
-
type: 'plain',
|
|
352
|
-
value: ''
|
|
353
|
-
},
|
|
354
|
-
description: {
|
|
355
|
-
type: 'plain',
|
|
356
|
-
value: 'Note: your change address has not been verified'
|
|
357
|
-
},
|
|
358
|
-
icon: undefined,
|
|
359
|
-
actions: undefined
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
return [2 /*return*/, [
|
|
363
|
-
address,
|
|
364
|
-
{
|
|
365
|
-
isChangeAddress: isChangeAddress,
|
|
366
|
-
isOwned: isOwned,
|
|
367
|
-
path: addressIndex === 0 ? '' : "m/84'/0'/0'/1/".concat(addressIndex),
|
|
368
|
-
amount: amount
|
|
369
|
-
}
|
|
370
|
-
]];
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
}); }))];
|
|
374
|
-
case 1:
|
|
375
|
-
changeAddressDatas = _d.sent();
|
|
376
|
-
changeAddressInfo = {};
|
|
377
|
-
changeAddressDatas.forEach(function (changeAddressData) {
|
|
378
|
-
changeAddressInfo[changeAddressData[0]] = changeAddressData[1];
|
|
379
|
-
});
|
|
380
|
-
return [2 /*return*/, [
|
|
381
|
-
{
|
|
382
|
-
from: transaction.ins.map(function (obj) { return obj.address; }),
|
|
383
|
-
to: transaction.outs.map(function (obj) { return obj.recipient; }),
|
|
384
|
-
isInbound: false,
|
|
385
|
-
amount: (0, module_kit_1.newAmount)(transaction.outs.map(function (obj) { return new bignumber_1.default(obj.value); }).reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); }), 'blockchain'),
|
|
386
|
-
fee: (0, module_kit_1.newAmount)(fee, 'blockchain'),
|
|
387
|
-
network: this.options.network,
|
|
388
|
-
changeAddressInfo: changeAddressInfo,
|
|
389
|
-
uiAlerts: uiAlerts
|
|
390
|
-
}
|
|
391
|
-
]];
|
|
221
|
+
}
|
|
392
222
|
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
223
|
+
}
|
|
224
|
+
if (isChangeAddress && isOwned) {
|
|
225
|
+
uiAlerts.push({
|
|
226
|
+
type: 'success',
|
|
227
|
+
title: {
|
|
228
|
+
type: 'plain',
|
|
229
|
+
value: ''
|
|
230
|
+
},
|
|
231
|
+
description: {
|
|
232
|
+
type: 'plain',
|
|
233
|
+
value: 'Note: your change address has been verified'
|
|
234
|
+
},
|
|
235
|
+
icon: undefined,
|
|
236
|
+
actions: undefined
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else if (isChangeAddress && !isOwned) {
|
|
240
|
+
uiAlerts.push({
|
|
241
|
+
type: 'warning',
|
|
242
|
+
title: {
|
|
243
|
+
type: 'plain',
|
|
244
|
+
value: ''
|
|
245
|
+
},
|
|
246
|
+
description: {
|
|
247
|
+
type: 'plain',
|
|
248
|
+
value: 'Note: your change address has not been verified'
|
|
249
|
+
},
|
|
250
|
+
icon: undefined,
|
|
251
|
+
actions: undefined
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return [
|
|
255
|
+
address,
|
|
256
|
+
{
|
|
257
|
+
isChangeAddress: isChangeAddress,
|
|
258
|
+
isOwned: isOwned,
|
|
259
|
+
path: addressIndex === 0 ? '' : `m/84'/0'/0'/1/${addressIndex}`,
|
|
260
|
+
amount
|
|
422
261
|
}
|
|
423
|
-
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
262
|
+
];
|
|
263
|
+
}));
|
|
264
|
+
const changeAddressInfo = {};
|
|
265
|
+
changeAddressDatas.forEach((changeAddressData) => {
|
|
266
|
+
changeAddressInfo[changeAddressData[0]] = changeAddressData[1];
|
|
267
|
+
});
|
|
268
|
+
return [
|
|
269
|
+
{
|
|
270
|
+
from: transaction.ins.map((obj) => obj.address),
|
|
271
|
+
to: transaction.outs.map((obj) => obj.recipient),
|
|
272
|
+
isInbound: false,
|
|
273
|
+
amount: (0, module_kit_1.newAmount)(transaction.outs.map((obj) => new bignumber_1.default(obj.value)).reduce((accumulator, currentValue) => accumulator.plus(currentValue)), 'blockchain'),
|
|
274
|
+
fee: (0, module_kit_1.newAmount)(fee, 'blockchain'),
|
|
275
|
+
network: this.options.network,
|
|
276
|
+
changeAddressInfo,
|
|
277
|
+
uiAlerts
|
|
278
|
+
}
|
|
279
|
+
];
|
|
280
|
+
}
|
|
281
|
+
async verifyMessageWithPublicKey(message, signature, publicKey) {
|
|
282
|
+
const encodedSignature = (0, signature_1.convertSignature)(signature, 'encoded');
|
|
283
|
+
return this.cryptoClient.verifyMessage(message, encodedSignature.value, publicKey.value);
|
|
284
|
+
}
|
|
285
|
+
async encryptAsymmetricWithPublicKey(payload, publicKey) {
|
|
286
|
+
const nonExtendedPublicKey = publicKey.type === 'pub' ? publicKey : await this.deriveFromExtendedPublicKey(publicKey, 0, 0);
|
|
287
|
+
const hexNonExtendedPublicKey = (0, key_1.convertPublicKey)(nonExtendedPublicKey, 'hex');
|
|
288
|
+
return this.cryptoClient.encryptAsymmetric(payload, hexNonExtendedPublicKey.value);
|
|
289
|
+
}
|
|
290
|
+
async getCryptoConfiguration() {
|
|
291
|
+
return this.cryptoConfiguration;
|
|
292
|
+
}
|
|
293
|
+
async getKeyPairFromDerivative(derivative) {
|
|
294
|
+
const node = this.derivativeToBip32Node(derivative);
|
|
295
|
+
return {
|
|
296
|
+
secretKey: (0, module_kit_1.newSecretKey)(node.keyPair.getPrivateKeyBuffer().toString('hex'), 'hex'),
|
|
297
|
+
publicKey: (0, module_kit_1.newPublicKey)(node.keyPair.getPublicKeyBuffer().toString('hex'), 'hex')
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async getExtendedKeyPairFromDerivative(derivative) {
|
|
301
|
+
const node = this.derivativeToBip32Node(derivative);
|
|
302
|
+
return {
|
|
303
|
+
secretKey: (0, module_kit_1.newExtendedSecretKey)(node.toBase58(), 'encoded'),
|
|
304
|
+
publicKey: (0, module_kit_1.newExtendedPublicKey)(node.neutered().toBase58(), 'encoded')
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async deriveFromExtendedSecretKey(extendedSecretKey, visibilityIndex, addressIndex) {
|
|
308
|
+
const encodedExtendedSecretKey = this.convertExtendedSecretKey(extendedSecretKey, 'encoded');
|
|
309
|
+
const childSecretKey = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedSecretKey.value, this.bitcoinJS.config.network)
|
|
310
|
+
.derive(visibilityIndex)
|
|
311
|
+
.derive(addressIndex)
|
|
312
|
+
.getPrivateKeyBuffer();
|
|
313
|
+
return (0, module_kit_1.newSecretKey)(childSecretKey.toString('hex'), 'hex');
|
|
314
|
+
}
|
|
315
|
+
async signTransactionWithSecretKey(transaction, secretKey) {
|
|
316
|
+
switch (secretKey.type) {
|
|
317
|
+
case 'priv':
|
|
318
|
+
return this.signTransactionWithNonExtendedSecretKey(transaction, secretKey);
|
|
319
|
+
case 'xpriv':
|
|
320
|
+
return this.signTransactionWithExtendedSecretKey(transaction, secretKey);
|
|
321
|
+
default:
|
|
322
|
+
(0, coinlib_core_1.assertNever)(secretKey);
|
|
323
|
+
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Secret key type not supported.');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async signTransactionWithNonExtendedSecretKey(transaction, secretKey) {
|
|
327
|
+
const hexSecretKey = (0, key_1.convertSecretKey)(secretKey, 'hex');
|
|
328
|
+
const transactionBuilder = new this.bitcoinJS.lib.TransactionBuilder(this.bitcoinJS.config.network);
|
|
329
|
+
for (const input of transaction.ins) {
|
|
330
|
+
transactionBuilder.addInput(input.txId, input.vout);
|
|
331
|
+
}
|
|
332
|
+
for (const output of transaction.outs) {
|
|
333
|
+
if (output.isChange) {
|
|
334
|
+
const bufferSecretKey = Buffer.from(secretKey.value, 'hex');
|
|
335
|
+
const keyPair = this.bitcoinJS.lib.ECPair(BigInteger.fromBuffer(bufferSecretKey), null, {
|
|
336
|
+
network: this.bitcoinJS.config.network
|
|
337
|
+
});
|
|
338
|
+
const publicKey = (0, module_kit_1.newPublicKey)(keyPair.getPublicKeyBuffer().toString('hex'), 'hex');
|
|
339
|
+
const generatedChangeAddress = await this.getAddressFromPublicKey(publicKey);
|
|
340
|
+
if (generatedChangeAddress !== output.recipient) {
|
|
341
|
+
throw new errors_1.ConditionViolationError(coinlib_core_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
481
342
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
});
|
|
508
|
-
publicKey = (0, module_kit_1.newPublicKey)(keyPair.getPublicKeyBuffer().toString('hex'), 'hex');
|
|
509
|
-
return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)];
|
|
510
|
-
case 2:
|
|
511
|
-
generatedChangeAddress = _d.sent();
|
|
512
|
-
if (generatedChangeAddress !== output.recipient) {
|
|
513
|
-
throw new errors_1.ConditionViolationError(coinlib_core_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
514
|
-
}
|
|
515
|
-
_d.label = 3;
|
|
516
|
-
case 3:
|
|
517
|
-
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
518
|
-
_d.label = 4;
|
|
519
|
-
case 4:
|
|
520
|
-
_b++;
|
|
521
|
-
return [3 /*break*/, 1];
|
|
522
|
-
case 5:
|
|
523
|
-
for (i = 0; i < transaction.ins.length; i++) {
|
|
524
|
-
transactionBuilder.sign(i, Buffer.from(hexSecretKey.value, 'hex'));
|
|
525
|
-
}
|
|
526
|
-
return [2 /*return*/, this.createSignedTransaction(transaction, transactionBuilder)];
|
|
343
|
+
}
|
|
344
|
+
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
345
|
+
}
|
|
346
|
+
for (let i = 0; i < transaction.ins.length; i++) {
|
|
347
|
+
transactionBuilder.sign(i, Buffer.from(hexSecretKey.value, 'hex'));
|
|
348
|
+
}
|
|
349
|
+
return this.createSignedTransaction(transaction, transactionBuilder);
|
|
350
|
+
}
|
|
351
|
+
async signTransactionWithExtendedSecretKey(transaction, extendedSecretKey) {
|
|
352
|
+
const encodedExtendedSecretKey = this.convertExtendedSecretKey(extendedSecretKey, 'encoded');
|
|
353
|
+
const transactionBuilder = new this.bitcoinJS.lib.TransactionBuilder(this.bitcoinJS.config.network);
|
|
354
|
+
const node = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedSecretKey.value, this.bitcoinJS.config.network);
|
|
355
|
+
for (const input of transaction.ins) {
|
|
356
|
+
transactionBuilder.addInput(input.txId, input.vout);
|
|
357
|
+
}
|
|
358
|
+
const changeAddressBatchSize = 10;
|
|
359
|
+
const changeAddressMaxAddresses = 500;
|
|
360
|
+
for (const output of transaction.outs) {
|
|
361
|
+
let changeAddressIsValid = false;
|
|
362
|
+
if (output.isChange) {
|
|
363
|
+
const extendedPublicKey = (0, module_kit_1.newExtendedPublicKey)(node.neutered().toBase58(), 'encoded');
|
|
364
|
+
if (output.derivationPath) {
|
|
365
|
+
const derivedPublicKey = await this.deriveFromExtendedPublicKey(extendedPublicKey, 1, parseInt(output.derivationPath, 10));
|
|
366
|
+
const generatedChangeAddress = await this.getAddressFromPublicKey(derivedPublicKey);
|
|
367
|
+
changeAddressIsValid = generatedChangeAddress === output.recipient;
|
|
527
368
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
transactionBuilder = new this.bitcoinJS.lib.TransactionBuilder(this.bitcoinJS.config.network);
|
|
540
|
-
node = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedSecretKey.value, this.bitcoinJS.config.network);
|
|
541
|
-
for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) {
|
|
542
|
-
input = _a[_i];
|
|
543
|
-
transactionBuilder.addInput(input.txId, input.vout);
|
|
544
|
-
}
|
|
545
|
-
changeAddressBatchSize = 10;
|
|
546
|
-
changeAddressMaxAddresses = 500;
|
|
547
|
-
_loop_1 = function (output) {
|
|
548
|
-
var changeAddressIsValid, extendedPublicKey_1, derivedPublicKey, generatedChangeAddress, _loop_2, out_x_1, x;
|
|
549
|
-
return __generator(this, function (_e) {
|
|
550
|
-
switch (_e.label) {
|
|
551
|
-
case 0:
|
|
552
|
-
changeAddressIsValid = false;
|
|
553
|
-
if (!output.isChange) return [3 /*break*/, 8];
|
|
554
|
-
extendedPublicKey_1 = (0, module_kit_1.newExtendedPublicKey)(node.neutered().toBase58(), 'encoded');
|
|
555
|
-
if (!output.derivationPath) return [3 /*break*/, 3];
|
|
556
|
-
return [4 /*yield*/, this_1.deriveFromExtendedPublicKey(extendedPublicKey_1, 1, parseInt(output.derivationPath, 10))];
|
|
557
|
-
case 1:
|
|
558
|
-
derivedPublicKey = _e.sent();
|
|
559
|
-
return [4 /*yield*/, this_1.getAddressFromPublicKey(derivedPublicKey)];
|
|
560
|
-
case 2:
|
|
561
|
-
generatedChangeAddress = _e.sent();
|
|
562
|
-
changeAddressIsValid = generatedChangeAddress === output.recipient;
|
|
563
|
-
return [3 /*break*/, 7];
|
|
564
|
-
case 3:
|
|
565
|
-
_loop_2 = function (x) {
|
|
566
|
-
var derivedPublicKeys, addresses;
|
|
567
|
-
return __generator(this, function (_f) {
|
|
568
|
-
switch (_f.label) {
|
|
569
|
-
case 0: return [4 /*yield*/, Promise.all(Array.from(new Array(changeAddressBatchSize)).map(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
570
|
-
return __generator(this, function (_a) {
|
|
571
|
-
return [2 /*return*/, this.deriveFromExtendedPublicKey(extendedPublicKey_1, 1, x)];
|
|
572
|
-
});
|
|
573
|
-
}); }))];
|
|
574
|
-
case 1:
|
|
575
|
-
derivedPublicKeys = _f.sent();
|
|
576
|
-
return [4 /*yield*/, Promise.all(derivedPublicKeys.map(function (publicKey) {
|
|
577
|
-
return _this.getAddressFromPublicKey(publicKey);
|
|
578
|
-
}))];
|
|
579
|
-
case 2:
|
|
580
|
-
addresses = _f.sent();
|
|
581
|
-
if (addresses.indexOf(output.recipient) >= 0) {
|
|
582
|
-
changeAddressIsValid = true;
|
|
583
|
-
x = changeAddressMaxAddresses;
|
|
584
|
-
}
|
|
585
|
-
out_x_1 = x;
|
|
586
|
-
return [2 /*return*/];
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
};
|
|
590
|
-
x = 0;
|
|
591
|
-
_e.label = 4;
|
|
592
|
-
case 4:
|
|
593
|
-
if (!(x < changeAddressMaxAddresses)) return [3 /*break*/, 7];
|
|
594
|
-
return [5 /*yield**/, _loop_2(x)];
|
|
595
|
-
case 5:
|
|
596
|
-
_e.sent();
|
|
597
|
-
x = out_x_1;
|
|
598
|
-
_e.label = 6;
|
|
599
|
-
case 6:
|
|
600
|
-
x += changeAddressBatchSize;
|
|
601
|
-
return [3 /*break*/, 4];
|
|
602
|
-
case 7:
|
|
603
|
-
if (!changeAddressIsValid) {
|
|
604
|
-
throw new errors_1.InvalidValueError(coinlib_core_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
605
|
-
}
|
|
606
|
-
_e.label = 8;
|
|
607
|
-
case 8:
|
|
608
|
-
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
609
|
-
return [2 /*return*/];
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
};
|
|
613
|
-
this_1 = this;
|
|
614
|
-
_b = 0, _c = transaction.outs;
|
|
615
|
-
_d.label = 1;
|
|
616
|
-
case 1:
|
|
617
|
-
if (!(_b < _c.length)) return [3 /*break*/, 4];
|
|
618
|
-
output = _c[_b];
|
|
619
|
-
return [5 /*yield**/, _loop_1(output)];
|
|
620
|
-
case 2:
|
|
621
|
-
_d.sent();
|
|
622
|
-
_d.label = 3;
|
|
623
|
-
case 3:
|
|
624
|
-
_b++;
|
|
625
|
-
return [3 /*break*/, 1];
|
|
626
|
-
case 4:
|
|
627
|
-
for (i = 0; i < transaction.ins.length; i++) {
|
|
628
|
-
transactionBuilder.sign(i, node.derivePath(transaction.ins[i].derivationPath));
|
|
369
|
+
else {
|
|
370
|
+
for (let x = 0; x < changeAddressMaxAddresses; x += changeAddressBatchSize) {
|
|
371
|
+
const derivedPublicKeys = await Promise.all(Array.from(new Array(changeAddressBatchSize)).map(async () => {
|
|
372
|
+
return this.deriveFromExtendedPublicKey(extendedPublicKey, 1, x);
|
|
373
|
+
}));
|
|
374
|
+
const addresses = await Promise.all(derivedPublicKeys.map((publicKey) => {
|
|
375
|
+
return this.getAddressFromPublicKey(publicKey);
|
|
376
|
+
}));
|
|
377
|
+
if (addresses.indexOf(output.recipient) >= 0) {
|
|
378
|
+
changeAddressIsValid = true;
|
|
379
|
+
x = changeAddressMaxAddresses;
|
|
629
380
|
}
|
|
630
|
-
|
|
381
|
+
}
|
|
631
382
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
383
|
+
if (!changeAddressIsValid) {
|
|
384
|
+
throw new errors_1.InvalidValueError(coinlib_core_1.Domain.BITCOIN, 'Change address could not be verified.');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber());
|
|
388
|
+
}
|
|
389
|
+
for (let i = 0; i < transaction.ins.length; i++) {
|
|
390
|
+
transactionBuilder.sign(i, node.derivePath(transaction.ins[i].derivationPath));
|
|
391
|
+
}
|
|
392
|
+
return this.createSignedTransaction(transaction, transactionBuilder);
|
|
393
|
+
}
|
|
394
|
+
createSignedTransaction(unsignedTransaction, transactionBuilder) {
|
|
395
|
+
let fee = new bignumber_1.default(0);
|
|
396
|
+
for (const txIn of unsignedTransaction.ins) {
|
|
639
397
|
fee = fee.plus(new bignumber_1.default(txIn.value));
|
|
640
398
|
}
|
|
641
|
-
for (
|
|
642
|
-
var txOut = _c[_b];
|
|
399
|
+
for (const txOut of unsignedTransaction.outs) {
|
|
643
400
|
fee = fee.minus(new bignumber_1.default(txOut.value));
|
|
644
401
|
}
|
|
645
402
|
return (0, module_kit_1.newSignedTransaction)({
|
|
646
|
-
from: unsignedTransaction.ins.map(
|
|
647
|
-
to: unsignedTransaction.outs.map(
|
|
403
|
+
from: unsignedTransaction.ins.map((obj) => obj.address),
|
|
404
|
+
to: unsignedTransaction.outs.map((obj) => obj.recipient),
|
|
648
405
|
amount: unsignedTransaction.outs
|
|
649
|
-
.map(
|
|
650
|
-
.reduce(
|
|
406
|
+
.map((obj) => new bignumber_1.default(obj.value))
|
|
407
|
+
.reduce((accumulator, currentValue) => accumulator.plus(currentValue))
|
|
651
408
|
.toString(10),
|
|
652
409
|
fee: fee.toString(10),
|
|
653
410
|
transaction: transactionBuilder.build().toHex()
|
|
654
411
|
});
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
return
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
encodedExtendedSecretKey = this.convertExtendedSecretKey(keyPair.secretKey, 'encoded');
|
|
683
|
-
node = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedSecretKey.value, this.bitcoinJS.config.network);
|
|
684
|
-
derivedNode = node.derive(0).derive(0);
|
|
685
|
-
hexSecretKey = (0, module_kit_1.newSecretKey)(derivedNode.keyPair.getPrivateKeyBuffer(), 'hex');
|
|
686
|
-
}
|
|
687
|
-
return [2 /*return*/, this.cryptoClient.decryptAsymmetric(payload, { publicKey: '', privateKey: hexSecretKey.value })];
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
};
|
|
691
|
-
BitcoinProtocolImpl.prototype.encryptAESWithSecretKey = function (payload, secretKey) {
|
|
692
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
693
|
-
return __generator(this, function (_a) {
|
|
694
|
-
return [2 /*return*/, this.cryptoClient.encryptAES(payload, secretKey.value)];
|
|
695
|
-
});
|
|
696
|
-
});
|
|
697
|
-
};
|
|
698
|
-
BitcoinProtocolImpl.prototype.decryptAESWithSecretKey = function (payload, secretKey) {
|
|
699
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
700
|
-
return __generator(this, function (_a) {
|
|
701
|
-
return [2 /*return*/, this.cryptoClient.decryptAES(payload, secretKey.value)];
|
|
702
|
-
});
|
|
703
|
-
});
|
|
704
|
-
};
|
|
412
|
+
}
|
|
413
|
+
async signMessageWithKeyPair(message, keyPair) {
|
|
414
|
+
const hexSecretKey = keyPair.secretKey.type === 'priv'
|
|
415
|
+
? (0, key_1.convertSecretKey)(keyPair.secretKey, 'hex')
|
|
416
|
+
: (0, key_1.convertExtendedSecretKey)(keyPair.secretKey, { format: 'hex' });
|
|
417
|
+
const signature = await this.cryptoClient.signMessage(message, { privateKey: hexSecretKey.value });
|
|
418
|
+
return (0, module_kit_1.newSignature)(signature, 'encoded');
|
|
419
|
+
}
|
|
420
|
+
async decryptAsymmetricWithKeyPair(payload, keyPair) {
|
|
421
|
+
let hexSecretKey = undefined;
|
|
422
|
+
if (keyPair.secretKey.type === 'priv') {
|
|
423
|
+
hexSecretKey = (0, key_1.convertSecretKey)(keyPair.secretKey, 'hex');
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
const encodedExtendedSecretKey = this.convertExtendedSecretKey(keyPair.secretKey, 'encoded');
|
|
427
|
+
const node = this.bitcoinJS.lib.HDNode.fromBase58(encodedExtendedSecretKey.value, this.bitcoinJS.config.network);
|
|
428
|
+
const derivedNode = node.derive(0).derive(0);
|
|
429
|
+
hexSecretKey = (0, module_kit_1.newSecretKey)(derivedNode.keyPair.getPrivateKeyBuffer(), 'hex');
|
|
430
|
+
}
|
|
431
|
+
return this.cryptoClient.decryptAsymmetric(payload, { publicKey: '', privateKey: hexSecretKey.value });
|
|
432
|
+
}
|
|
433
|
+
async encryptAESWithSecretKey(payload, secretKey) {
|
|
434
|
+
return this.cryptoClient.encryptAES(payload, secretKey.value);
|
|
435
|
+
}
|
|
436
|
+
async decryptAESWithSecretKey(payload, secretKey) {
|
|
437
|
+
return this.cryptoClient.decryptAES(payload, secretKey.value);
|
|
438
|
+
}
|
|
705
439
|
// Online
|
|
706
|
-
|
|
707
|
-
return
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
return __generator(this, function (_g) {
|
|
747
|
-
switch (_g.label) {
|
|
748
|
-
case 0:
|
|
749
|
-
encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
750
|
-
page = (_a = cursor === null || cursor === void 0 ? void 0 : cursor.page) !== null && _a !== void 0 ? _a : 1;
|
|
751
|
-
url = "".concat(this.options.network.indexerApi, "/api/v2/xpub/").concat(encodedExtendedPublicKey.value, "?details=txs&tokens=used&pageSize=").concat(limit, "&page=").concat(page);
|
|
752
|
-
return [4 /*yield*/, index_1.default.get(url, {
|
|
753
|
-
responseType: 'json'
|
|
754
|
-
})];
|
|
755
|
-
case 1:
|
|
756
|
-
data = (_g.sent()).data;
|
|
757
|
-
ourAddresses = (data.tokens || []).filter(function (token) { return token.type === 'XPUBAddress'; }).map(function (token) { return token.name; });
|
|
758
|
-
airGapTransactions = [];
|
|
759
|
-
if (data.page === page) {
|
|
760
|
-
for (_i = 0, _b = data.transactions || []; _i < _b.length; _i++) {
|
|
761
|
-
transaction = _b[_i];
|
|
762
|
-
tempAirGapTransactionFrom = [];
|
|
763
|
-
tempAirGapTransactionTo = [];
|
|
764
|
-
tempAirGapTransactionIsInbound = true;
|
|
765
|
-
amountNotAdded = true;
|
|
766
|
-
amount = new bignumber_1.default(0);
|
|
767
|
-
for (_c = 0, _d = transaction.vin; _c < _d.length; _c++) {
|
|
768
|
-
vin = _d[_c];
|
|
769
|
-
if ((0, common_1.containsSome)(vin.addresses, ourAddresses)) {
|
|
770
|
-
tempAirGapTransactionIsInbound = false;
|
|
771
|
-
}
|
|
772
|
-
tempAirGapTransactionFrom.push.apply(tempAirGapTransactionFrom, vin.addresses);
|
|
773
|
-
// amount = amount.plus(vin.value)
|
|
774
|
-
}
|
|
775
|
-
for (_e = 0, _f = transaction.vout; _e < _f.length; _e++) {
|
|
776
|
-
vout = _f[_e];
|
|
777
|
-
if (vout.addresses) {
|
|
778
|
-
tempAirGapTransactionTo.push.apply(tempAirGapTransactionTo, vout.addresses);
|
|
779
|
-
// If receiving address is our address, and transaction is outbound => our change
|
|
780
|
-
// if (containsSome(vout.addresses, ourAddresses) && !tempAirGapTransactionIsInbound) {
|
|
781
|
-
// remove only if related to this address
|
|
782
|
-
// amount = amount.minus(vout.value)
|
|
783
|
-
// }
|
|
784
|
-
// If receiving address is not ours, and transaction isbound => senders change
|
|
785
|
-
// if (!containsSome(vout.addresses, ourAddresses) && tempAirGapTransactionIsInbound) {
|
|
786
|
-
// amount = amount.minus(vout.value)
|
|
787
|
-
// }
|
|
788
|
-
}
|
|
789
|
-
if ((0, common_1.containsSome)(vout.addresses, ourAddresses) && transaction.vout.length > 2) {
|
|
790
|
-
amount = amount.plus(vout.value);
|
|
791
|
-
amountNotAdded = false;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
// deduct fee from amount
|
|
795
|
-
//amount = amount.minus(transaction.fees)
|
|
796
|
-
if (amountNotAdded) {
|
|
797
|
-
amount = amount.plus(transaction.vout[0].value);
|
|
798
|
-
}
|
|
799
|
-
airGapTransaction = {
|
|
800
|
-
from: tempAirGapTransactionFrom,
|
|
801
|
-
to: tempAirGapTransactionTo,
|
|
802
|
-
isInbound: tempAirGapTransactionIsInbound,
|
|
803
|
-
amount: (0, module_kit_1.newAmount)(amount, 'blockchain'),
|
|
804
|
-
fee: (0, module_kit_1.newAmount)(transaction.fees, 'blockchain'),
|
|
805
|
-
status: {
|
|
806
|
-
type: 'applied',
|
|
807
|
-
hash: transaction.txid,
|
|
808
|
-
block: transaction.blockHeight.toString()
|
|
809
|
-
},
|
|
810
|
-
network: this.options.network,
|
|
811
|
-
timestamp: transaction.blockTime
|
|
812
|
-
};
|
|
813
|
-
airGapTransactions.push(airGapTransaction);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
hasNext = page < data.totalPages;
|
|
817
|
-
return [2 /*return*/, {
|
|
818
|
-
transactions: airGapTransactions,
|
|
819
|
-
cursor: {
|
|
820
|
-
hasNext: hasNext,
|
|
821
|
-
page: hasNext ? page + 1 : undefined
|
|
822
|
-
}
|
|
823
|
-
}];
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
});
|
|
827
|
-
};
|
|
828
|
-
BitcoinProtocolImpl.prototype.getTransactionsForAddress = function (address, limit, cursor) {
|
|
829
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
830
|
-
return __generator(this, function (_a) {
|
|
831
|
-
return [2 /*return*/, this.getTransactionsForAddresses([address], limit, cursor)];
|
|
832
|
-
});
|
|
833
|
-
});
|
|
834
|
-
};
|
|
835
|
-
BitcoinProtocolImpl.prototype.getTransactionsForAddresses = function (addresses, limit, cursor) {
|
|
836
|
-
var _a;
|
|
837
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
838
|
-
var airGapTransactions, page, url, data, _i, _b, transaction, tempAirGapTransactionFrom, tempAirGapTransactionTo, tempAirGapTransactionIsInbound, amount, _c, _d, vin, _e, _f, vout, airGapTransaction, hasNext;
|
|
839
|
-
return __generator(this, function (_g) {
|
|
840
|
-
switch (_g.label) {
|
|
841
|
-
case 0:
|
|
842
|
-
airGapTransactions = [];
|
|
843
|
-
page = (_a = cursor === null || cursor === void 0 ? void 0 : cursor.page) !== null && _a !== void 0 ? _a : 1;
|
|
844
|
-
url = "".concat(this.options.network.indexerApi, "/api/v2/address/").concat(addresses[0], "?page=").concat(page, "&pageSize=").concat(limit, "&details=txs");
|
|
845
|
-
return [4 /*yield*/, index_1.default.get(url, {
|
|
846
|
-
responseType: 'json'
|
|
847
|
-
})];
|
|
848
|
-
case 1:
|
|
849
|
-
data = (_g.sent()).data;
|
|
850
|
-
if (data.page == page) {
|
|
851
|
-
for (_i = 0, _b = data.transactions || []; _i < _b.length; _i++) {
|
|
852
|
-
transaction = _b[_i];
|
|
853
|
-
tempAirGapTransactionFrom = [];
|
|
854
|
-
tempAirGapTransactionTo = [];
|
|
855
|
-
tempAirGapTransactionIsInbound = true;
|
|
856
|
-
amount = new bignumber_1.default(0);
|
|
857
|
-
for (_c = 0, _d = transaction.vin; _c < _d.length; _c++) {
|
|
858
|
-
vin = _d[_c];
|
|
859
|
-
if (vin.addresses && (0, common_1.containsSome)(vin.addresses, addresses)) {
|
|
860
|
-
tempAirGapTransactionIsInbound = false;
|
|
861
|
-
}
|
|
862
|
-
tempAirGapTransactionFrom.push.apply(tempAirGapTransactionFrom, vin.addresses);
|
|
863
|
-
amount = vin.value ? amount.plus(vin.value) : amount;
|
|
864
|
-
}
|
|
865
|
-
for (_e = 0, _f = transaction.vout; _e < _f.length; _e++) {
|
|
866
|
-
vout = _f[_e];
|
|
867
|
-
if (vout.addresses) {
|
|
868
|
-
tempAirGapTransactionTo.push.apply(tempAirGapTransactionTo, vout.addresses);
|
|
869
|
-
// If receiving address is our address, and transaction is outbound => our change
|
|
870
|
-
if ((0, common_1.containsSome)(vout.addresses, addresses) && !tempAirGapTransactionIsInbound) {
|
|
871
|
-
// remove only if related to this address
|
|
872
|
-
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
873
|
-
}
|
|
874
|
-
// If receiving address is not ours, and transaction isbound => senders change
|
|
875
|
-
if (!(0, common_1.containsSome)(vout.addresses, addresses) && tempAirGapTransactionIsInbound) {
|
|
876
|
-
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
// deduct fee from amount
|
|
881
|
-
amount = amount.minus(new bignumber_1.default(transaction.fees));
|
|
882
|
-
airGapTransaction = {
|
|
883
|
-
from: tempAirGapTransactionFrom,
|
|
884
|
-
to: tempAirGapTransactionTo,
|
|
885
|
-
isInbound: tempAirGapTransactionIsInbound,
|
|
886
|
-
amount: (0, module_kit_1.newAmount)(amount, 'blockchain'),
|
|
887
|
-
fee: (0, module_kit_1.newAmount)(transaction.fees, 'blockchain'),
|
|
888
|
-
status: {
|
|
889
|
-
type: 'applied',
|
|
890
|
-
hash: transaction.txid,
|
|
891
|
-
block: transaction.blockHeight.toString()
|
|
892
|
-
},
|
|
893
|
-
network: this.options.network,
|
|
894
|
-
timestamp: transaction.blockTime
|
|
895
|
-
};
|
|
896
|
-
airGapTransactions.push(airGapTransaction);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
hasNext = page < data.totalPages;
|
|
900
|
-
return [2 /*return*/, {
|
|
901
|
-
transactions: airGapTransactions,
|
|
902
|
-
cursor: {
|
|
903
|
-
hasNext: hasNext,
|
|
904
|
-
page: hasNext ? page + 1 : undefined
|
|
905
|
-
}
|
|
906
|
-
}];
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
});
|
|
910
|
-
};
|
|
911
|
-
BitcoinProtocolImpl.prototype.getBalanceOfPublicKey = function (publicKey) {
|
|
912
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
913
|
-
return __generator(this, function (_a) {
|
|
914
|
-
switch (publicKey.type) {
|
|
915
|
-
case 'pub':
|
|
916
|
-
return [2 /*return*/, this.getBalanceOfNonExtendedPublicKey(publicKey)];
|
|
917
|
-
case 'xpub':
|
|
918
|
-
return [2 /*return*/, this.getBalanceOfExtendedPublicKey(publicKey)];
|
|
919
|
-
default:
|
|
920
|
-
(0, coinlib_core_1.assertNever)(publicKey);
|
|
921
|
-
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Unsupported public key type.');
|
|
922
|
-
}
|
|
923
|
-
return [2 /*return*/];
|
|
924
|
-
});
|
|
925
|
-
});
|
|
926
|
-
};
|
|
927
|
-
BitcoinProtocolImpl.prototype.getBalanceOfNonExtendedPublicKey = function (publicKey) {
|
|
928
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
929
|
-
var address;
|
|
930
|
-
return __generator(this, function (_a) {
|
|
931
|
-
switch (_a.label) {
|
|
932
|
-
case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)];
|
|
933
|
-
case 1:
|
|
934
|
-
address = _a.sent();
|
|
935
|
-
return [2 /*return*/, this.getBalanceOfAddresses([address])];
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
});
|
|
939
|
-
};
|
|
940
|
-
BitcoinProtocolImpl.prototype.getBalanceOfExtendedPublicKey = function (extendedPublicKey) {
|
|
941
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
942
|
-
var encodedExtendedPublicKey, data;
|
|
943
|
-
return __generator(this, function (_a) {
|
|
944
|
-
switch (_a.label) {
|
|
945
|
-
case 0:
|
|
946
|
-
encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
947
|
-
return [4 /*yield*/, index_1.default.get("".concat(this.options.network.indexerApi, "/api/v2/xpub/").concat(encodedExtendedPublicKey.value, "?pageSize=1"), {
|
|
948
|
-
responseType: 'json'
|
|
949
|
-
})];
|
|
950
|
-
case 1:
|
|
951
|
-
data = (_a.sent()).data;
|
|
952
|
-
return [2 /*return*/, {
|
|
953
|
-
total: (0, module_kit_1.newAmount)(data.balance, 'blockchain')
|
|
954
|
-
}];
|
|
440
|
+
async getNetwork() {
|
|
441
|
+
return this.options.network;
|
|
442
|
+
}
|
|
443
|
+
async getTransactionsForPublicKey(publicKey, limit, cursor) {
|
|
444
|
+
switch (publicKey.type) {
|
|
445
|
+
case 'pub':
|
|
446
|
+
return this.getTransactionsForNonExtendedPublicKey(publicKey, limit, cursor);
|
|
447
|
+
case 'xpub':
|
|
448
|
+
return this.getTransactionsForExtendedPublicKey(publicKey, limit, cursor);
|
|
449
|
+
default:
|
|
450
|
+
(0, coinlib_core_1.assertNever)(publicKey);
|
|
451
|
+
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Public key type not supported');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async getTransactionsForNonExtendedPublicKey(publicKey, limit, cursor) {
|
|
455
|
+
const address = await this.getAddressFromPublicKey(publicKey);
|
|
456
|
+
return this.getTransactionsForAddresses([address], limit, cursor);
|
|
457
|
+
}
|
|
458
|
+
async getTransactionsForExtendedPublicKey(extendedPublicKey, limit, cursor) {
|
|
459
|
+
const encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
460
|
+
const page = cursor?.page ?? 1;
|
|
461
|
+
const url = `${this.options.network.indexerApi}/api/v2/xpub/${encodedExtendedPublicKey.value}?details=txs&tokens=used&pageSize=${limit}&page=${page}`;
|
|
462
|
+
const { data } = await index_1.default.get(url, {
|
|
463
|
+
responseType: 'json'
|
|
464
|
+
});
|
|
465
|
+
const ourAddresses = (data.tokens || []).filter((token) => token.type === 'XPUBAddress').map((token) => token.name);
|
|
466
|
+
const airGapTransactions = [];
|
|
467
|
+
if (data.page === page) {
|
|
468
|
+
for (const transaction of data.transactions || []) {
|
|
469
|
+
const tempAirGapTransactionFrom = [];
|
|
470
|
+
const tempAirGapTransactionTo = [];
|
|
471
|
+
let tempAirGapTransactionIsInbound = true;
|
|
472
|
+
let amountNotAdded = true;
|
|
473
|
+
let amount = new bignumber_1.default(0);
|
|
474
|
+
for (const vin of transaction.vin) {
|
|
475
|
+
if ((0, common_1.containsSome)(vin.addresses, ourAddresses)) {
|
|
476
|
+
tempAirGapTransactionIsInbound = false;
|
|
477
|
+
}
|
|
478
|
+
tempAirGapTransactionFrom.push(...vin.addresses);
|
|
479
|
+
// amount = amount.plus(vin.value)
|
|
955
480
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
_i = 0, addresses_1 = addresses;
|
|
974
|
-
_a.label = 1;
|
|
975
|
-
case 1:
|
|
976
|
-
if (!(_i < addresses_1.length)) return [3 /*break*/, 4];
|
|
977
|
-
address = addresses_1[_i];
|
|
978
|
-
return [4 /*yield*/, index_1.default.get("".concat(this.options.network.indexerApi, "/api/v2/address/").concat(address, "?details=basic"), {
|
|
979
|
-
responseType: 'json'
|
|
980
|
-
})];
|
|
981
|
-
case 2:
|
|
982
|
-
data = (_a.sent()).data;
|
|
983
|
-
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(data.balance));
|
|
984
|
-
_a.label = 3;
|
|
985
|
-
case 3:
|
|
986
|
-
_i++;
|
|
987
|
-
return [3 /*break*/, 1];
|
|
988
|
-
case 4: return [2 /*return*/, {
|
|
989
|
-
total: (0, module_kit_1.newAmount)(valueAccumulator, 'blockchain')
|
|
990
|
-
}];
|
|
481
|
+
for (const vout of transaction.vout) {
|
|
482
|
+
if (vout.addresses) {
|
|
483
|
+
tempAirGapTransactionTo.push(...vout.addresses);
|
|
484
|
+
// If receiving address is our address, and transaction is outbound => our change
|
|
485
|
+
// if (containsSome(vout.addresses, ourAddresses) && !tempAirGapTransactionIsInbound) {
|
|
486
|
+
// remove only if related to this address
|
|
487
|
+
// amount = amount.minus(vout.value)
|
|
488
|
+
// }
|
|
489
|
+
// If receiving address is not ours, and transaction isbound => senders change
|
|
490
|
+
// if (!containsSome(vout.addresses, ourAddresses) && tempAirGapTransactionIsInbound) {
|
|
491
|
+
// amount = amount.minus(vout.value)
|
|
492
|
+
// }
|
|
493
|
+
}
|
|
494
|
+
if ((0, common_1.containsSome)(vout.addresses, ourAddresses) && transaction.vout.length > 2) {
|
|
495
|
+
amount = amount.plus(vout.value);
|
|
496
|
+
amountNotAdded = false;
|
|
497
|
+
}
|
|
991
498
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
997
|
-
return __generator(this, function (_a) {
|
|
998
|
-
switch (_a.label) {
|
|
999
|
-
case 0: return [4 /*yield*/, this.getBalanceOfPublicKey(publicKey)];
|
|
1000
|
-
case 1: return [2 /*return*/, (_a.sent()).total];
|
|
499
|
+
// deduct fee from amount
|
|
500
|
+
//amount = amount.minus(transaction.fees)
|
|
501
|
+
if (amountNotAdded) {
|
|
502
|
+
amount = amount.plus(transaction.vout[0].value);
|
|
1001
503
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
504
|
+
const airGapTransaction = {
|
|
505
|
+
from: tempAirGapTransactionFrom,
|
|
506
|
+
to: tempAirGapTransactionTo,
|
|
507
|
+
isInbound: tempAirGapTransactionIsInbound,
|
|
508
|
+
amount: (0, module_kit_1.newAmount)(amount, 'blockchain'),
|
|
509
|
+
fee: (0, module_kit_1.newAmount)(transaction.fees, 'blockchain'),
|
|
510
|
+
status: {
|
|
511
|
+
type: 'applied',
|
|
512
|
+
hash: transaction.txid,
|
|
513
|
+
block: transaction.blockHeight.toString()
|
|
514
|
+
},
|
|
515
|
+
network: this.options.network,
|
|
516
|
+
timestamp: transaction.blockTime
|
|
517
|
+
};
|
|
518
|
+
airGapTransactions.push(airGapTransaction);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const hasNext = page < data.totalPages;
|
|
522
|
+
return {
|
|
523
|
+
transactions: airGapTransactions,
|
|
524
|
+
cursor: {
|
|
525
|
+
hasNext,
|
|
526
|
+
page: hasNext ? page + 1 : undefined
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
async getTransactionsForAddress(address, limit, cursor) {
|
|
531
|
+
return this.getTransactionsForAddresses([address], limit, cursor);
|
|
532
|
+
}
|
|
533
|
+
async getTransactionsForAddresses(addresses, limit, cursor) {
|
|
534
|
+
const airGapTransactions = [];
|
|
535
|
+
const page = cursor?.page ?? 1;
|
|
536
|
+
const url = `${this.options.network.indexerApi}/api/v2/address/${addresses[0]}?page=${page}&pageSize=${limit}&details=txs`;
|
|
537
|
+
const { data } = await index_1.default.get(url, {
|
|
538
|
+
responseType: 'json'
|
|
539
|
+
});
|
|
540
|
+
if (data.page == page) {
|
|
541
|
+
for (const transaction of data.transactions || []) {
|
|
542
|
+
const tempAirGapTransactionFrom = [];
|
|
543
|
+
const tempAirGapTransactionTo = [];
|
|
544
|
+
let tempAirGapTransactionIsInbound = true;
|
|
545
|
+
let amount = new bignumber_1.default(0);
|
|
546
|
+
for (const vin of transaction.vin) {
|
|
547
|
+
if (vin.addresses && (0, common_1.containsSome)(vin.addresses, addresses)) {
|
|
548
|
+
tempAirGapTransactionIsInbound = false;
|
|
549
|
+
}
|
|
550
|
+
tempAirGapTransactionFrom.push(...vin.addresses);
|
|
551
|
+
amount = vin.value ? amount.plus(vin.value) : amount;
|
|
1041
552
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
var _this = this;
|
|
1050
|
-
return __generator(this, function (_a) {
|
|
1051
|
-
switch (_a.label) {
|
|
1052
|
-
case 0:
|
|
1053
|
-
if (!((configuration === null || configuration === void 0 ? void 0 : configuration.fee) !== undefined)) return [3 /*break*/, 1];
|
|
1054
|
-
fee = configuration.fee;
|
|
1055
|
-
return [3 /*break*/, 3];
|
|
1056
|
-
case 1: return [4 /*yield*/, this.getTransactionFeeWithPublicKey(publicKey, details)];
|
|
1057
|
-
case 2:
|
|
1058
|
-
estimatedFee = _a.sent();
|
|
1059
|
-
fee = estimatedFee.medium;
|
|
1060
|
-
_a.label = 3;
|
|
1061
|
-
case 3:
|
|
1062
|
-
wrappedFee = new bignumber_1.default((0, module_kit_1.newAmount)(fee).blockchain(this.units).value);
|
|
1063
|
-
transaction = (0, module_kit_1.newUnsignedTransaction)({
|
|
1064
|
-
ins: [],
|
|
1065
|
-
outs: []
|
|
1066
|
-
});
|
|
1067
|
-
return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)];
|
|
1068
|
-
case 4:
|
|
1069
|
-
address = _a.sent();
|
|
1070
|
-
return [4 /*yield*/, index_1.default.get("".concat(this.options.network.indexerApi, "/api/v2/utxo/").concat(address), {
|
|
1071
|
-
responseType: 'json'
|
|
1072
|
-
})];
|
|
1073
|
-
case 5:
|
|
1074
|
-
utxos = (_a.sent()).data;
|
|
1075
|
-
totalRequiredBalance = details
|
|
1076
|
-
.map(function (_a) {
|
|
1077
|
-
var amount = _a.amount;
|
|
1078
|
-
return new bignumber_1.default((0, module_kit_1.newAmount)(amount).blockchain(_this.units).value);
|
|
1079
|
-
})
|
|
1080
|
-
.reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); })
|
|
1081
|
-
.plus(wrappedFee);
|
|
1082
|
-
valueAccumulator = new bignumber_1.default(0);
|
|
1083
|
-
for (_i = 0, utxos_1 = utxos; _i < utxos_1.length; _i++) {
|
|
1084
|
-
utxo = utxos_1[_i];
|
|
1085
|
-
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(utxo.value));
|
|
1086
|
-
transaction.ins.push({
|
|
1087
|
-
txId: utxo.txid,
|
|
1088
|
-
value: new bignumber_1.default(utxo.value).toString(10),
|
|
1089
|
-
vout: utxo.vout,
|
|
1090
|
-
address: address
|
|
1091
|
-
});
|
|
1092
|
-
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
1093
|
-
break;
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
if (valueAccumulator.isLessThan(totalRequiredBalance)) {
|
|
1097
|
-
throw new errors_1.BalanceError(coinlib_core_1.Domain.BITCOIN, "not enough balance, having ".concat(valueAccumulator.toFixed(), " of ").concat(totalRequiredBalance.toFixed()));
|
|
1098
|
-
}
|
|
1099
|
-
// tx.addInput(utxo.txid, utxo.vout)
|
|
1100
|
-
for (i = 0; i < details.length; i++) {
|
|
1101
|
-
value = (0, module_kit_1.newAmount)(details[i].amount).blockchain(this.units).value;
|
|
1102
|
-
transaction.outs.push({
|
|
1103
|
-
recipient: details[i].to,
|
|
1104
|
-
isChange: false,
|
|
1105
|
-
value: value
|
|
1106
|
-
});
|
|
1107
|
-
valueAccumulator = valueAccumulator.minus(value);
|
|
1108
|
-
// tx.addOutput(details[i].to, details[i].amount)
|
|
553
|
+
for (const vout of transaction.vout) {
|
|
554
|
+
if (vout.addresses) {
|
|
555
|
+
tempAirGapTransactionTo.push(...vout.addresses);
|
|
556
|
+
// If receiving address is our address, and transaction is outbound => our change
|
|
557
|
+
if ((0, common_1.containsSome)(vout.addresses, addresses) && !tempAirGapTransactionIsInbound) {
|
|
558
|
+
// remove only if related to this address
|
|
559
|
+
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
1109
560
|
}
|
|
1110
|
-
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1113
|
-
recipient: address,
|
|
1114
|
-
isChange: true,
|
|
1115
|
-
value: changeValue.toString(10)
|
|
1116
|
-
});
|
|
561
|
+
// If receiving address is not ours, and transaction isbound => senders change
|
|
562
|
+
if (!(0, common_1.containsSome)(vout.addresses, addresses) && tempAirGapTransactionIsInbound) {
|
|
563
|
+
amount = amount.minus(new bignumber_1.default(vout.value));
|
|
1117
564
|
}
|
|
1118
|
-
|
|
565
|
+
}
|
|
1119
566
|
}
|
|
1120
|
-
|
|
567
|
+
// deduct fee from amount
|
|
568
|
+
amount = amount.minus(new bignumber_1.default(transaction.fees));
|
|
569
|
+
const airGapTransaction = {
|
|
570
|
+
from: tempAirGapTransactionFrom,
|
|
571
|
+
to: tempAirGapTransactionTo,
|
|
572
|
+
isInbound: tempAirGapTransactionIsInbound,
|
|
573
|
+
amount: (0, module_kit_1.newAmount)(amount, 'blockchain'),
|
|
574
|
+
fee: (0, module_kit_1.newAmount)(transaction.fees, 'blockchain'),
|
|
575
|
+
status: {
|
|
576
|
+
type: 'applied',
|
|
577
|
+
hash: transaction.txid,
|
|
578
|
+
block: transaction.blockHeight.toString()
|
|
579
|
+
},
|
|
580
|
+
network: this.options.network,
|
|
581
|
+
timestamp: transaction.blockTime
|
|
582
|
+
};
|
|
583
|
+
airGapTransactions.push(airGapTransaction);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const hasNext = page < data.totalPages;
|
|
587
|
+
return {
|
|
588
|
+
transactions: airGapTransactions,
|
|
589
|
+
cursor: {
|
|
590
|
+
hasNext,
|
|
591
|
+
page: hasNext ? page + 1 : undefined
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
async getBalanceOfPublicKey(publicKey) {
|
|
596
|
+
switch (publicKey.type) {
|
|
597
|
+
case 'pub':
|
|
598
|
+
return this.getBalanceOfNonExtendedPublicKey(publicKey);
|
|
599
|
+
case 'xpub':
|
|
600
|
+
return this.getBalanceOfExtendedPublicKey(publicKey);
|
|
601
|
+
default:
|
|
602
|
+
(0, coinlib_core_1.assertNever)(publicKey);
|
|
603
|
+
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Unsupported public key type.');
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async getBalanceOfNonExtendedPublicKey(publicKey) {
|
|
607
|
+
const address = await this.getAddressFromPublicKey(publicKey);
|
|
608
|
+
return this.getBalanceOfAddresses([address]);
|
|
609
|
+
}
|
|
610
|
+
async getBalanceOfExtendedPublicKey(extendedPublicKey) {
|
|
611
|
+
const encodedExtendedPublicKey = this.convertExtendedPublicKey(extendedPublicKey, 'encoded');
|
|
612
|
+
const { data } = await index_1.default.get(`${this.options.network.indexerApi}/api/v2/xpub/${encodedExtendedPublicKey.value}?pageSize=1`, {
|
|
613
|
+
responseType: 'json'
|
|
1121
614
|
});
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
derivedPublicKey = _a.sent();
|
|
1231
|
-
return [4 /*yield*/, this.getAddressFromPublicKey(derivedPublicKey)];
|
|
1232
|
-
case 11:
|
|
1233
|
-
derivedAddress = _a.sent();
|
|
1234
|
-
transaction.outs.push({
|
|
1235
|
-
recipient: derivedAddress,
|
|
1236
|
-
isChange: true,
|
|
1237
|
-
value: changeValue.toString(10),
|
|
1238
|
-
derivationPath: changeAddressIndex.toString()
|
|
1239
|
-
});
|
|
1240
|
-
_a.label = 12;
|
|
1241
|
-
case 12: return [2 /*return*/, transaction];
|
|
1242
|
-
}
|
|
615
|
+
return {
|
|
616
|
+
total: (0, module_kit_1.newAmount)(data.balance, 'blockchain')
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
async getBalanceOfAddress(address) {
|
|
620
|
+
return this.getBalanceOfAddresses([address]);
|
|
621
|
+
}
|
|
622
|
+
async getBalanceOfAddresses(addresses) {
|
|
623
|
+
let valueAccumulator = new bignumber_1.default(0);
|
|
624
|
+
// The API doesn't support batch checking of balances, so we have to do it manually
|
|
625
|
+
for (const address of addresses) {
|
|
626
|
+
const { data } = await index_1.default.get(`${this.options.network.indexerApi}/api/v2/address/${address}?details=basic`, {
|
|
627
|
+
responseType: 'json'
|
|
628
|
+
});
|
|
629
|
+
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(data.balance));
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
total: (0, module_kit_1.newAmount)(valueAccumulator, 'blockchain')
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
async getTransactionMaxAmountWithPublicKey(publicKey, to, configuration) {
|
|
636
|
+
return (await this.getBalanceOfPublicKey(publicKey)).total;
|
|
637
|
+
}
|
|
638
|
+
async getTransactionFeeWithPublicKey(_publicKey, _details, _configuration) {
|
|
639
|
+
const result = (await index_1.default.get(`${this.options.network.indexerApi}/api/v2/estimatefee/5`)).data.result;
|
|
640
|
+
const estimatedFee = new bignumber_1.default((0, module_kit_1.newAmount)(result, 'BTC').blockchain(this.units).value);
|
|
641
|
+
if (estimatedFee.isZero()) {
|
|
642
|
+
return this.feeDefaults;
|
|
643
|
+
}
|
|
644
|
+
const feeStepFactor = new bignumber_1.default(0.5);
|
|
645
|
+
const mediumFee = estimatedFee;
|
|
646
|
+
const lowFee = mediumFee.minus(mediumFee.times(feeStepFactor)).integerValue(bignumber_1.default.ROUND_FLOOR);
|
|
647
|
+
const highFee = mediumFee.plus(mediumFee.times(feeStepFactor)).integerValue(bignumber_1.default.ROUND_FLOOR);
|
|
648
|
+
return {
|
|
649
|
+
low: (0, module_kit_1.newAmount)(lowFee, 'blockchain'),
|
|
650
|
+
medium: (0, module_kit_1.newAmount)(mediumFee, 'blockchain'),
|
|
651
|
+
high: (0, module_kit_1.newAmount)(highFee, 'blockchain')
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
async prepareTransactionWithPublicKey(publicKey, details, configuration) {
|
|
655
|
+
switch (publicKey.type) {
|
|
656
|
+
case 'pub':
|
|
657
|
+
return this.prepareTransactionWithNonExtendedPublicKey(publicKey, details, configuration);
|
|
658
|
+
case 'xpub':
|
|
659
|
+
return this.prepareTransactionWithExtendedPublicKey(publicKey, details, configuration);
|
|
660
|
+
default:
|
|
661
|
+
(0, coinlib_core_1.assertNever)(publicKey);
|
|
662
|
+
throw new errors_1.UnsupportedError(coinlib_core_1.Domain.BITCOIN, 'Unuspported public key type.');
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async prepareTransactionWithNonExtendedPublicKey(publicKey, details, configuration) {
|
|
666
|
+
let fee;
|
|
667
|
+
if (configuration?.fee !== undefined) {
|
|
668
|
+
fee = configuration.fee;
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
const estimatedFee = await this.getTransactionFeeWithPublicKey(publicKey, details);
|
|
672
|
+
fee = estimatedFee.medium;
|
|
673
|
+
}
|
|
674
|
+
const wrappedFee = new bignumber_1.default((0, module_kit_1.newAmount)(fee).blockchain(this.units).value);
|
|
675
|
+
const transaction = (0, module_kit_1.newUnsignedTransaction)({
|
|
676
|
+
ins: [],
|
|
677
|
+
outs: []
|
|
678
|
+
});
|
|
679
|
+
const address = await this.getAddressFromPublicKey(publicKey);
|
|
680
|
+
const { data: utxos } = await index_1.default.get(`${this.options.network.indexerApi}/api/v2/utxo/${address}`, {
|
|
681
|
+
responseType: 'json'
|
|
682
|
+
});
|
|
683
|
+
const totalRequiredBalance = details
|
|
684
|
+
.map(({ amount }) => new bignumber_1.default((0, module_kit_1.newAmount)(amount).blockchain(this.units).value))
|
|
685
|
+
.reduce((accumulator, currentValue) => accumulator.plus(currentValue))
|
|
686
|
+
.plus(wrappedFee);
|
|
687
|
+
let valueAccumulator = new bignumber_1.default(0);
|
|
688
|
+
for (const utxo of utxos) {
|
|
689
|
+
valueAccumulator = valueAccumulator.plus(new bignumber_1.default(utxo.value));
|
|
690
|
+
transaction.ins.push({
|
|
691
|
+
txId: utxo.txid,
|
|
692
|
+
value: new bignumber_1.default(utxo.value).toString(10),
|
|
693
|
+
vout: utxo.vout,
|
|
694
|
+
address
|
|
695
|
+
});
|
|
696
|
+
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (valueAccumulator.isLessThan(totalRequiredBalance)) {
|
|
701
|
+
throw new errors_1.BalanceError(coinlib_core_1.Domain.BITCOIN, `not enough balance, having ${valueAccumulator.toFixed()} of ${totalRequiredBalance.toFixed()}`);
|
|
702
|
+
}
|
|
703
|
+
// tx.addInput(utxo.txid, utxo.vout)
|
|
704
|
+
for (let i = 0; i < details.length; i++) {
|
|
705
|
+
const value = (0, module_kit_1.newAmount)(details[i].amount).blockchain(this.units).value;
|
|
706
|
+
transaction.outs.push({
|
|
707
|
+
recipient: details[i].to,
|
|
708
|
+
isChange: false,
|
|
709
|
+
value
|
|
710
|
+
});
|
|
711
|
+
valueAccumulator = valueAccumulator.minus(value);
|
|
712
|
+
// tx.addOutput(details[i].to, details[i].amount)
|
|
713
|
+
}
|
|
714
|
+
// If the change is considered dust, the transaction will fail.
|
|
715
|
+
// Dust is a variable value around 300-600 satoshis, depending on the configuration.
|
|
716
|
+
// We set a low fee here to not block any transactions, but it might still fail due to "dust".
|
|
717
|
+
const changeValue = valueAccumulator.minus(wrappedFee);
|
|
718
|
+
if (changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) {
|
|
719
|
+
transaction.outs.push({
|
|
720
|
+
recipient: address,
|
|
721
|
+
isChange: true,
|
|
722
|
+
value: changeValue.toString(10)
|
|
1243
723
|
});
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
724
|
+
}
|
|
725
|
+
return transaction;
|
|
726
|
+
}
|
|
727
|
+
async prepareTransactionWithExtendedPublicKey(extendedPublicKey, details, configuration) {
|
|
728
|
+
let targetFee;
|
|
729
|
+
if (configuration?.fee !== undefined) {
|
|
730
|
+
targetFee = configuration.fee;
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
const estimatedFee = await this.getTransactionFeeWithPublicKey(extendedPublicKey, details);
|
|
734
|
+
targetFee = estimatedFee.medium;
|
|
735
|
+
}
|
|
736
|
+
const wrappedFee = new bignumber_1.default((0, module_kit_1.newAmount)(targetFee).blockchain(this.units).value);
|
|
737
|
+
const transaction = (0, module_kit_1.newUnsignedTransaction)({
|
|
738
|
+
ins: [],
|
|
739
|
+
outs: []
|
|
740
|
+
});
|
|
741
|
+
const { data: utxos } = await index_1.default
|
|
742
|
+
.get(`${this.options.network.indexerApi}/api/v2/utxo/${extendedPublicKey.value}?confirmed=true`, {
|
|
743
|
+
responseType: 'json'
|
|
744
|
+
})
|
|
745
|
+
.catch((error) => {
|
|
746
|
+
throw new errors_1.NetworkError(coinlib_core_1.Domain.BITCOIN, error);
|
|
747
|
+
});
|
|
748
|
+
if (utxos.length <= 0) {
|
|
749
|
+
throw new errors_1.BalanceError(coinlib_core_1.Domain.BITCOIN, 'not enough balance'); // no transactions found on those addresses, probably won't find anything in the next ones
|
|
750
|
+
}
|
|
751
|
+
const totalRequiredBalance = details
|
|
752
|
+
.map(({ amount }) => new bignumber_1.default((0, module_kit_1.newAmount)(amount).blockchain(this.units).value))
|
|
753
|
+
.reduce((accumulator, currentValue) => accumulator.plus(currentValue))
|
|
754
|
+
.plus(wrappedFee);
|
|
755
|
+
let valueAccumulator = new bignumber_1.default(0);
|
|
756
|
+
const getPathIndexes = (path) => {
|
|
757
|
+
const result = path
|
|
758
|
+
.split('/')
|
|
759
|
+
.slice(-2)
|
|
760
|
+
.map((item) => parseInt(item, 10))
|
|
761
|
+
.filter((item) => !isNaN(item));
|
|
762
|
+
if (result.length !== 2) {
|
|
763
|
+
throw new errors_1.ConditionViolationError(coinlib_core_1.Domain.BITCOIN, 'Unexpected path format');
|
|
764
|
+
}
|
|
765
|
+
return [result[0], result[1]];
|
|
766
|
+
};
|
|
767
|
+
for (const utxo of utxos) {
|
|
768
|
+
valueAccumulator = valueAccumulator.plus(utxo.value);
|
|
769
|
+
const indexes = getPathIndexes(utxo.path);
|
|
770
|
+
const derivedPublicKey = await this.deriveFromExtendedPublicKey(extendedPublicKey, indexes[0], indexes[1]);
|
|
771
|
+
const derivedAddress = await this.getAddressFromPublicKey(derivedPublicKey);
|
|
772
|
+
if (derivedAddress === utxo.address) {
|
|
773
|
+
transaction.ins.push({
|
|
774
|
+
txId: utxo.txid,
|
|
775
|
+
value: new bignumber_1.default(utxo.value).toString(10),
|
|
776
|
+
vout: utxo.vout,
|
|
777
|
+
address: utxo.address,
|
|
778
|
+
derivationPath: indexes.join('/')
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
throw new errors_1.InvalidValueError(coinlib_core_1.Domain.BITCOIN, `Invalid address ${JSON.stringify(utxo.address)} returned from API`);
|
|
783
|
+
}
|
|
784
|
+
if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) {
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (valueAccumulator.isLessThan(totalRequiredBalance)) {
|
|
789
|
+
throw new errors_1.BalanceError(coinlib_core_1.Domain.BITCOIN, 'not enough balance');
|
|
790
|
+
}
|
|
791
|
+
for (let i = 0; i < details.length; i++) {
|
|
792
|
+
const value = (0, module_kit_1.newAmount)(details[i].amount).blockchain(this.units).value;
|
|
793
|
+
transaction.outs.push({
|
|
794
|
+
recipient: details[i].to,
|
|
795
|
+
isChange: false,
|
|
796
|
+
value,
|
|
797
|
+
derivationPath: '' // TODO: Remove this as soon as our serializer supports optional properties
|
|
798
|
+
});
|
|
799
|
+
valueAccumulator = valueAccumulator.minus(value);
|
|
800
|
+
}
|
|
801
|
+
const lastUsedInternalAddress = Math.max(-1, ...utxos
|
|
802
|
+
.map((utxo) => getPathIndexes(utxo.path))
|
|
803
|
+
.filter((indexes) => indexes[0] === 1)
|
|
804
|
+
.map((indexes) => indexes[1]));
|
|
805
|
+
// If the change is considered dust, the transaction will fail.
|
|
806
|
+
// Dust is a variable value around 300-600 satoshis, depending on the configuration.
|
|
807
|
+
// We set a low fee here to not block any transactions, but it might still fail due to "dust".
|
|
808
|
+
const changeValue = valueAccumulator.minus(wrappedFee);
|
|
809
|
+
if (changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) {
|
|
810
|
+
const changeAddressIndex = lastUsedInternalAddress + 1;
|
|
811
|
+
const derivedPublicKey = await this.deriveFromExtendedPublicKey(extendedPublicKey, 1, changeAddressIndex);
|
|
812
|
+
const derivedAddress = await this.getAddressFromPublicKey(derivedPublicKey);
|
|
813
|
+
transaction.outs.push({
|
|
814
|
+
recipient: derivedAddress,
|
|
815
|
+
isChange: true,
|
|
816
|
+
value: changeValue.toString(10),
|
|
817
|
+
derivationPath: changeAddressIndex.toString()
|
|
1256
818
|
});
|
|
1257
|
-
}
|
|
1258
|
-
|
|
819
|
+
}
|
|
820
|
+
return transaction;
|
|
821
|
+
}
|
|
822
|
+
async broadcastTransaction(transaction) {
|
|
823
|
+
const { data } = await index_1.default.post(`${this.options.network.indexerApi}/api/v2/sendtx/`, transaction.transaction);
|
|
824
|
+
return data.result;
|
|
825
|
+
}
|
|
1259
826
|
// Custom
|
|
1260
|
-
|
|
827
|
+
convertExtendedSecretKey(extendedSecretKey, targetFormat) {
|
|
1261
828
|
return (0, key_1.convertExtendedSecretKey)(extendedSecretKey, {
|
|
1262
829
|
format: targetFormat,
|
|
1263
830
|
type: this.keyConfiguration.xpriv.type,
|
|
1264
831
|
hashFunction: this.bitcoinJS.config.network.hashFunctions.address
|
|
1265
832
|
});
|
|
1266
|
-
}
|
|
1267
|
-
|
|
833
|
+
}
|
|
834
|
+
convertExtendedPublicKey(extendedPublicKey, targetFormat) {
|
|
1268
835
|
return (0, key_1.convertExtendedPublicKey)(extendedPublicKey, {
|
|
1269
836
|
format: targetFormat,
|
|
1270
837
|
type: this.keyConfiguration.xpub.type,
|
|
1271
838
|
hashFunction: this.bitcoinJS.config.network.hashFunctions.address
|
|
1272
839
|
});
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
840
|
+
}
|
|
841
|
+
convertCryptoDerivative(derivative) {
|
|
842
|
+
const hexNode = (0, crypto_1.encodeDerivative)('hex', {
|
|
843
|
+
...derivative,
|
|
844
|
+
secretKey: `00${derivative.secretKey}`
|
|
845
|
+
});
|
|
846
|
+
const extendedSecretKey = {
|
|
1277
847
|
type: 'xpriv',
|
|
1278
848
|
format: 'hex',
|
|
1279
849
|
value: hexNode.secretKey
|
|
1280
850
|
};
|
|
1281
851
|
return this.convertExtendedSecretKey(extendedSecretKey, 'encoded');
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
|
|
852
|
+
}
|
|
853
|
+
derivativeToBip32Node(derivative) {
|
|
854
|
+
const extendedSecretKey = this.convertCryptoDerivative(derivative);
|
|
1285
855
|
return this.bitcoinJS.lib.HDNode.fromBase58(extendedSecretKey.value, this.bitcoinJS.config.network);
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
}());
|
|
856
|
+
}
|
|
857
|
+
}
|
|
1289
858
|
exports.BitcoinProtocolImpl = BitcoinProtocolImpl;
|
|
1290
859
|
// Factory
|
|
1291
|
-
function createBitcoinProtocol(options) {
|
|
1292
|
-
if (options === void 0) { options = {}; }
|
|
860
|
+
function createBitcoinProtocol(options = {}) {
|
|
1293
861
|
return new BitcoinProtocolImpl(options);
|
|
1294
862
|
}
|
|
1295
|
-
exports.createBitcoinProtocol = createBitcoinProtocol;
|
|
1296
863
|
exports.BITCOIN_MAINNET_PROTOCOL_NETWORK = {
|
|
1297
864
|
name: 'Mainnet',
|
|
1298
865
|
type: 'mainnet',
|
|
@@ -1300,13 +867,12 @@ exports.BITCOIN_MAINNET_PROTOCOL_NETWORK = {
|
|
|
1300
867
|
blockExplorerUrl: 'https://live.blockcypher.com/btc',
|
|
1301
868
|
indexerApi: 'https://bitcoin.prod.gke.papers.tech'
|
|
1302
869
|
};
|
|
1303
|
-
|
|
1304
|
-
function createBitcoinProtocolOptions(network) {
|
|
1305
|
-
if (network === void 0) { network = {}; }
|
|
870
|
+
const DEFAULT_BITCOIN_PROTOCOL_NETWORK = exports.BITCOIN_MAINNET_PROTOCOL_NETWORK;
|
|
871
|
+
function createBitcoinProtocolOptions(network = {}) {
|
|
1306
872
|
return {
|
|
1307
873
|
network: network.type === 'custom'
|
|
1308
|
-
?
|
|
874
|
+
? { ...DEFAULT_BITCOIN_PROTOCOL_NETWORK, bitcoinjsNetworkName: 'bitcoin', ...network }
|
|
875
|
+
: { ...DEFAULT_BITCOIN_PROTOCOL_NETWORK, ...network }
|
|
1309
876
|
};
|
|
1310
877
|
}
|
|
1311
|
-
exports.createBitcoinProtocolOptions = createBitcoinProtocolOptions;
|
|
1312
878
|
//# sourceMappingURL=BitcoinProtocol.js.map
|