@bitpay-labs/bitcore-wallet-service 11.10.2 → 11.10.4
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/.mocharc.yml +2 -0
- package/.nycrc.yml +13 -0
- package/bws.example.config.js +4 -3
- package/package.json +6 -25
- package/src/config.ts +4 -3
- package/src/externalservices/moonpay.ts +10 -0
- package/src/externalservices/transak.ts +15 -0
- package/src/lib/blockchainexplorers/v8.ts +19 -0
- package/src/lib/chain/xrp/index.ts +13 -8
- package/src/lib/common/defaults.ts +3 -0
- package/src/lib/common/utils.ts +3 -3
- package/src/lib/emailservice.ts +2 -1
- package/src/lib/model/txproposal.ts +4 -0
- package/src/lib/pushnotificationsservice.ts +2 -1
- package/src/lib/routes/moralis.ts +12 -0
- package/src/lib/routes/services.ts +22 -6
- package/src/lib/routes/tss.ts +1 -1
- package/src/lib/routes/wallets.ts +16 -1
- package/src/lib/server.ts +96 -2
- package/test/chain/xrp.test.ts +21 -0
- package/test/integration/externalservices/moonpay.test.ts +14 -2
- package/test/integration/externalservices/moralis.test.ts +476 -0
- package/test/integration/server.test.ts +138 -0
- package/ts_build/package.json +5 -24
- package/ts_build/src/config.d.ts.map +1 -1
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
- package/ts_build/src/externalservices/moonpay.js +6 -0
- package/ts_build/src/externalservices/moonpay.js.map +1 -1
- package/ts_build/src/externalservices/transak.d.ts.map +1 -1
- package/ts_build/src/externalservices/transak.js +15 -0
- package/ts_build/src/externalservices/transak.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts +3 -0
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.js +18 -0
- package/ts_build/src/lib/blockchainexplorers/v8.js.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.js +12 -8
- package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
- package/ts_build/src/lib/common/defaults.d.ts +1 -0
- package/ts_build/src/lib/common/defaults.d.ts.map +1 -1
- package/ts_build/src/lib/common/defaults.js +1 -0
- package/ts_build/src/lib/common/defaults.js.map +1 -1
- package/ts_build/src/lib/common/index.d.ts +2 -1
- package/ts_build/src/lib/common/index.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.d.ts +1 -1
- package/ts_build/src/lib/common/utils.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.js.map +1 -1
- package/ts_build/src/lib/emailservice.d.ts.map +1 -1
- package/ts_build/src/lib/emailservice.js +1 -1
- package/ts_build/src/lib/emailservice.js.map +1 -1
- package/ts_build/src/lib/model/txproposal.d.ts +2 -0
- package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposal.js +3 -0
- package/ts_build/src/lib/model/txproposal.js.map +1 -1
- package/ts_build/src/lib/pushnotificationsservice.d.ts.map +1 -1
- package/ts_build/src/lib/pushnotificationsservice.js +1 -1
- package/ts_build/src/lib/pushnotificationsservice.js.map +1 -1
- package/ts_build/src/lib/routes/moralis.d.ts.map +1 -1
- package/ts_build/src/lib/routes/moralis.js +10 -0
- package/ts_build/src/lib/routes/moralis.js.map +1 -1
- package/ts_build/src/lib/routes/services.d.ts.map +1 -1
- package/ts_build/src/lib/routes/services.js +22 -6
- package/ts_build/src/lib/routes/services.js.map +1 -1
- package/ts_build/src/lib/routes/tss.js +0 -1
- package/ts_build/src/lib/routes/tss.js.map +1 -1
- package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
- package/ts_build/src/lib/routes/wallets.js +15 -0
- package/ts_build/src/lib/routes/wallets.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +5 -0
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +83 -2
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/test/chain/xrp.test.js +19 -0
- package/ts_build/test/chain/xrp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/moonpay.test.js +14 -2
- package/ts_build/test/integration/externalservices/moonpay.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/moralis.test.d.ts +2 -0
- package/ts_build/test/integration/externalservices/moralis.test.d.ts.map +1 -0
- package/ts_build/test/integration/externalservices/moralis.test.js +455 -0
- package/ts_build/test/integration/externalservices/moralis.test.js.map +1 -0
- package/ts_build/test/integration/server.test.js +115 -0
- package/ts_build/test/integration/server.test.js.map +1 -1
package/.mocharc.yml
ADDED
package/.nycrc.yml
ADDED
package/bws.example.config.js
CHANGED
|
@@ -325,7 +325,7 @@ module.exports = {
|
|
|
325
325
|
// publicKey: 'simplex_web_public_key_here',
|
|
326
326
|
// }
|
|
327
327
|
// },
|
|
328
|
-
// thorswap
|
|
328
|
+
// thorswap: {
|
|
329
329
|
// sandbox: {
|
|
330
330
|
// api: 'https://dev-api.thorswap.net',
|
|
331
331
|
// apiKey: 'thorswap_sandbox_api_key_here',
|
|
@@ -339,7 +339,7 @@ module.exports = {
|
|
|
339
339
|
// referer: 'thorswap_production_referer_here'
|
|
340
340
|
// },
|
|
341
341
|
// },
|
|
342
|
-
// transak
|
|
342
|
+
// transak: {
|
|
343
343
|
// sandbox: {
|
|
344
344
|
// api: 'https://api-stg.transak.com',
|
|
345
345
|
// widgetApi: 'https://api-gateway-stg.transak.com',
|
|
@@ -363,7 +363,8 @@ module.exports = {
|
|
|
363
363
|
// widgetApi: 'https://api-gateway.transak.com',
|
|
364
364
|
// apiKey: 'transak_production_web_api_key_here',
|
|
365
365
|
// secretKey: 'transak_production_web_secret_key_here',
|
|
366
|
-
// }
|
|
366
|
+
// },
|
|
367
|
+
// whitelist: []
|
|
367
368
|
// },
|
|
368
369
|
// wyre: {
|
|
369
370
|
// sandbox: {
|
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.4",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "ts_build/src/index.js",
|
|
8
8
|
"types": "./ts_build/src/index.d.ts",
|
|
@@ -24,9 +24,7 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"start": "npm run clean && npm run build && ./start.sh",
|
|
26
26
|
"stop": "./stop.sh",
|
|
27
|
-
"
|
|
28
|
-
"test": "npm run compile && mocha --exit 'ts_build/test/**/*.test.js'",
|
|
29
|
-
"test:ci": "BWS_LOG_LEVEL=none npm run test",
|
|
27
|
+
"test": "npm run compile && nyc mocha 'ts_build/test/**/*.test.js'",
|
|
30
28
|
"build": "tsc && npm run cp:templates",
|
|
31
29
|
"build:prod": "tsc -p tsconfig.prod.json && npm run cp:templates",
|
|
32
30
|
"build:docker": "docker build -t bitpay/bitcore-wallet-service:latest -f ./Dockerfile ../..",
|
|
@@ -43,9 +41,9 @@
|
|
|
43
41
|
"cp:templates": "cp -r ./templates ./ts_build/templates"
|
|
44
42
|
},
|
|
45
43
|
"dependencies": {
|
|
46
|
-
"@bitpay-labs/bitcore-logging": "^11.
|
|
47
|
-
"@bitpay-labs/bitcore-tss": "^11.10.
|
|
48
|
-
"@bitpay-labs/crypto-wallet-core": "^11.10.
|
|
44
|
+
"@bitpay-labs/bitcore-logging": "^11.10.4",
|
|
45
|
+
"@bitpay-labs/bitcore-tss": "^11.10.4",
|
|
46
|
+
"@bitpay-labs/crypto-wallet-core": "^11.10.4",
|
|
49
47
|
"@sendgrid/mail": "6.5.4",
|
|
50
48
|
"abi-decoder": "2.4.0",
|
|
51
49
|
"async": "0.9.2",
|
|
@@ -86,27 +84,10 @@
|
|
|
86
84
|
"@types/request": "^2.48.1",
|
|
87
85
|
"chai": "^5.2.0",
|
|
88
86
|
"memdown": "^1.0.0",
|
|
89
|
-
"mocha": "^5.2.0",
|
|
90
|
-
"nyc": "^17.1.0",
|
|
91
87
|
"sinon": "^7.1.1",
|
|
92
88
|
"supertest": "*",
|
|
93
89
|
"typescript": "^5.7.3"
|
|
94
90
|
},
|
|
95
|
-
"nyc": {
|
|
96
|
-
"include": [
|
|
97
|
-
"ts_build/src",
|
|
98
|
-
"src"
|
|
99
|
-
],
|
|
100
|
-
"reporter": [
|
|
101
|
-
"html",
|
|
102
|
-
"text-summary"
|
|
103
|
-
],
|
|
104
|
-
"check-coverage": true,
|
|
105
|
-
"lines": 60,
|
|
106
|
-
"functions": 60,
|
|
107
|
-
"branches": 60,
|
|
108
|
-
"statements": 60
|
|
109
|
-
},
|
|
110
91
|
"bitcoreNode": "./bitcorenode",
|
|
111
92
|
"contributors": [
|
|
112
93
|
{
|
|
@@ -126,5 +107,5 @@
|
|
|
126
107
|
"email": "kjoseph@bitpay.com"
|
|
127
108
|
}
|
|
128
109
|
],
|
|
129
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "3cdd407df1a56d452b243083c884735757c10a2a"
|
|
130
111
|
}
|
package/src/config.ts
CHANGED
|
@@ -400,7 +400,7 @@ const Config = (): any => {
|
|
|
400
400
|
// publicKey: 'simplex_web_public_key_here',
|
|
401
401
|
// }
|
|
402
402
|
// },
|
|
403
|
-
// thorswap
|
|
403
|
+
// thorswap: {
|
|
404
404
|
// sandbox: {
|
|
405
405
|
// api: 'https://dev-api.thorswap.net',
|
|
406
406
|
// apiKey: 'thorswap_sandbox_api_key_here',
|
|
@@ -414,7 +414,7 @@ const Config = (): any => {
|
|
|
414
414
|
// referer: 'thorswap_production_referer_here'
|
|
415
415
|
// },
|
|
416
416
|
// },
|
|
417
|
-
// transak
|
|
417
|
+
// transak: {
|
|
418
418
|
// sandbox: {
|
|
419
419
|
// api: 'https://api-stg.transak.com',
|
|
420
420
|
// widgetApi: 'https://global-stg.transak.com',
|
|
@@ -438,7 +438,8 @@ const Config = (): any => {
|
|
|
438
438
|
// widgetApi: 'https://global.transak.com',
|
|
439
439
|
// apiKey: 'transak_production_web_api_key_here',
|
|
440
440
|
// secretKey: 'transak_production_web_secret_key_here',
|
|
441
|
-
// }
|
|
441
|
+
// },
|
|
442
|
+
// whitelist: []
|
|
442
443
|
// },
|
|
443
444
|
// wyre: {
|
|
444
445
|
// sandbox: {
|
|
@@ -225,6 +225,16 @@ export class MoonpayService {
|
|
|
225
225
|
if (req.body.paymentMethod) qs.push('paymentMethod=' + encodeURIComponent(req.body.paymentMethod));
|
|
226
226
|
if (req.body.areFeesIncluded) qs.push('areFeesIncluded=' + encodeURIComponent(req.body.areFeesIncluded));
|
|
227
227
|
|
|
228
|
+
const deviceIp = Utils.getIpFromReq(req);
|
|
229
|
+
if (!deviceIp) {
|
|
230
|
+
throw new ClientError('Could not determine device IP address');
|
|
231
|
+
}
|
|
232
|
+
const allowedIpAddress: string = Bitcore.crypto.Hash.sha256hmac(
|
|
233
|
+
Buffer.from(deviceIp),
|
|
234
|
+
Buffer.from(SECRET_KEY)
|
|
235
|
+
).toString('base64');
|
|
236
|
+
qs.push('allowedIpAddress=' + encodeURIComponent(allowedIpAddress));
|
|
237
|
+
|
|
228
238
|
const URL_SEARCH: string = `?${qs.join('&')}`;
|
|
229
239
|
|
|
230
240
|
const URLSignatureHash: string = Bitcore.crypto.Hash.sha256hmac(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as request from 'request';
|
|
2
2
|
import config from '../config';
|
|
3
|
+
import { Utils } from '../lib/common/utils';
|
|
3
4
|
import { ClientError } from '../lib/errors/clienterror';
|
|
4
5
|
import { checkRequired } from '../lib/server';
|
|
5
6
|
|
|
@@ -50,6 +51,8 @@ export class TransakService {
|
|
|
50
51
|
const headers = {
|
|
51
52
|
'Content-Type': 'application/json',
|
|
52
53
|
'api-secret': SECRET_KEY,
|
|
54
|
+
'x-api-key': API_KEY,
|
|
55
|
+
'x-user-ip': Utils.getIpFromReq(req),
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
const body = {
|
|
@@ -85,10 +88,13 @@ export class TransakService {
|
|
|
85
88
|
return reject(err);
|
|
86
89
|
}
|
|
87
90
|
const API = keys.API;
|
|
91
|
+
const API_KEY = keys.API_KEY;
|
|
88
92
|
|
|
89
93
|
const headers = {
|
|
90
94
|
'Accept': 'application/json',
|
|
91
95
|
'Content-Type': 'application/json',
|
|
96
|
+
'x-api-key': API_KEY,
|
|
97
|
+
'x-user-ip': Utils.getIpFromReq(req),
|
|
92
98
|
};
|
|
93
99
|
|
|
94
100
|
const URL: string = API + '/api/v2/currencies/crypto-currencies';
|
|
@@ -124,6 +130,8 @@ export class TransakService {
|
|
|
124
130
|
const headers = {
|
|
125
131
|
'Accept': 'application/json',
|
|
126
132
|
'Content-Type': 'application/json',
|
|
133
|
+
'x-api-key': API_KEY,
|
|
134
|
+
'x-user-ip': Utils.getIpFromReq(req),
|
|
127
135
|
};
|
|
128
136
|
|
|
129
137
|
const URL: string = API + `/api/v2/currencies/fiat-currencies?apiKey=${API_KEY}`;
|
|
@@ -162,6 +170,8 @@ export class TransakService {
|
|
|
162
170
|
|
|
163
171
|
const headers = {
|
|
164
172
|
Accept: 'application/json',
|
|
173
|
+
'x-api-key': API_KEY,
|
|
174
|
+
'x-user-ip': Utils.getIpFromReq(req),
|
|
165
175
|
};
|
|
166
176
|
|
|
167
177
|
const qs: string[] = [];
|
|
@@ -227,6 +237,8 @@ export class TransakService {
|
|
|
227
237
|
Accept: 'application/json',
|
|
228
238
|
'Content-Type': 'application/json',
|
|
229
239
|
'access-token': req.body.accessToken,
|
|
240
|
+
'x-api-key': API_KEY,
|
|
241
|
+
'x-user-ip': Utils.getIpFromReq(req),
|
|
230
242
|
};
|
|
231
243
|
|
|
232
244
|
const body = {
|
|
@@ -266,6 +278,7 @@ export class TransakService {
|
|
|
266
278
|
return reject(err);
|
|
267
279
|
}
|
|
268
280
|
const API = keys.API;
|
|
281
|
+
const API_KEY = keys.API_KEY;
|
|
269
282
|
|
|
270
283
|
if (!checkRequired(req.body, ['orderId', 'accessToken'])) {
|
|
271
284
|
return reject(new ClientError("Transak's request missing arguments"));
|
|
@@ -274,6 +287,8 @@ export class TransakService {
|
|
|
274
287
|
const headers = {
|
|
275
288
|
Accept: 'application/json',
|
|
276
289
|
'access-token': req.body.accessToken,
|
|
290
|
+
'x-api-key': API_KEY,
|
|
291
|
+
'x-user-ip': Utils.getIpFromReq(req),
|
|
277
292
|
};
|
|
278
293
|
|
|
279
294
|
const URL: string = API + `/partners/api/v2/order/${req.body.orderId}`;
|
|
@@ -43,6 +43,13 @@ function v8network(bwsNetwork, chain = 'btc') {
|
|
|
43
43
|
return bwsNetwork;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function scrubResponseError(err) {
|
|
47
|
+
if (err.response) {
|
|
48
|
+
return new Error(`HTTP ${err.response.statusCode} - ${err.response.statusMessage}: ${err.response.body}\n${err.response.request.method} ${err.response.request.href}`);
|
|
49
|
+
}
|
|
50
|
+
return new Error(err);
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
export type WalletWithOpts = IWallet & { tokenAddress?: string; multisigContractAddress?: string };
|
|
47
54
|
|
|
48
55
|
export class V8 {
|
|
@@ -765,6 +772,18 @@ export class V8 {
|
|
|
765
772
|
return callbacks.onIncomingPayments(notification);
|
|
766
773
|
});
|
|
767
774
|
}
|
|
775
|
+
|
|
776
|
+
async getFlags(opts: { address: string }) {
|
|
777
|
+
try {
|
|
778
|
+
const url = `${this.baseUrl}/address/${opts.address}/flags`;
|
|
779
|
+
const ret = await this.request.get(url);
|
|
780
|
+
return JSON.parse(ret).flags;
|
|
781
|
+
} catch (err) {
|
|
782
|
+
const e = scrubResponseError(err);
|
|
783
|
+
logger.error(`Error getting flags for address ${opts.address}: %o`, e);
|
|
784
|
+
throw e;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
768
787
|
}
|
|
769
788
|
|
|
770
789
|
const getPerformanceKey = (name: string) => {
|
|
@@ -90,14 +90,19 @@ export class XrpChain implements IChain {
|
|
|
90
90
|
getWalletSendMaxInfo(server, wallet, opts, cb) {
|
|
91
91
|
server.getBalance({}, (err, balance) => {
|
|
92
92
|
if (err) return cb(err);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
|
|
94
|
+
server._getFeePerKb(wallet, opts, (err, feePerKb) => {
|
|
95
|
+
if (err) return cb(err);
|
|
96
|
+
|
|
97
|
+
const { availableAmount } = balance;
|
|
98
|
+
const fee = feePerKb;
|
|
99
|
+
return cb(null, {
|
|
100
|
+
utxosBelowFee: 0,
|
|
101
|
+
amountBelowFee: 0,
|
|
102
|
+
amount: availableAmount - fee,
|
|
103
|
+
feePerKb,
|
|
104
|
+
fee
|
|
105
|
+
});
|
|
101
106
|
});
|
|
102
107
|
});
|
|
103
108
|
}
|
|
@@ -19,6 +19,9 @@ export const Defaults = {
|
|
|
19
19
|
// TODO: should allow different gap sizes for external/internal chains
|
|
20
20
|
SCAN_ADDRESS_GAP: 30,
|
|
21
21
|
|
|
22
|
+
// Lookback window (in seconds) for scanning broadcasted txps during nonce assignment
|
|
23
|
+
BROADCASTED_NONCE_SCAN_WINDOW: 24 * 3600,
|
|
24
|
+
|
|
22
25
|
FEE_LEVELS: {
|
|
23
26
|
btc: [
|
|
24
27
|
{
|
package/src/lib/common/utils.ts
CHANGED
|
@@ -81,11 +81,11 @@ export const Utils = {
|
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
*
|
|
84
|
-
* @desc rounds a
|
|
85
|
-
* @param number
|
|
84
|
+
* @desc rounds a Javascript number
|
|
85
|
+
* @param {number} number
|
|
86
86
|
* @return {number}
|
|
87
87
|
*/
|
|
88
|
-
strip(number) {
|
|
88
|
+
strip(number: number): number {
|
|
89
89
|
return parseFloat(number.toPrecision(12));
|
|
90
90
|
},
|
|
91
91
|
|
package/src/lib/emailservice.ts
CHANGED
|
@@ -561,7 +561,8 @@ export class EmailService {
|
|
|
561
561
|
_checkShouldSendEmail(notification: Notification, cb) {
|
|
562
562
|
if (notification.type != 'NewTxProposal') return cb(null, true);
|
|
563
563
|
this.storage.fetchWallet(notification.walletId, (err, wallet) => {
|
|
564
|
-
|
|
564
|
+
// TSS wallets are stored as m=1 but still need other signers
|
|
565
|
+
return cb(err, wallet && (wallet.m > 1 || !!wallet.tssKeyId));
|
|
565
566
|
});
|
|
566
567
|
}
|
|
567
568
|
|
|
@@ -81,6 +81,7 @@ export interface ITxProposal<NumberType = number> {
|
|
|
81
81
|
multisigTxId?: string;
|
|
82
82
|
destinationTag?: string;
|
|
83
83
|
invoiceID?: string;
|
|
84
|
+
flags?: string; // Comma-delimited list of XRP account flags to set (e.g. "tfRequireDestTag,tfDisallowXRP")
|
|
84
85
|
lockUntilBlockHeight?: NumberType;
|
|
85
86
|
instantAcceptanceEscrow?: NumberType;
|
|
86
87
|
isTokenSwap?: boolean;
|
|
@@ -175,6 +176,7 @@ export class TxProposal<NumberType = number> implements ITxProposal<NumberType>
|
|
|
175
176
|
multisigTxId?: string;
|
|
176
177
|
destinationTag?: string;
|
|
177
178
|
invoiceID?: string;
|
|
179
|
+
flags?: string; // Comma-delimited list of XRP account flags to set (e.g. "tfRequireDestTag,tfDisallowXRP")
|
|
178
180
|
lockUntilBlockHeight?: NumberType;
|
|
179
181
|
instantAcceptanceEscrow?: NumberType;
|
|
180
182
|
isTokenSwap?: boolean;
|
|
@@ -305,6 +307,7 @@ export class TxProposal<NumberType = number> implements ITxProposal<NumberType>
|
|
|
305
307
|
x.destinationTag = opts.destinationTag;
|
|
306
308
|
x.invoiceID = opts.invoiceID;
|
|
307
309
|
x.multiTx = opts.multiTx; // proposal contains multiple transactions
|
|
310
|
+
x.flags = opts.flags; // XRP account flags to set
|
|
308
311
|
|
|
309
312
|
// SOL
|
|
310
313
|
x.space = opts.space; // space to allocate for account creation
|
|
@@ -400,6 +403,7 @@ export class TxProposal<NumberType = number> implements ITxProposal<NumberType>
|
|
|
400
403
|
x.destinationTag = obj.destinationTag;
|
|
401
404
|
x.invoiceID = obj.invoiceID;
|
|
402
405
|
x.multiTx = obj.multiTx;
|
|
406
|
+
x.flags = obj.flags; // XRP account flags to set
|
|
403
407
|
|
|
404
408
|
// SOL
|
|
405
409
|
x.space = obj.space; // space to allocate for account creation
|
|
@@ -358,7 +358,8 @@ export class PushNotificationsService {
|
|
|
358
358
|
_checkShouldSendNotif(notification, cb) {
|
|
359
359
|
if (notification.type != 'NewTxProposal') return cb(null, true);
|
|
360
360
|
this.storage.fetchWallet(notification.walletId, (err, wallet) => {
|
|
361
|
-
|
|
361
|
+
// TSS wallets are stored as m=1 but still need other signers
|
|
362
|
+
return cb(err, wallet && (wallet.m > 1 || !!wallet.tssKeyId));
|
|
362
363
|
});
|
|
363
364
|
}
|
|
364
365
|
|
|
@@ -86,4 +86,16 @@ export function registerMoralisRoutes(router: express.Router, context: RouteCont
|
|
|
86
86
|
return server.moralisGetSolWalletPortfolio(req);
|
|
87
87
|
});
|
|
88
88
|
});
|
|
89
|
+
|
|
90
|
+
router.post('/v1/moralis/getTransactionVerbose', cors(moralisCorsOptions), (req, res) => {
|
|
91
|
+
respondWithPublicServer(req, res, context, server => {
|
|
92
|
+
return server.moralisGetTransactionVerbose(req);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
router.post('/v1/moralis/getMultipleSolTokenPrices', cors(moralisCorsOptions), (req, res) => {
|
|
97
|
+
respondWithPublicServer(req, res, context, server => {
|
|
98
|
+
return server.moralisGetMultipleSolTokenPrices(req);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
89
101
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import cors from 'cors';
|
|
1
2
|
import express from 'express';
|
|
2
3
|
import rp from 'request-promise-native';
|
|
4
|
+
import config from '../../config';
|
|
3
5
|
import { Common } from '../common';
|
|
4
6
|
import type * as Types from '../../types/expressapp';
|
|
5
7
|
import type { WalletService } from '../server';
|
|
@@ -65,6 +67,20 @@ function respondWithAuthServer(req, res, context: RouteContext, handler: RouteHa
|
|
|
65
67
|
export function registerServiceRoutes(router: express.Router, context: RouteContext) {
|
|
66
68
|
const { getServerWithAuth, setPublicCache, returnError } = context;
|
|
67
69
|
|
|
70
|
+
const transakCorsOptions = {
|
|
71
|
+
origin: (origin, cb) => {
|
|
72
|
+
if (!origin) {
|
|
73
|
+
return cb(null, true);
|
|
74
|
+
}
|
|
75
|
+
const transakWhiteList = config.transak?.whitelist ?? [];
|
|
76
|
+
if (transakWhiteList.indexOf(origin) !== -1) {
|
|
77
|
+
cb(null, true);
|
|
78
|
+
} else {
|
|
79
|
+
cb(new Error('Not allowed by CORS'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
68
84
|
router.get('/latest-version', async (req, res) => {
|
|
69
85
|
setPublicCache(res, 10 * ONE_MINUTE);
|
|
70
86
|
try {
|
|
@@ -420,37 +436,37 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
420
436
|
});
|
|
421
437
|
});
|
|
422
438
|
|
|
423
|
-
router.post('/v1/service/transak/getAccessToken', (req, res) => {
|
|
439
|
+
router.post('/v1/service/transak/getAccessToken', cors(transakCorsOptions), (req, res) => {
|
|
424
440
|
respondWithAuthServer(req, res, context, server => {
|
|
425
441
|
return server.externalServices.transak.transakGetAccessToken(req);
|
|
426
442
|
});
|
|
427
443
|
});
|
|
428
444
|
|
|
429
|
-
router.post('/v1/service/transak/cryptoCurrencies', (req, res) => {
|
|
445
|
+
router.post('/v1/service/transak/cryptoCurrencies', cors(transakCorsOptions), (req, res) => {
|
|
430
446
|
respondWithPublicServer(req, res, context, server => {
|
|
431
447
|
return server.externalServices.transak.transakGetCryptoCurrencies(req);
|
|
432
448
|
});
|
|
433
449
|
});
|
|
434
450
|
|
|
435
|
-
router.post('/v1/service/transak/fiatCurrencies', (req, res) => {
|
|
451
|
+
router.post('/v1/service/transak/fiatCurrencies', cors(transakCorsOptions), (req, res) => {
|
|
436
452
|
respondWithPublicServer(req, res, context, server => {
|
|
437
453
|
return server.externalServices.transak.transakGetFiatCurrencies(req);
|
|
438
454
|
});
|
|
439
455
|
});
|
|
440
456
|
|
|
441
|
-
router.post('/v1/service/transak/quote', (req, res) => {
|
|
457
|
+
router.post('/v1/service/transak/quote', cors(transakCorsOptions), (req, res) => {
|
|
442
458
|
respondWithAuthServer(req, res, context, server => {
|
|
443
459
|
return server.externalServices.transak.transakGetQuote(req);
|
|
444
460
|
});
|
|
445
461
|
});
|
|
446
462
|
|
|
447
|
-
router.post('/v1/service/transak/signedPaymentUrl', (req, res) => {
|
|
463
|
+
router.post('/v1/service/transak/signedPaymentUrl', cors(transakCorsOptions), (req, res) => {
|
|
448
464
|
respondWithAuthServer(req, res, context, server => {
|
|
449
465
|
return server.externalServices.transak.transakGetSignedPaymentUrl(req);
|
|
450
466
|
});
|
|
451
467
|
});
|
|
452
468
|
|
|
453
|
-
router.post('/v1/service/transak/orderDetails', (req, res) => {
|
|
469
|
+
router.post('/v1/service/transak/orderDetails', cors(transakCorsOptions), (req, res) => {
|
|
454
470
|
respondWithPublicServer(req, res, context, server => {
|
|
455
471
|
return server.externalServices.transak.transakGetOrderDetails(req);
|
|
456
472
|
});
|
package/src/lib/routes/tss.ts
CHANGED
|
@@ -291,4 +291,19 @@ export function registerWalletRoutes(router: express.Router, context: RouteConte
|
|
|
291
291
|
});
|
|
292
292
|
});
|
|
293
293
|
});
|
|
294
|
-
|
|
294
|
+
|
|
295
|
+
router.get('/v1/flags', (req, res) => {
|
|
296
|
+
getServerWithAuth(req, res, async server => {
|
|
297
|
+
try {
|
|
298
|
+
const opts = { account: 0 };
|
|
299
|
+
if (req.query.account) {
|
|
300
|
+
opts.account = parseInt(req.query.account);
|
|
301
|
+
}
|
|
302
|
+
const flags = await server.getFlags(opts);
|
|
303
|
+
res.json(flags);
|
|
304
|
+
} catch (err) {
|
|
305
|
+
returnError(err, res, req);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
}
|
package/src/lib/server.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import util from 'util';
|
|
1
2
|
import {
|
|
2
3
|
BitcoreLib as Bitcore,
|
|
3
4
|
BitcoreLibCash as BitcoreCash,
|
|
@@ -2924,7 +2925,8 @@ export class WalletService implements IWalletService {
|
|
|
2924
2925
|
fromAta: opts.fromAta,
|
|
2925
2926
|
decimals: opts.decimals,
|
|
2926
2927
|
refreshOnPublish: opts.refreshOnPublish,
|
|
2927
|
-
deferNonce: opts.deferNonce
|
|
2928
|
+
deferNonce: opts.deferNonce,
|
|
2929
|
+
flags: opts.flags
|
|
2928
2930
|
};
|
|
2929
2931
|
txp = TxProposal.create(txOpts);
|
|
2930
2932
|
next();
|
|
@@ -3358,6 +3360,19 @@ export class WalletService implements IWalletService {
|
|
|
3358
3360
|
return cb(Errors.TX_NONCE_CONFLICT);
|
|
3359
3361
|
}
|
|
3360
3362
|
}
|
|
3363
|
+
// A deferred txp matching a broadcasted txp's nonce means its JIT-assigned
|
|
3364
|
+
// value went stale. Non-deferred txps may reuse an in-flight nonce
|
|
3365
|
+
// intentionally (e.g. speed-up).
|
|
3366
|
+
if (txp.nonce != null && txp.deferNonce) {
|
|
3367
|
+
const broadcastedTxps = await util.promisify(this.storage.fetchBroadcastedTxs).call(this.storage, this.walletId, {
|
|
3368
|
+
minTs: Math.floor(Date.now() / 1000) - Defaults.BROADCASTED_NONCE_SCAN_WINDOW
|
|
3369
|
+
});
|
|
3370
|
+
for (const t of broadcastedTxps || []) {
|
|
3371
|
+
if (t.id !== txp.id && t.nonce != null && Number(t.nonce) === Number(txp.nonce)) {
|
|
3372
|
+
return cb(Errors.TX_NONCE_CONFLICT);
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3361
3376
|
} catch (err) {
|
|
3362
3377
|
return cb(err);
|
|
3363
3378
|
}
|
|
@@ -3486,9 +3501,18 @@ export class WalletService implements IWalletService {
|
|
|
3486
3501
|
.filter(t => t.id !== txp.id && t.nonce != null && t.status !== 'rejected')
|
|
3487
3502
|
.map(t => Number(t.nonce));
|
|
3488
3503
|
|
|
3504
|
+
// 2b. Broadcasted-but-unmined txps still occupy their nonces but appear in
|
|
3505
|
+
// neither the confirmed count nor the pending set
|
|
3506
|
+
const broadcastedTxps = await util.promisify(this.storage.fetchBroadcastedTxs).call(this.storage, this.walletId, {
|
|
3507
|
+
minTs: Math.floor(Date.now() / 1000) - Defaults.BROADCASTED_NONCE_SCAN_WINDOW
|
|
3508
|
+
});
|
|
3509
|
+
const inFlightNonces = (broadcastedTxps || [])
|
|
3510
|
+
.filter(t => t.nonce != null && Number(t.nonce) >= Number(confirmedNonce))
|
|
3511
|
+
.map(t => Number(t.nonce));
|
|
3512
|
+
|
|
3489
3513
|
// 3. Calculate gap-free nonce
|
|
3490
3514
|
let suggestedNonce = Number(confirmedNonce);
|
|
3491
|
-
const allNonces = pendingNonces.sort((a, b) => a - b);
|
|
3515
|
+
const allNonces = [...pendingNonces, ...inFlightNonces].sort((a, b) => a - b);
|
|
3492
3516
|
for (const n of allNonces) {
|
|
3493
3517
|
if (n === suggestedNonce) {
|
|
3494
3518
|
suggestedNonce++;
|
|
@@ -5303,6 +5327,27 @@ export class WalletService implements IWalletService {
|
|
|
5303
5327
|
});
|
|
5304
5328
|
}
|
|
5305
5329
|
|
|
5330
|
+
async getFlags(opts: { account: number }) {
|
|
5331
|
+
try {
|
|
5332
|
+
const wallet = await util.promisify(this.getWallet).call(this, {});
|
|
5333
|
+
if (wallet.chain !== 'xrp') throw new Error('Flags are only supported for XRP wallets');
|
|
5334
|
+
|
|
5335
|
+
const addresses = await util.promisify(this.storage.fetchAddresses).call(this.storage, this.walletId);
|
|
5336
|
+
const addressObj = addresses.find(a => a.path === `m/0/${opts.account || 0}`);
|
|
5337
|
+
if (!addressObj) throw new Error('Could not find address for account ' + opts.account);
|
|
5338
|
+
|
|
5339
|
+
const bc = this._getBlockchainExplorer(wallet.chain, wallet.network);
|
|
5340
|
+
if (!bc) throw new Error('Could not get blockchain explorer instance');
|
|
5341
|
+
|
|
5342
|
+
const address = addressObj.address.split(':')[0]; // Remove testnet suffix
|
|
5343
|
+
const flags = await bc.getFlags({ address });
|
|
5344
|
+
return flags;
|
|
5345
|
+
} catch (err) {
|
|
5346
|
+
this.logw('Error getting flags: %o', err);
|
|
5347
|
+
throw err;
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
|
|
5306
5351
|
static upgradeNeeded(
|
|
5307
5352
|
paths: Upgrade | Upgrade[],
|
|
5308
5353
|
opts: UpgradeCheckOpts & { clientVersion: string; userAgent: string }
|
|
@@ -5567,6 +5612,55 @@ export class WalletService implements IWalletService {
|
|
|
5567
5612
|
});
|
|
5568
5613
|
}
|
|
5569
5614
|
|
|
5615
|
+
moralisGetTransactionVerbose(req): Promise<any> {
|
|
5616
|
+
return new Promise(async (resolve, reject) => {
|
|
5617
|
+
try {
|
|
5618
|
+
const response = await Moralis.EvmApi.transaction.getTransactionVerbose({
|
|
5619
|
+
transactionHash: req.body.transactionHash,
|
|
5620
|
+
chain: req.body.chain,
|
|
5621
|
+
});
|
|
5622
|
+
|
|
5623
|
+
return resolve(response?.raw ?? response);
|
|
5624
|
+
} catch (err) {
|
|
5625
|
+
reject(err);
|
|
5626
|
+
}
|
|
5627
|
+
});
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5630
|
+
moralisGetMultipleSolTokenPrices(req): Promise<any> {
|
|
5631
|
+
return new Promise((resolve, reject) => {
|
|
5632
|
+
if (!config.moralis) return reject(new Error('Moralis missing credentials'));
|
|
5633
|
+
if (!checkRequired(req.body, ['addresses']) || !Array.isArray(req.body.addresses)) {
|
|
5634
|
+
return reject(new ClientError('moralisGetMultipleSolTokenPrices request missing arguments'));
|
|
5635
|
+
}
|
|
5636
|
+
|
|
5637
|
+
const network = req.body.network === 'devnet' ? 'devnet' : 'mainnet';
|
|
5638
|
+
const headers = {
|
|
5639
|
+
'Accept': 'application/json',
|
|
5640
|
+
'Content-Type': 'application/json',
|
|
5641
|
+
'X-Api-Key': config.moralis.apiKey,
|
|
5642
|
+
};
|
|
5643
|
+
|
|
5644
|
+
const URL: string = `https://solana-gateway.moralis.io/token/${network}/prices`;
|
|
5645
|
+
|
|
5646
|
+
this.request.post(
|
|
5647
|
+
URL,
|
|
5648
|
+
{
|
|
5649
|
+
headers,
|
|
5650
|
+
body: { addresses: req.body.addresses },
|
|
5651
|
+
json: true
|
|
5652
|
+
},
|
|
5653
|
+
(err, data) => {
|
|
5654
|
+
if (err) {
|
|
5655
|
+
return reject(err.body ?? err);
|
|
5656
|
+
} else {
|
|
5657
|
+
return resolve(data.body ?? data);
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
);
|
|
5661
|
+
});
|
|
5662
|
+
}
|
|
5663
|
+
|
|
5570
5664
|
moralisGetSolWalletPortfolio(req): Promise<any> {
|
|
5571
5665
|
return new Promise(async (resolve, reject) => {
|
|
5572
5666
|
let network;
|
package/test/chain/xrp.test.ts
CHANGED
|
@@ -10,6 +10,27 @@ const should = chai.should();
|
|
|
10
10
|
const xpub = 'tpubDD7tYYerLNNm65Ez7pRjxQ2NpRHDoyRWoLWudnQ8agXjr7qs9BsPsRXk8Z6spPPJodnaY158YqeCKT5oXuZvbuNLfm1R4kXGJ2vPd9pUxDT';
|
|
11
11
|
|
|
12
12
|
describe('Chain XRP', function() {
|
|
13
|
+
describe('#getWalletSendMaxInfo', function() {
|
|
14
|
+
it('should resolve fee level before calculating sendMax amount', function(done) {
|
|
15
|
+
const server = {
|
|
16
|
+
getBalance: (_opts, cb) => cb(null, { availableAmount: 1000000 }),
|
|
17
|
+
_getFeePerKb: (_wallet, opts, cb) => {
|
|
18
|
+
opts.feeLevel.should.equal('normal');
|
|
19
|
+
return cb(null, 12);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const wallet = { chain: 'xrp' };
|
|
23
|
+
|
|
24
|
+
ChainService.get('xrp').getWalletSendMaxInfo(server as any, wallet as any, { feeLevel: 'normal' }, (err, info) => {
|
|
25
|
+
should.not.exist(err);
|
|
26
|
+
info.amount.should.equal(999988);
|
|
27
|
+
info.fee.should.equal(12);
|
|
28
|
+
info.feePerKb.should.equal(12);
|
|
29
|
+
done();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
13
34
|
describe('#getBitcoreTx', function() {
|
|
14
35
|
it('should create a valid bitcore TX', function() {
|
|
15
36
|
const txp = TxProposal.fromObj(aTXP()) as TxProposal;
|