@bitpay-labs/bitcore-wallet-service 11.10.4 → 11.10.6
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 +2 -2
- package/src/externalservices/moonpay.ts +22 -7
- package/src/externalservices/ramp.ts +31 -11
- package/src/lib/expressapp.ts +1 -0
- package/src/lib/routes/moralis.ts +25 -9
- package/src/lib/routes/services.ts +8 -8
- package/test/expressapp.test.ts +83 -0
- package/test/integration/externalservices/moonpay.test.ts +30 -0
- package/test/integration/externalservices/ramp.test.ts +151 -20
- package/ts_build/package.json +4 -4
- package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
- package/ts_build/src/externalservices/moonpay.js +8 -4
- package/ts_build/src/externalservices/moonpay.js.map +1 -1
- package/ts_build/src/externalservices/ramp.d.ts.map +1 -1
- package/ts_build/src/externalservices/ramp.js +19 -18
- package/ts_build/src/externalservices/ramp.js.map +1 -1
- package/ts_build/src/lib/expressapp.d.ts.map +1 -1
- package/ts_build/src/lib/expressapp.js +1 -0
- package/ts_build/src/lib/expressapp.js.map +1 -1
- package/ts_build/src/lib/routes/moralis.d.ts +1 -0
- package/ts_build/src/lib/routes/moralis.d.ts.map +1 -1
- package/ts_build/src/lib/routes/moralis.js +22 -9
- package/ts_build/src/lib/routes/moralis.js.map +1 -1
- package/ts_build/src/lib/routes/services.js +8 -8
- package/ts_build/src/lib/routes/services.js.map +1 -1
- package/ts_build/test/expressapp.test.js +79 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/moonpay.test.js +26 -0
- package/ts_build/test/integration/externalservices/moonpay.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/ramp.test.js +113 -20
- package/ts_build/test/integration/externalservices/ramp.test.js.map +1 -1
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.6",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "ts_build/src/index.js",
|
|
8
8
|
"types": "./ts_build/src/index.d.ts",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
"email": "kjoseph@bitpay.com"
|
|
108
108
|
}
|
|
109
109
|
],
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "68cba09ab40e3e91528cae682333303c08b3f565"
|
|
111
111
|
}
|
|
@@ -190,6 +190,8 @@ export class MoonpayService {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
moonpayGetSignedPaymentUrl(req): { urlWithSignature: string } {
|
|
193
|
+
// moonpayGetKeys deletes env/context from the body, so read context first
|
|
194
|
+
const isWebContext = req.body.context === 'web';
|
|
193
195
|
const keys = this.moonpayGetKeys(req);
|
|
194
196
|
const SECRET_KEY = keys.SECRET_KEY;
|
|
195
197
|
const API_KEY = keys.API_KEY;
|
|
@@ -225,15 +227,28 @@ export class MoonpayService {
|
|
|
225
227
|
if (req.body.paymentMethod) qs.push('paymentMethod=' + encodeURIComponent(req.body.paymentMethod));
|
|
226
228
|
if (req.body.areFeesIncluded) qs.push('areFeesIncluded=' + encodeURIComponent(req.body.areFeesIncluded));
|
|
227
229
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
+
// Web requests are proxied through the bitpay backend, so the IP on this
|
|
231
|
+
// request belongs to that server, not the customer. The proxy captures the
|
|
232
|
+
// customer's public IP and forwards it as deviceIp. Web credentials are only
|
|
233
|
+
// held by the proxy, so the forwarded value is trusted for that context only.
|
|
234
|
+
// If the proxy does not forward an IP, omit allowedIpAddress rather than
|
|
235
|
+
// signing an IP the customer will never match.
|
|
236
|
+
let deviceIp = isWebContext ? req.body.deviceIp : Utils.getIpFromReq(req);
|
|
237
|
+
if (!deviceIp && !isWebContext) {
|
|
230
238
|
throw new ClientError('Could not determine device IP address');
|
|
231
239
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
if (deviceIp) {
|
|
241
|
+
// Canonicalize before hashing: HMAC is byte-exact and MoonPay hashes the
|
|
242
|
+
// plain IPv4 it observes, while dual-stack sockets report IPv4 clients as
|
|
243
|
+
// IPv4-mapped IPv6 (::ffff:1.2.3.4) - strip the prefix so both sides
|
|
244
|
+
// hash the same string
|
|
245
|
+
deviceIp = String(deviceIp).trim().replace(/^::ffff:/i, '');
|
|
246
|
+
const allowedIpAddress: string = Bitcore.crypto.Hash.sha256hmac(
|
|
247
|
+
Buffer.from(deviceIp),
|
|
248
|
+
Buffer.from(SECRET_KEY)
|
|
249
|
+
).toString('base64');
|
|
250
|
+
qs.push('allowedIpAddress=' + encodeURIComponent(allowedIpAddress));
|
|
251
|
+
}
|
|
237
252
|
|
|
238
253
|
const URL_SEARCH: string = `?${qs.join('&')}`;
|
|
239
254
|
|
|
@@ -112,14 +112,13 @@ export class RampService {
|
|
|
112
112
|
const webRequiredParams = [
|
|
113
113
|
'selectedCountryCode',
|
|
114
114
|
];
|
|
115
|
-
|
|
115
|
+
let appRequiredParams = [
|
|
116
116
|
'enabledFlows',
|
|
117
117
|
'defaultFlow',
|
|
118
118
|
'selectedCountryCode',
|
|
119
|
-
'defaultAsset',
|
|
120
119
|
];
|
|
121
|
-
const extraRequiredParams = req.body.flow && req.body.flow === 'sell' ? [
|
|
122
|
-
appRequiredParams.concat(extraRequiredParams);
|
|
120
|
+
const extraRequiredParams = req.body.flow && req.body.flow === 'sell' ? [] : ['finalUrl', 'userAddress'];
|
|
121
|
+
appRequiredParams = appRequiredParams.concat(extraRequiredParams);
|
|
123
122
|
|
|
124
123
|
const requiredParams = req.body.context === 'web' ? webRequiredParams : appRequiredParams;
|
|
125
124
|
const keys = this.rampGetKeys(req);
|
|
@@ -138,21 +137,42 @@ export class RampService {
|
|
|
138
137
|
qs.push('selectedCountryCode=' + encodeURIComponent(req.body.selectedCountryCode));
|
|
139
138
|
if (req.body.finalUrl) qs.push('finalUrl=' + encodeURIComponent(req.body.finalUrl));
|
|
140
139
|
if (req.body.userAddress) qs.push('userAddress=' + encodeURIComponent(req.body.userAddress));
|
|
141
|
-
if (req.body.swapAsset) qs.push('swapAsset=' + encodeURIComponent(req.body.swapAsset));
|
|
142
|
-
if (req.body.offrampAsset) qs.push('offrampAsset=' + encodeURIComponent(req.body.offrampAsset));
|
|
143
140
|
if (req.body.enabledFlows) qs.push('enabledFlows=' + encodeURIComponent(req.body.enabledFlows));
|
|
144
141
|
if (req.body.defaultFlow) qs.push('defaultFlow=' + encodeURIComponent(req.body.defaultFlow));
|
|
145
142
|
if (req.body.hostLogoUrl) qs.push('hostLogoUrl=' + encodeURIComponent(req.body.hostLogoUrl));
|
|
146
143
|
if (req.body.hostAppName) qs.push('hostAppName=' + encodeURIComponent(req.body.hostAppName));
|
|
147
|
-
if (req.body.swapAmount) qs.push('swapAmount=' + encodeURIComponent(req.body.swapAmount));
|
|
148
|
-
if (req.body.fiatValue) qs.push('fiatValue=' + encodeURIComponent(req.body.fiatValue));
|
|
149
|
-
if (req.body.fiatCurrency) qs.push('fiatCurrency=' + encodeURIComponent(req.body.fiatCurrency));
|
|
150
|
-
if (req.body.defaultAsset) qs.push('defaultAsset=' + encodeURIComponent(req.body.defaultAsset));
|
|
151
144
|
if (req.body.userEmailAddress) qs.push('userEmailAddress=' + encodeURIComponent(req.body.userEmailAddress));
|
|
152
145
|
if (req.body.useSendCryptoCallback) qs.push('useSendCryptoCallback=' + encodeURIComponent(req.body.useSendCryptoCallback));
|
|
153
146
|
if (req.body.paymentMethodType) qs.push('paymentMethodType=' + encodeURIComponent(req.body.paymentMethodType));
|
|
154
147
|
if (req.body.hideExitButton) qs.push('hideExitButton=' + encodeURIComponent(req.body.hideExitButton));
|
|
155
|
-
|
|
148
|
+
|
|
149
|
+
// Ramp deprecated the legacy per-flow search params in favor of a unified format.
|
|
150
|
+
// Ref: https://docs.rampnetwork.com/search-params-migration
|
|
151
|
+
// Older app versions still send the legacy fields, so we translate them into the new
|
|
152
|
+
// unified params here to keep those requests working going forward.
|
|
153
|
+
// `flow === 'sell'` => OFFRAMP; otherwise => ONRAMP.
|
|
154
|
+
// If a newer client already sends the new params, those take precedence.
|
|
155
|
+
const isOfframp = req.body.flow === 'sell';
|
|
156
|
+
|
|
157
|
+
// enabledCryptoAssets <- swapAsset (onramp) / offrampAsset (offramp)
|
|
158
|
+
const enabledCryptoAssets = req.body.enabledCryptoAssets ?? (isOfframp ? req.body.offrampAsset : req.body.swapAsset);
|
|
159
|
+
if (enabledCryptoAssets) qs.push('enabledCryptoAssets=' + encodeURIComponent(enabledCryptoAssets));
|
|
160
|
+
|
|
161
|
+
// inAsset (incoming, paid by the user) <- fiatCurrency (onramp) / defaultAsset (offramp)
|
|
162
|
+
const inAsset = req.body.inAsset ?? (isOfframp ? req.body.defaultAsset : req.body.fiatCurrency);
|
|
163
|
+
if (inAsset) qs.push('inAsset=' + encodeURIComponent(inAsset));
|
|
164
|
+
|
|
165
|
+
// outAsset (outgoing, received by the user) <- defaultAsset (onramp) / fiatCurrency (offramp)
|
|
166
|
+
const outAsset = req.body.outAsset ?? (isOfframp ? req.body.fiatCurrency : req.body.defaultAsset);
|
|
167
|
+
if (outAsset) qs.push('outAsset=' + encodeURIComponent(outAsset));
|
|
168
|
+
|
|
169
|
+
// inAssetValue (units, no decimals) <- fiatValue (onramp) / swapAmount (offramp)
|
|
170
|
+
const inAssetValue = req.body.inAssetValue ?? (isOfframp ? req.body.swapAmount : req.body.fiatValue);
|
|
171
|
+
if (inAssetValue) qs.push('inAssetValue=' + encodeURIComponent(inAssetValue));
|
|
172
|
+
|
|
173
|
+
// outAssetValue (units, no decimals) <- swapAmount (onramp) / fiatValue (offramp)
|
|
174
|
+
const outAssetValue = req.body.outAssetValue ?? (isOfframp ? req.body.fiatValue : req.body.swapAmount);
|
|
175
|
+
if (outAssetValue) qs.push('outAssetValue=' + encodeURIComponent(outAssetValue));
|
|
156
176
|
|
|
157
177
|
const queryString = qs.join('&');
|
|
158
178
|
|
package/src/lib/expressapp.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { WalletService } from '../server';
|
|
|
6
6
|
|
|
7
7
|
interface RouteContext {
|
|
8
8
|
getServer: Types.GetServerFn;
|
|
9
|
+
getServerWithAuth: Types.GetServerWithAuthFn;
|
|
9
10
|
returnError: Types.ReturnErrorFn;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -33,6 +34,21 @@ function respondWithPublicServer(req, res, context: RouteContext, handler: Route
|
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
function walletAuthOrPublic(context: RouteContext) {
|
|
38
|
+
return function(req, res, next) {
|
|
39
|
+
if (!req.header('x-identity')) {
|
|
40
|
+
// Not from wallet
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
43
|
+
return context.getServerWithAuth(req, res, _server => {
|
|
44
|
+
if (!req.header('origin')) {
|
|
45
|
+
req.headers['origin'] = config.moralis?.whitelist?.[0];
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
export function registerMoralisRoutes(router: express.Router, context: RouteContext) {
|
|
37
53
|
const moralisCorsOptions = {
|
|
38
54
|
origin: (origin, cb) => {
|
|
@@ -45,55 +61,55 @@ export function registerMoralisRoutes(router: express.Router, context: RouteCont
|
|
|
45
61
|
}
|
|
46
62
|
};
|
|
47
63
|
|
|
48
|
-
router.post('/v1/moralis/getWalletTokenBalances', cors(moralisCorsOptions), (req, res) => {
|
|
64
|
+
router.post('/v1/moralis/getWalletTokenBalances', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
49
65
|
respondWithPublicServer(req, res, context, server => {
|
|
50
66
|
return server.moralisGetWalletTokenBalances(req);
|
|
51
67
|
});
|
|
52
68
|
});
|
|
53
69
|
|
|
54
|
-
router.post('/v1/moralis/moralisGetTokenAllowance', cors(moralisCorsOptions), (req, res) => {
|
|
70
|
+
router.post('/v1/moralis/moralisGetTokenAllowance', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
55
71
|
respondWithPublicServer(req, res, context, server => {
|
|
56
72
|
return server.moralisGetTokenAllowance(req);
|
|
57
73
|
});
|
|
58
74
|
});
|
|
59
75
|
|
|
60
|
-
router.post('/v1/moralis/moralisGetNativeBalance', cors(moralisCorsOptions), (req, res) => {
|
|
76
|
+
router.post('/v1/moralis/moralisGetNativeBalance', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
61
77
|
respondWithPublicServer(req, res, context, server => {
|
|
62
78
|
return server.moralisGetNativeBalance(req);
|
|
63
79
|
});
|
|
64
80
|
});
|
|
65
81
|
|
|
66
|
-
router.post('/v1/moralis/GetTokenPrice', cors(moralisCorsOptions), (req, res) => {
|
|
82
|
+
router.post('/v1/moralis/GetTokenPrice', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
67
83
|
respondWithPublicServer(req, res, context, server => {
|
|
68
84
|
return server.moralisGetTokenPrice(req);
|
|
69
85
|
});
|
|
70
86
|
});
|
|
71
87
|
|
|
72
|
-
router.post('/v1/moralis/getMultipleERC20TokenPrices', cors(moralisCorsOptions), (req, res) => {
|
|
88
|
+
router.post('/v1/moralis/getMultipleERC20TokenPrices', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
73
89
|
respondWithPublicServer(req, res, context, server => {
|
|
74
90
|
return server.moralisGetMultipleERC20TokenPrices(req);
|
|
75
91
|
});
|
|
76
92
|
});
|
|
77
93
|
|
|
78
|
-
router.post('/v1/moralis/getERC20TokenBalancesWithPricesByWallet', cors(moralisCorsOptions), (req, res) => {
|
|
94
|
+
router.post('/v1/moralis/getERC20TokenBalancesWithPricesByWallet', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
79
95
|
respondWithPublicServer(req, res, context, server => {
|
|
80
96
|
return server.moralisGetERC20TokenBalancesWithPricesByWallet(req);
|
|
81
97
|
});
|
|
82
98
|
});
|
|
83
99
|
|
|
84
|
-
router.post('/v1/moralis/getSolWalletPortfolio', cors(moralisCorsOptions), (req, res) => {
|
|
100
|
+
router.post('/v1/moralis/getSolWalletPortfolio', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
85
101
|
respondWithPublicServer(req, res, context, server => {
|
|
86
102
|
return server.moralisGetSolWalletPortfolio(req);
|
|
87
103
|
});
|
|
88
104
|
});
|
|
89
105
|
|
|
90
|
-
router.post('/v1/moralis/getTransactionVerbose', cors(moralisCorsOptions), (req, res) => {
|
|
106
|
+
router.post('/v1/moralis/getTransactionVerbose', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
91
107
|
respondWithPublicServer(req, res, context, server => {
|
|
92
108
|
return server.moralisGetTransactionVerbose(req);
|
|
93
109
|
});
|
|
94
110
|
});
|
|
95
111
|
|
|
96
|
-
router.post('/v1/moralis/getMultipleSolTokenPrices', cors(moralisCorsOptions), (req, res) => {
|
|
112
|
+
router.post('/v1/moralis/getMultipleSolTokenPrices', walletAuthOrPublic(context), cors(moralisCorsOptions), (req, res) => {
|
|
97
113
|
respondWithPublicServer(req, res, context, server => {
|
|
98
114
|
return server.moralisGetMultipleSolTokenPrices(req);
|
|
99
115
|
});
|
|
@@ -232,7 +232,7 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
232
232
|
});
|
|
233
233
|
|
|
234
234
|
router.post('/v1/service/banxa/getOrder', (req, res) => {
|
|
235
|
-
|
|
235
|
+
respondWithAuthServer(req, res, context, server => {
|
|
236
236
|
return server.externalServices.banxa.banxaGetOrder(req);
|
|
237
237
|
});
|
|
238
238
|
});
|
|
@@ -274,7 +274,7 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
router.post('/v1/service/moonpay/transactionDetails', (req, res) => {
|
|
277
|
-
|
|
277
|
+
respondWithAuthServer(req, res, context, server => {
|
|
278
278
|
return server.externalServices.moonpay.moonpayGetTransactionDetails(req);
|
|
279
279
|
});
|
|
280
280
|
});
|
|
@@ -292,7 +292,7 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
292
292
|
});
|
|
293
293
|
|
|
294
294
|
router.post('/v1/service/moonpay/accountDetails', (req, res) => {
|
|
295
|
-
|
|
295
|
+
respondWithAuthServer(req, res, context, server => {
|
|
296
296
|
return server.externalServices.moonpay.moonpayGetAccountDetails(req);
|
|
297
297
|
});
|
|
298
298
|
});
|
|
@@ -364,7 +364,7 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
364
364
|
});
|
|
365
365
|
|
|
366
366
|
router.post('/v1/service/sardine/ordersDetails', (req, res) => {
|
|
367
|
-
|
|
367
|
+
respondWithAuthServer(req, res, context, server => {
|
|
368
368
|
return server.externalServices.sardine.sardineGetOrdersDetails(req);
|
|
369
369
|
});
|
|
370
370
|
});
|
|
@@ -431,7 +431,7 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
431
431
|
});
|
|
432
432
|
|
|
433
433
|
router.post('/v1/service/thorswap/getSwapTx', (req, res) => {
|
|
434
|
-
|
|
434
|
+
respondWithAuthServer(req, res, context, server => {
|
|
435
435
|
return server.externalServices.thorswap.thorswapGetSwapTx(req);
|
|
436
436
|
});
|
|
437
437
|
});
|
|
@@ -467,7 +467,7 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
467
467
|
});
|
|
468
468
|
|
|
469
469
|
router.post('/v1/service/transak/orderDetails', cors(transakCorsOptions), (req, res) => {
|
|
470
|
-
|
|
470
|
+
respondWithAuthServer(req, res, context, server => {
|
|
471
471
|
return server.externalServices.transak.transakGetOrderDetails(req);
|
|
472
472
|
});
|
|
473
473
|
});
|
|
@@ -509,13 +509,13 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
509
509
|
});
|
|
510
510
|
|
|
511
511
|
router.post('/v1/service/changelly/getTransactions', (req, res) => {
|
|
512
|
-
|
|
512
|
+
respondWithAuthServer(req, res, context, server => {
|
|
513
513
|
return server.externalServices.changelly.changellyGetTransactions(req);
|
|
514
514
|
});
|
|
515
515
|
});
|
|
516
516
|
|
|
517
517
|
router.post('/v1/service/changelly/getStatus', (req, res) => {
|
|
518
|
-
|
|
518
|
+
respondWithAuthServer(req, res, context, server => {
|
|
519
519
|
return server.externalServices.changelly.changellyGetStatus(req);
|
|
520
520
|
});
|
|
521
521
|
});
|
package/test/expressapp.test.ts
CHANGED
|
@@ -610,6 +610,89 @@ describe('ExpressApp', function() {
|
|
|
610
610
|
});
|
|
611
611
|
});
|
|
612
612
|
|
|
613
|
+
describe('Moralis routes (wallet-authenticated or public)', function() {
|
|
614
|
+
const whitelistedOrigin = 'https://whitelisted-dapp.example';
|
|
615
|
+
let originalMoralisConfig;
|
|
616
|
+
beforeEach(function() {
|
|
617
|
+
originalMoralisConfig = config.moralis;
|
|
618
|
+
config.moralis = { whitelist: [whitelistedOrigin] };
|
|
619
|
+
});
|
|
620
|
+
afterEach(function() {
|
|
621
|
+
config.moralis = originalMoralisConfig;
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('/v1/moralis/getMultipleERC20TokenPrices should work without auth headers (public/dApp)', function(done) {
|
|
625
|
+
const server = {
|
|
626
|
+
moralisGetMultipleERC20TokenPrices: sinon.stub().resolves([{ tokenAddress: '0xaaa', usdPrice: 1 }]),
|
|
627
|
+
};
|
|
628
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
629
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
630
|
+
start(ExpressApp, function() {
|
|
631
|
+
const requestOptions = {
|
|
632
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/moralis/getMultipleERC20TokenPrices',
|
|
633
|
+
method: 'post',
|
|
634
|
+
json: { chain: '0x1', tokens: [{ tokenAddress: '0xaaa' }] },
|
|
635
|
+
headers: {
|
|
636
|
+
origin: whitelistedOrigin
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
request(requestOptions, function(err, res, body) {
|
|
640
|
+
should.not.exist(err);
|
|
641
|
+
res.statusCode.should.equal(200);
|
|
642
|
+
body[0].tokenAddress.should.equal('0xaaa');
|
|
643
|
+
done();
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('/v1/moralis/getMultipleERC20TokenPrices should work with valid wallet auth headers', function(done) {
|
|
649
|
+
const server = {
|
|
650
|
+
moralisGetMultipleERC20TokenPrices: sinon.stub().resolves([{ tokenAddress: '0xaaa', usdPrice: 1 }]),
|
|
651
|
+
};
|
|
652
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
653
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
654
|
+
sandbox.stub(WalletService, 'getInstanceWithAuth').callsArgWith(1, null, server);
|
|
655
|
+
start(ExpressApp, function() {
|
|
656
|
+
const requestOptions = {
|
|
657
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/moralis/getMultipleERC20TokenPrices',
|
|
658
|
+
method: 'post',
|
|
659
|
+
json: { chain: '0x1', tokens: [{ tokenAddress: '0xaaa' }] },
|
|
660
|
+
headers: {
|
|
661
|
+
'x-identity': 'identity',
|
|
662
|
+
'x-signature': 'signature'
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
request(requestOptions, function(err, res, body) {
|
|
666
|
+
should.not.exist(err);
|
|
667
|
+
res.statusCode.should.equal(200);
|
|
668
|
+
body[0].tokenAddress.should.equal('0xaaa');
|
|
669
|
+
done();
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('/v1/moralis/getMultipleERC20TokenPrices should fail with invalid wallet auth headers', function(done) {
|
|
675
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
676
|
+
sandbox.stub(WalletService, 'getInstanceWithAuth').callsArgWith(1, new ClientError('NOT_AUTHORIZED', 'Not authorized'));
|
|
677
|
+
start(ExpressApp, function() {
|
|
678
|
+
const requestOptions = {
|
|
679
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/moralis/getMultipleERC20TokenPrices',
|
|
680
|
+
method: 'post',
|
|
681
|
+
json: { chain: '0x1', tokens: [{ tokenAddress: '0xaaa' }] },
|
|
682
|
+
headers: {
|
|
683
|
+
'x-identity': 'identity',
|
|
684
|
+
'x-signature': 'bad-signature'
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
request(requestOptions, function(err, res) {
|
|
688
|
+
should.not.exist(err);
|
|
689
|
+
res.statusCode.should.equal(401);
|
|
690
|
+
done();
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
|
|
613
696
|
describe('Token allowance', function() {
|
|
614
697
|
it('/v1/token/allowance', function(done) {
|
|
615
698
|
const server = {
|
|
@@ -292,6 +292,36 @@ describe('Moonpay integration', () => {
|
|
|
292
292
|
}
|
|
293
293
|
});
|
|
294
294
|
|
|
295
|
+
it('should hash the forwarded deviceIp instead of the request IP for web context', () => {
|
|
296
|
+
req.body.context = 'web';
|
|
297
|
+
req.body.deviceIp = '203.0.113.42';
|
|
298
|
+
const data = server.externalServices.moonpay.moonpayGetSignedPaymentUrl(req);
|
|
299
|
+
should.exist(data.urlWithSignature);
|
|
300
|
+
data.urlWithSignature.should.equal('widgetApi4?apiKey=apiKey4¤cyCode=btc&walletAddress=bitcoin%3A123123&baseCurrencyCode=usd&baseCurrencyAmount=500&externalTransactionId=123123&redirectURL=bitpay%3A%2F%2Fmoonpay&allowedIpAddress=HkPyqsZMUzAgsEx27Tlz%2B5XfZHaH0fSfWV%2FMKR7JAPc%3D&signature=B%2Bw0TTQiy8%2Ffq6QeoeSf4dKpdPHZ%2F2EnBB1S4UotNGM%3D');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should canonicalize IPv4-mapped IPv6 deviceIp before hashing', () => {
|
|
304
|
+
req.body.context = 'web';
|
|
305
|
+
req.body.deviceIp = '::ffff:203.0.113.42';
|
|
306
|
+
const data = server.externalServices.moonpay.moonpayGetSignedPaymentUrl(req);
|
|
307
|
+
should.exist(data.urlWithSignature);
|
|
308
|
+
data.urlWithSignature.should.equal('widgetApi4?apiKey=apiKey4¤cyCode=btc&walletAddress=bitcoin%3A123123&baseCurrencyCode=usd&baseCurrencyAmount=500&externalTransactionId=123123&redirectURL=bitpay%3A%2F%2Fmoonpay&allowedIpAddress=HkPyqsZMUzAgsEx27Tlz%2B5XfZHaH0fSfWV%2FMKR7JAPc%3D&signature=B%2Bw0TTQiy8%2Ffq6QeoeSf4dKpdPHZ%2F2EnBB1S4UotNGM%3D');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should omit allowedIpAddress for web context when no deviceIp is forwarded', () => {
|
|
312
|
+
req.body.context = 'web';
|
|
313
|
+
const data = server.externalServices.moonpay.moonpayGetSignedPaymentUrl(req);
|
|
314
|
+
should.exist(data.urlWithSignature);
|
|
315
|
+
data.urlWithSignature.should.equal('widgetApi4?apiKey=apiKey4¤cyCode=btc&walletAddress=bitcoin%3A123123&baseCurrencyCode=usd&baseCurrencyAmount=500&externalTransactionId=123123&redirectURL=bitpay%3A%2F%2Fmoonpay&signature=13Q%2BET1UQLnCqCyg3stDAN4%2FTQ8QB009LcuAP1y6B%2FI%3D');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should ignore a body deviceIp for non-web context and use the request IP', () => {
|
|
319
|
+
req.body.deviceIp = '203.0.113.42';
|
|
320
|
+
const data = server.externalServices.moonpay.moonpayGetSignedPaymentUrl(req);
|
|
321
|
+
should.exist(data.urlWithSignature);
|
|
322
|
+
data.urlWithSignature.should.equal('widgetApi2?apiKey=apiKey2¤cyCode=btc&walletAddress=bitcoin%3A123123&baseCurrencyCode=usd&baseCurrencyAmount=500&externalTransactionId=123123&redirectURL=bitpay%3A%2F%2Fmoonpay&allowedIpAddress=CN35SFB5PKS4vkiZ4CglTxRgTAaUHBLGZcenAw6gHEY%3D&signature=3XxjRX3EMj2RNaoAwgOwFBOiVTXsgAS7C50uJf9SsvM%3D');
|
|
323
|
+
});
|
|
324
|
+
|
|
295
325
|
it('should return error if there is some missing arguments', () => {
|
|
296
326
|
delete req.body.currencyCode;
|
|
297
327
|
try {
|