@bitpay-labs/bitcore-wallet-service 11.10.9 → 11.10.11
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/README.md +1 -4
- package/package.json +4 -4
- package/src/externalservices/simplex.ts +15 -3
- package/src/lib/blockchainexplorers/v8/client.ts +14 -0
- package/src/lib/blockchainexplorers/v8.ts +12 -1
- package/src/lib/messagebroker.ts +9 -3
- package/src/lib/model/notification.ts +2 -2
- package/src/lib/routes/helpers/error.ts +1 -0
- package/src/lib/routes/tss.ts +51 -4
- package/src/lib/routes/walletdata.ts +11 -0
- package/src/lib/server.ts +48 -6
- package/src/lib/storage.ts +5 -3
- package/src/lib/tss.ts +251 -34
- package/test/expressapp.test.ts +26 -0
- package/test/integration/externalservices/simplex.test.ts +141 -1
- package/test/integration/server.test.ts +2 -2
- package/test/messagebroker.test.ts +137 -0
- package/test/tss.test.ts +165 -0
- package/test/v8.test.ts +32 -0
- package/ts_build/package.json +4 -4
- package/ts_build/src/externalservices/simplex.d.ts +1 -0
- package/ts_build/src/externalservices/simplex.d.ts.map +1 -1
- package/ts_build/src/externalservices/simplex.js +11 -2
- package/ts_build/src/externalservices/simplex.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8/client.d.ts +1 -0
- package/ts_build/src/lib/blockchainexplorers/v8/client.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8/client.js +13 -0
- package/ts_build/src/lib/blockchainexplorers/v8/client.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts +1 -0
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.js +10 -0
- package/ts_build/src/lib/blockchainexplorers/v8.js.map +1 -1
- package/ts_build/src/lib/messagebroker.d.ts +4 -2
- package/ts_build/src/lib/messagebroker.d.ts.map +1 -1
- package/ts_build/src/lib/messagebroker.js +4 -1
- package/ts_build/src/lib/messagebroker.js.map +1 -1
- package/ts_build/src/lib/model/notification.d.ts +2 -2
- package/ts_build/src/lib/model/notification.d.ts.map +1 -1
- package/ts_build/src/lib/routes/helpers/error.d.ts.map +1 -1
- package/ts_build/src/lib/routes/helpers/error.js +1 -0
- package/ts_build/src/lib/routes/helpers/error.js.map +1 -1
- package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
- package/ts_build/src/lib/routes/tss.js +31 -4
- package/ts_build/src/lib/routes/tss.js.map +1 -1
- package/ts_build/src/lib/routes/walletdata.d.ts.map +1 -1
- package/ts_build/src/lib/routes/walletdata.js +12 -0
- package/ts_build/src/lib/routes/walletdata.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +4 -2
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +40 -5
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +5 -3
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/src/lib/tss.d.ts +20 -4
- package/ts_build/src/lib/tss.d.ts.map +1 -1
- package/ts_build/src/lib/tss.js +155 -17
- package/ts_build/src/lib/tss.js.map +1 -1
- package/ts_build/test/expressapp.test.js +25 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/simplex.test.js +130 -0
- package/ts_build/test/integration/externalservices/simplex.test.js.map +1 -1
- package/ts_build/test/integration/server.test.js +2 -2
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/messagebroker.test.d.ts +2 -0
- package/ts_build/test/messagebroker.test.d.ts.map +1 -0
- package/ts_build/test/messagebroker.test.js +155 -0
- package/ts_build/test/messagebroker.test.js.map +1 -0
- package/ts_build/test/tss.test.d.ts +2 -0
- package/ts_build/test/tss.test.d.ts.map +1 -0
- package/ts_build/test/tss.test.js +190 -0
- package/ts_build/test/tss.test.js.map +1 -0
- package/ts_build/test/v8.test.js +30 -0
- package/ts_build/test/v8.test.js.map +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ More about BWS at https://blog.bitpay.com/announcing-the-bitcore-wallet-suite/
|
|
|
23
23
|
```sh
|
|
24
24
|
git clone https://github.com/bitpay/bitcore.git
|
|
25
25
|
cd bitcore
|
|
26
|
-
npm
|
|
26
|
+
npm ci
|
|
27
27
|
npm run bws
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -86,9 +86,6 @@ There are plenty examples of creating and sending proposals in the `/test/integr
|
|
|
86
86
|
- copay running on port: 8100
|
|
87
87
|
- bitcoin-core running on regtest mode (blue icon logo)
|
|
88
88
|
|
|
89
|
-
> mongo topology crashes sometimes due to notifications being incompatible in a web browser
|
|
90
|
-
> **bitcore-wallet-service/lib/notificationbroadcaster.js**
|
|
91
|
-
> Note: If testing on a PC browser, comment out notificationbroadcaster.js to disable notifications.
|
|
92
89
|
|
|
93
90
|
### Steps:
|
|
94
91
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@bitpay-labs/bitcore-wallet-service",
|
|
3
3
|
"description": "A service for Mutisig HD Bitcoin Wallets",
|
|
4
4
|
"author": "BitPay Inc",
|
|
5
|
-
"version": "11.10.
|
|
5
|
+
"version": "11.10.11",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "ts_build/src/index.js",
|
|
8
8
|
"types": "./ts_build/src/index.d.ts",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@bitpay-labs/bitcore-logging": "^11.10.9",
|
|
45
|
-
"@bitpay-labs/bitcore-tss": "^11.10.
|
|
46
|
-
"@bitpay-labs/crypto-wallet-core": "^11.10.
|
|
45
|
+
"@bitpay-labs/bitcore-tss": "^11.10.11",
|
|
46
|
+
"@bitpay-labs/crypto-wallet-core": "^11.10.11",
|
|
47
47
|
"@sendgrid/mail": "6.5.4",
|
|
48
48
|
"abi-decoder": "2.4.0",
|
|
49
49
|
"async": "0.9.2",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
"email": "kjoseph@bitpay.com"
|
|
108
108
|
}
|
|
109
109
|
],
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "10bcfcf7cc6953e2af65d707b35d3e65e1254583"
|
|
111
111
|
}
|
|
@@ -32,6 +32,18 @@ export class SimplexService {
|
|
|
32
32
|
return keys;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
private simplexGetUserIp(req): string {
|
|
36
|
+
const isWebContext = req.body?.context === 'web';
|
|
37
|
+
let userIp = (isWebContext && req.body.deviceIp) || Utils.getIpFromReq(req);
|
|
38
|
+
delete req.body.deviceIp;
|
|
39
|
+
if (userIp) {
|
|
40
|
+
// Canonicalize IPv4-mapped IPv6 (dual-stack sockets report IPv4 clients as
|
|
41
|
+
// ::ffff:1.2.3.4) so Simplex sees a plain IPv4 address.
|
|
42
|
+
userIp = String(userIp).trim().replace(/^::ffff:/i, '');
|
|
43
|
+
}
|
|
44
|
+
return userIp || '';
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
simplexGetCurrencies(req): Promise<any> {
|
|
36
48
|
return new Promise((resolve, reject) => {
|
|
37
49
|
const keys = this.simplexGetKeys(req);
|
|
@@ -63,11 +75,11 @@ export class SimplexService {
|
|
|
63
75
|
|
|
64
76
|
simplexGetQuote(req): Promise<any> {
|
|
65
77
|
return new Promise((resolve, reject) => {
|
|
78
|
+
const ip = this.simplexGetUserIp(req);
|
|
66
79
|
const keys = this.simplexGetKeys(req);
|
|
67
80
|
|
|
68
81
|
const API = keys.API;
|
|
69
82
|
const API_KEY = keys.API_KEY;
|
|
70
|
-
const ip = Utils.getIpFromReq(req);
|
|
71
83
|
|
|
72
84
|
req.body.client_ip = ip;
|
|
73
85
|
req.body.wallet_id = keys.APP_PROVIDER_ID;
|
|
@@ -147,6 +159,7 @@ export class SimplexService {
|
|
|
147
159
|
|
|
148
160
|
simplexPaymentRequest(req): Promise<any> {
|
|
149
161
|
return new Promise((resolve, reject) => {
|
|
162
|
+
const ip = this.simplexGetUserIp(req);
|
|
150
163
|
const keys = this.simplexGetKeys(req);
|
|
151
164
|
|
|
152
165
|
const API = keys.API;
|
|
@@ -155,7 +168,6 @@ export class SimplexService {
|
|
|
155
168
|
const paymentId = Uuid.v4();
|
|
156
169
|
const orderId = Uuid.v4();
|
|
157
170
|
const apiHost = keys.API;
|
|
158
|
-
const ip = Utils.getIpFromReq(req);
|
|
159
171
|
|
|
160
172
|
if (
|
|
161
173
|
!checkRequired(req.body, ['account_details', 'transaction_details']) &&
|
|
@@ -278,4 +290,4 @@ export class SimplexService {
|
|
|
278
290
|
);
|
|
279
291
|
});
|
|
280
292
|
}
|
|
281
|
-
}
|
|
293
|
+
}
|
|
@@ -61,6 +61,20 @@ export class Client {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
async getBalanceAtTime(params) {
|
|
65
|
+
const { payload, pubKey, time, tokenAddress } = params;
|
|
66
|
+
let url = `${this.baseUrl}/wallet/${pubKey}/balance/${encodeURIComponent(time)}`;
|
|
67
|
+
if (tokenAddress) {
|
|
68
|
+
url += `?tokenAddress=${encodeURIComponent(tokenAddress)}`;
|
|
69
|
+
}
|
|
70
|
+
const signature = this.sign({ method: 'GET', url, payload });
|
|
71
|
+
return request.get(url, {
|
|
72
|
+
headers: { 'x-signature': signature },
|
|
73
|
+
body: payload,
|
|
74
|
+
json: true
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
64
78
|
async getCheckData(params) {
|
|
65
79
|
const { payload, pubKey } = params;
|
|
66
80
|
const url = `${this.baseUrl}/wallet/${pubKey}/check`;
|
|
@@ -178,6 +178,17 @@ export class V8 {
|
|
|
178
178
|
.catch(cb);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
async getBalanceAtTime(wallet: WalletWithOpts, time: string, cb) {
|
|
182
|
+
const client = this._getAuthClient(wallet);
|
|
183
|
+
const { tokenAddress } = wallet;
|
|
184
|
+
client
|
|
185
|
+
.getBalanceAtTime({ pubKey: wallet.beAuthPublicKey2, payload: {}, time, tokenAddress })
|
|
186
|
+
.then(ret => {
|
|
187
|
+
return cb(null, ret);
|
|
188
|
+
})
|
|
189
|
+
.catch(cb);
|
|
190
|
+
}
|
|
191
|
+
|
|
181
192
|
getConnectionInfo() {
|
|
182
193
|
return 'V8 (' + this.chain + '/' + this.v8network + ') @ ' + this.host;
|
|
183
194
|
}
|
|
@@ -788,4 +799,4 @@ export class V8 {
|
|
|
788
799
|
|
|
789
800
|
const getPerformanceKey = (name: string) => {
|
|
790
801
|
return name + '-' + crypto.randomBytes(5).toString('hex');
|
|
791
|
-
};
|
|
802
|
+
};
|
package/src/lib/messagebroker.ts
CHANGED
|
@@ -2,10 +2,12 @@ import { EventEmitter } from 'events';
|
|
|
2
2
|
import * as io from 'socket.io-client';
|
|
3
3
|
import 'source-map-support/register';
|
|
4
4
|
import logger from './logger';
|
|
5
|
+
import type { Notification } from './model/notification';
|
|
5
6
|
|
|
6
7
|
export class MessageBroker extends EventEmitter {
|
|
7
|
-
remote: boolean;
|
|
8
|
+
remote: boolean = false;
|
|
8
9
|
mq: io.Socket;
|
|
10
|
+
|
|
9
11
|
constructor(opts) {
|
|
10
12
|
super();
|
|
11
13
|
|
|
@@ -28,7 +30,7 @@ export class MessageBroker extends EventEmitter {
|
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
send(data) {
|
|
33
|
+
send(data: Notification) {
|
|
32
34
|
if (this.remote) {
|
|
33
35
|
this.mq.emit('msg', data);
|
|
34
36
|
} else {
|
|
@@ -36,7 +38,11 @@ export class MessageBroker extends EventEmitter {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
onMessage(handler) {
|
|
41
|
+
onMessage(handler: (data: Notification) => void) {
|
|
40
42
|
this.on('msg', handler);
|
|
41
43
|
}
|
|
44
|
+
|
|
45
|
+
offMessage(handler: (data: Notification) => void) {
|
|
46
|
+
this.off('msg', handler);
|
|
47
|
+
}
|
|
42
48
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
export interface INotification {
|
|
25
25
|
version: string;
|
|
26
26
|
createdOn: number;
|
|
27
|
-
id: number;
|
|
27
|
+
id: string | number;
|
|
28
28
|
type: string;
|
|
29
29
|
data: any;
|
|
30
30
|
walletId: string;
|
|
@@ -32,7 +32,7 @@ export interface INotification {
|
|
|
32
32
|
isCreator: boolean;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export class Notification {
|
|
35
|
+
export class Notification implements INotification {
|
|
36
36
|
version: string;
|
|
37
37
|
createdOn: number;
|
|
38
38
|
id: string | number;
|
|
@@ -13,6 +13,7 @@ export class ApiErrorHelper {
|
|
|
13
13
|
returnError(err: any, res: express.Response, req: express.Request): void {
|
|
14
14
|
// make sure headers have not been sent as this leads to an uncaught error
|
|
15
15
|
if (res.headersSent) {
|
|
16
|
+
res.end();
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
if (err instanceof ClientError) {
|
package/src/lib/routes/tss.ts
CHANGED
|
@@ -37,17 +37,40 @@ export class TssRouter {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
router.get('/v1/tss/keygen/:id/:round', authTssRequest(), async function(req, res) {
|
|
40
|
+
let interval: NodeJS.Timeout;
|
|
40
41
|
try {
|
|
42
|
+
const abort = new AbortController();
|
|
43
|
+
res.on('close', () => { clearInterval(interval); abort.abort(); });
|
|
44
|
+
|
|
41
45
|
const { id, round } = req.params as { [key: string]: string };
|
|
46
|
+
const { maxWaitTime } = req.query as { [key: string]: string };
|
|
42
47
|
const copayerId = req.headers['x-identity'];
|
|
43
48
|
if (round === 'secret') {
|
|
44
49
|
const secret = await TssKeyGen.getBwsJoinSecret({ id, copayerId });
|
|
45
50
|
return res.json({ secret });
|
|
46
51
|
}
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
|
|
53
|
+
// Validate access and fetch session before committing to a streaming response, so that errors like
|
|
54
|
+
// "session not found" or "not a participant" can still be returned with a proper
|
|
55
|
+
// HTTP status instead of silently becoming an empty 200 (see below).
|
|
56
|
+
const session = await TssKeyGen.getSessionForCopayer({ id, copayerId });
|
|
57
|
+
|
|
58
|
+
if (abort.signal.aborted || res.closed) {
|
|
59
|
+
return; // Connection closed by client, stop processing
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Keep the connection alive while waiting for the change stream to return a result.
|
|
63
|
+
// Headers must be finalized before writing the first heartbeat byte.
|
|
64
|
+
// Flush ensures the heartbeat is sent immediately to keep the connection alive.
|
|
65
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
66
|
+
interval = setInterval(() => { res.write('\n'); res.flush(); }, 1000);
|
|
67
|
+
|
|
68
|
+
const { messages, publicKey } = await TssKeyGen.getMessagesForParty({ session, round: parseInt(round), copayerId, maxWaitTimeSec: parseInt(maxWaitTime), abortSignal: abort.signal });
|
|
69
|
+
return res.end(JSON.stringify({ messages, publicKey }));
|
|
49
70
|
} catch (err) {
|
|
50
71
|
return returnError(err ?? 'unknown', res, req);
|
|
72
|
+
} finally {
|
|
73
|
+
clearInterval(interval);
|
|
51
74
|
}
|
|
52
75
|
});
|
|
53
76
|
|
|
@@ -104,13 +127,37 @@ export class TssRouter {
|
|
|
104
127
|
});
|
|
105
128
|
|
|
106
129
|
router.get('/v1/tss/sign/:id/:round', authTssRequest(), async function(req, res) {
|
|
130
|
+
let interval: NodeJS.Timeout;
|
|
107
131
|
try {
|
|
132
|
+
const abort = new AbortController();
|
|
133
|
+
res.on('close', () => { clearInterval(interval); abort.abort(); });
|
|
134
|
+
|
|
108
135
|
const { id, round } = req.params as { [key: string]: string };
|
|
136
|
+
const { maxWaitTime } = req.query as { [key: string]: string };
|
|
109
137
|
const copayerId = req.headers['x-identity'];
|
|
110
|
-
|
|
111
|
-
|
|
138
|
+
|
|
139
|
+
// Validate access and fetch session before committing to a streaming response, so that errors like
|
|
140
|
+
// "session not found" or "not a participant" can still be returned with a proper
|
|
141
|
+
// HTTP status instead of silently becoming an empty 200 (see below).
|
|
142
|
+
const session = await TssSign.getSessionForCopayer({ id, copayerId });
|
|
143
|
+
|
|
144
|
+
if (abort.signal.aborted || res.closed) {
|
|
145
|
+
return; // Connection closed by client, stop processing
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Keep the connection alive while waiting for the change stream to return a result.
|
|
149
|
+
// Headers must be finalized before writing the first heartbeat byte.
|
|
150
|
+
// Flush ensures the heartbeat is sent immediately to keep the connection alive.
|
|
151
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
152
|
+
interval = setInterval(() => { res.write('\n'); res.flush(); }, 1000);
|
|
153
|
+
|
|
154
|
+
const { messages, signature, participants } = await TssSign.getMessagesForParty({ session, round: parseInt(round), copayerId, maxWaitTimeSec: parseInt(maxWaitTime), abortSignal: abort.signal });
|
|
155
|
+
clearInterval(interval);
|
|
156
|
+
return res.end(JSON.stringify({ messages, signature, participants }));
|
|
112
157
|
} catch (err) {
|
|
113
158
|
return returnError(err ?? 'unknown', res, req);
|
|
159
|
+
} finally {
|
|
160
|
+
clearInterval(interval);
|
|
114
161
|
}
|
|
115
162
|
});
|
|
116
163
|
|
|
@@ -108,6 +108,17 @@ export function registerWalletDataRoutes(router: express.Router, context: RouteC
|
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
router.get('/v1/balance/:time/', (req, res) => {
|
|
112
|
+
getServerWithAuth(req, res, server => {
|
|
113
|
+
const opts: { time: string; tokenAddress?: string } = { time: req.params.time };
|
|
114
|
+
if (req.query.tokenAddress) opts.tokenAddress = req.query.tokenAddress as string;
|
|
115
|
+
server.getBalanceAtTime(opts, (err, balance) => {
|
|
116
|
+
if (err) return returnError(err, res, req);
|
|
117
|
+
res.json(balance);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
111
122
|
router.get('/v1/sendmaxinfo/', (req, res) => {
|
|
112
123
|
getServerWithAuth(req, res, server => {
|
|
113
124
|
const query = req.query;
|
package/src/lib/server.ts
CHANGED
|
@@ -326,12 +326,9 @@ export class WalletService implements IWalletService {
|
|
|
326
326
|
);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
static handleIncomingNotifications(
|
|
330
|
-
cb = cb || function() { };
|
|
331
|
-
|
|
329
|
+
static handleIncomingNotifications(_notification: INotification) {
|
|
332
330
|
// do nothing here....
|
|
333
331
|
// bc height cache is cleared on bcmonitor
|
|
334
|
-
return cb();
|
|
335
332
|
}
|
|
336
333
|
|
|
337
334
|
static shutDown(cb) {
|
|
@@ -474,6 +471,13 @@ export class WalletService implements IWalletService {
|
|
|
474
471
|
return storage;
|
|
475
472
|
}
|
|
476
473
|
|
|
474
|
+
static getMessageBroker() {
|
|
475
|
+
if (!initialized) {
|
|
476
|
+
throw new Error('Message broker requested before server was initialized');
|
|
477
|
+
}
|
|
478
|
+
return messageBroker;
|
|
479
|
+
}
|
|
480
|
+
|
|
477
481
|
_runLocked(cb, task, waitTime?: number) {
|
|
478
482
|
$.checkState(this.walletId, 'Failed state: this.walletId undefined at <_runLocked()>');
|
|
479
483
|
this.lock.runLocked(this.walletId, { waitTime }, cb, task);
|
|
@@ -1152,7 +1156,7 @@ export class WalletService implements IWalletService {
|
|
|
1152
1156
|
try {
|
|
1153
1157
|
const isValid = this._verifyRequestPubKey(opts.requestPubKey, opts.signature, target.xPubKey);
|
|
1154
1158
|
if (!isValid) return cb(Errors.NOT_AUTHORIZED);
|
|
1155
|
-
} catch
|
|
1159
|
+
} catch {
|
|
1156
1160
|
return cb(Errors.NOT_AUTHORIZED);
|
|
1157
1161
|
}
|
|
1158
1162
|
|
|
@@ -2088,6 +2092,44 @@ export class WalletService implements IWalletService {
|
|
|
2088
2092
|
});
|
|
2089
2093
|
}
|
|
2090
2094
|
|
|
2095
|
+
/**
|
|
2096
|
+
* Get wallet balance at a specific time.
|
|
2097
|
+
* @param {Object} opts
|
|
2098
|
+
* @param {string} opts.time - Date or time accepted by the bitcore-node API.
|
|
2099
|
+
* @returns {Object} balance - The chain-state provider balance at the requested time.
|
|
2100
|
+
*/
|
|
2101
|
+
getBalanceAtTime(opts, cb) {
|
|
2102
|
+
opts = opts || {};
|
|
2103
|
+
if (!opts.time) {
|
|
2104
|
+
return cb(new ClientError('time is required in getBalanceAtTime'));
|
|
2105
|
+
}
|
|
2106
|
+
let wallet = opts.wallet;
|
|
2107
|
+
|
|
2108
|
+
const setWallet = cb1 => {
|
|
2109
|
+
if (wallet) return cb1();
|
|
2110
|
+
this.getWallet({}, (err, ret) => {
|
|
2111
|
+
if (err) return cb(err);
|
|
2112
|
+
wallet = ret;
|
|
2113
|
+
return cb1(null, wallet);
|
|
2114
|
+
});
|
|
2115
|
+
};
|
|
2116
|
+
|
|
2117
|
+
setWallet(() => {
|
|
2118
|
+
if (!wallet.isComplete()) {
|
|
2119
|
+
return cb(null, { confirmed: 0, unconfirmed: 0, balance: 0 });
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
this.syncWallet(wallet, err => {
|
|
2123
|
+
if (err) return cb(err);
|
|
2124
|
+
const bc = this._getBlockchainExplorer(wallet.chain, wallet.network);
|
|
2125
|
+
if (!bc) {
|
|
2126
|
+
return cb(new Error('Could not get blockchain explorer instance'));
|
|
2127
|
+
}
|
|
2128
|
+
return bc.getBalanceAtTime({ ...wallet, tokenAddress: opts.tokenAddress }, opts.time, cb);
|
|
2129
|
+
});
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2091
2133
|
/**
|
|
2092
2134
|
* Return info needed to send all funds in the wallet
|
|
2093
2135
|
* @param {Object} opts
|
|
@@ -3857,7 +3899,7 @@ export class WalletService implements IWalletService {
|
|
|
3857
3899
|
const notifications = res
|
|
3858
3900
|
.flat()
|
|
3859
3901
|
.map((n: INotification) => ({ ...n, walletId: this.walletId }))
|
|
3860
|
-
.sort((a, b) => a.id
|
|
3902
|
+
.sort((a, b) => a.id?.toString()?.localeCompare(b.id?.toString()));
|
|
3861
3903
|
|
|
3862
3904
|
return cb(null, notifications);
|
|
3863
3905
|
}
|
package/src/lib/storage.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as async from 'async';
|
|
2
2
|
import _ from 'lodash';
|
|
3
|
-
import { Db } from 'mongodb';
|
|
4
3
|
import * as mongodb from 'mongodb';
|
|
5
4
|
import preconditions from 'preconditions';
|
|
6
5
|
import { BCHAddressTranslator } from './bchaddresstranslator'; // only for migration
|
|
@@ -19,8 +18,11 @@ import {
|
|
|
19
18
|
TxProposal,
|
|
20
19
|
Wallet
|
|
21
20
|
} from './model';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
21
|
+
import { TssKeyGenModel } from './model/tsskeygen';
|
|
22
|
+
import { TssSigGenModel } from './model/tsssign';
|
|
23
|
+
import type { ITssKeyMessageObject } from './model/tsskeygen';
|
|
24
|
+
import type { ITssSigMessageObject } from './model/tsssign';
|
|
25
|
+
import type { Db } from 'mongodb';
|
|
24
26
|
|
|
25
27
|
const $ = preconditions.singleton();
|
|
26
28
|
|