@bitpay-labs/bitcore-wallet-service 11.10.3 → 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/package.json +6 -25
- package/src/lib/routes/moralis.ts +12 -0
- package/src/lib/server.ts +49 -0
- package/test/integration/externalservices/moralis.test.ts +476 -0
- package/ts_build/package.json +5 -24
- 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/server.d.ts +2 -0
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +42 -0
- package/ts_build/src/lib/server.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/.mocharc.yml
ADDED
package/.nycrc.yml
ADDED
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.10.
|
|
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
|
}
|
|
@@ -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
|
}
|
package/src/lib/server.ts
CHANGED
|
@@ -5612,6 +5612,55 @@ export class WalletService implements IWalletService {
|
|
|
5612
5612
|
});
|
|
5613
5613
|
}
|
|
5614
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
|
+
|
|
5615
5664
|
moralisGetSolWalletPortfolio(req): Promise<any> {
|
|
5616
5665
|
return new Promise(async (resolve, reject) => {
|
|
5617
5666
|
let network;
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import * as chai from 'chai';
|
|
4
|
+
import 'chai/register-should';
|
|
5
|
+
import util from 'util';
|
|
6
|
+
import sinon from 'sinon';
|
|
7
|
+
import Moralis from 'moralis';
|
|
8
|
+
import { WalletService } from '../../../src/lib/server';
|
|
9
|
+
import * as TestData from '../../testdata';
|
|
10
|
+
import config from '../../../src/config';
|
|
11
|
+
import helpers from '../helpers';
|
|
12
|
+
|
|
13
|
+
const should = chai.should();
|
|
14
|
+
|
|
15
|
+
describe('Moralis integration', () => {
|
|
16
|
+
let server;
|
|
17
|
+
let wallet;
|
|
18
|
+
let fakeRequest;
|
|
19
|
+
let lastGet;
|
|
20
|
+
let lastPost;
|
|
21
|
+
let req;
|
|
22
|
+
|
|
23
|
+
before(async function() {
|
|
24
|
+
await helpers.before();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
beforeEach(async function() {
|
|
28
|
+
config.moralis = {
|
|
29
|
+
apiKey: 'moralisApiKey1',
|
|
30
|
+
whitelist: []
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
lastGet = null;
|
|
34
|
+
lastPost = null;
|
|
35
|
+
fakeRequest = {
|
|
36
|
+
get: (_url, _opts, _cb) => {
|
|
37
|
+
lastGet = { url: _url, opts: _opts };
|
|
38
|
+
return _cb(null, { statusCode: 200, body: { result: [] } });
|
|
39
|
+
},
|
|
40
|
+
post: (_url, _opts, _cb) => {
|
|
41
|
+
lastPost = { url: _url, opts: _opts };
|
|
42
|
+
return _cb(null, { statusCode: 200, body: [{ tokenAddress: 'addr1', usdPrice: 1 }] });
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
await helpers.beforeEach();
|
|
47
|
+
({ wallet } = await helpers.createAndJoinWallet(1, 1));
|
|
48
|
+
const priv = TestData.copayers[0].privKey_1H_0;
|
|
49
|
+
const sig = helpers.signMessage('hello world', priv);
|
|
50
|
+
|
|
51
|
+
server = await util.promisify(WalletService.getInstanceWithAuth).call(WalletService, {
|
|
52
|
+
// test assumes wallet's copayer[0] is TestData's copayer[0]
|
|
53
|
+
copayerId: wallet.copayers[0].id,
|
|
54
|
+
message: 'hello world',
|
|
55
|
+
signature: sig,
|
|
56
|
+
clientVersion: 'bwc-2.0.0',
|
|
57
|
+
walletId: '123',
|
|
58
|
+
});
|
|
59
|
+
server.request = fakeRequest;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
sinon.restore();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
after(async function() {
|
|
67
|
+
await helpers.after();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('#moralisGetWalletTokenBalances', () => {
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
req = {
|
|
73
|
+
headers: {},
|
|
74
|
+
body: {
|
|
75
|
+
address: '0xabc',
|
|
76
|
+
chain: '0x1',
|
|
77
|
+
toBlock: 100,
|
|
78
|
+
tokenAddresses: ['0xtoken1'],
|
|
79
|
+
excludeSpam: true,
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should resolve response.raw and pass through the request params', async () => {
|
|
85
|
+
const stub = sinon.stub(Moralis.EvmApi.token, 'getWalletTokenBalances')
|
|
86
|
+
.resolves({ raw: [{ token_address: '0xtoken1', balance: '100' }] });
|
|
87
|
+
|
|
88
|
+
const data = await server.moralisGetWalletTokenBalances(req);
|
|
89
|
+
|
|
90
|
+
data.should.deep.equal([{ token_address: '0xtoken1', balance: '100' }]);
|
|
91
|
+
stub.getCall(0).args[0].should.deep.equal({
|
|
92
|
+
address: '0xabc',
|
|
93
|
+
chain: '0x1',
|
|
94
|
+
toBlock: 100,
|
|
95
|
+
tokenAddresses: ['0xtoken1'],
|
|
96
|
+
excludeSpam: true,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should reject if the SDK call fails', async () => {
|
|
101
|
+
sinon.stub(Moralis.EvmApi.token, 'getWalletTokenBalances').rejects(new Error('sdk error'));
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await server.moralisGetWalletTokenBalances(req);
|
|
105
|
+
should.fail('should have thrown');
|
|
106
|
+
} catch (err) {
|
|
107
|
+
err.message.should.equal('sdk error');
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('#moralisGetTokenAllowance', () => {
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
req = {
|
|
115
|
+
headers: {},
|
|
116
|
+
body: {
|
|
117
|
+
ownerAddress: '0xowner',
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should call the approvals endpoint with server-side credentials using ownerAddress', async () => {
|
|
123
|
+
const data = await server.moralisGetTokenAllowance(req);
|
|
124
|
+
should.exist(data);
|
|
125
|
+
lastGet.url.should.equal('https://deep-index.moralis.io/api/v2.2/wallets/0xowner/approvals');
|
|
126
|
+
lastGet.opts.headers['X-Api-Key'].should.equal('moralisApiKey1');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should fall back to address when ownerAddress is not provided', async () => {
|
|
130
|
+
req.body = { address: '0xaddr' };
|
|
131
|
+
await server.moralisGetTokenAllowance(req);
|
|
132
|
+
lastGet.url.should.equal('https://deep-index.moralis.io/api/v2.2/wallets/0xaddr/approvals');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should reject if neither ownerAddress nor address is provided', async () => {
|
|
136
|
+
req.body = {};
|
|
137
|
+
try {
|
|
138
|
+
await server.moralisGetTokenAllowance(req);
|
|
139
|
+
should.fail('should have thrown');
|
|
140
|
+
} catch (err) {
|
|
141
|
+
err.message.should.contain('request missing arguments');
|
|
142
|
+
should.not.exist(lastGet);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should convert a numeric chain to hex in the query string', async () => {
|
|
147
|
+
req.body.chain = 1;
|
|
148
|
+
await server.moralisGetTokenAllowance(req);
|
|
149
|
+
lastGet.url.should.contain('chain=0x1');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should apply the legacy spender/allowance workaround when spenderAddress and ownerAddress are given', async () => {
|
|
153
|
+
req.body = {
|
|
154
|
+
ownerAddress: '0xowner',
|
|
155
|
+
address: '0xtoken',
|
|
156
|
+
spenderAddress: '0xSPENDER',
|
|
157
|
+
};
|
|
158
|
+
const fakeRequest2 = {
|
|
159
|
+
get: (_url, _opts, _cb) => {
|
|
160
|
+
return _cb(null, {
|
|
161
|
+
statusCode: 200,
|
|
162
|
+
body: {
|
|
163
|
+
result: [
|
|
164
|
+
{
|
|
165
|
+
spender: { address: '0xspender' },
|
|
166
|
+
token: { address: '0xtoken' },
|
|
167
|
+
value: '42',
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
server.request = fakeRequest2;
|
|
175
|
+
|
|
176
|
+
const data = await server.moralisGetTokenAllowance(req);
|
|
177
|
+
data.should.deep.equal({ allowance: '42' });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should default the allowance to 0 when no matching spender is found', async () => {
|
|
181
|
+
req.body = {
|
|
182
|
+
ownerAddress: '0xowner',
|
|
183
|
+
address: '0xtoken',
|
|
184
|
+
spenderAddress: '0xnotfound',
|
|
185
|
+
};
|
|
186
|
+
const fakeRequest2 = {
|
|
187
|
+
get: (_url, _opts, _cb) => {
|
|
188
|
+
return _cb(null, { statusCode: 200, body: { result: [] } });
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
server.request = fakeRequest2;
|
|
192
|
+
|
|
193
|
+
const data = await server.moralisGetTokenAllowance(req);
|
|
194
|
+
data.should.deep.equal({ allowance: '0' });
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should return error if moralis is commented in config', async () => {
|
|
198
|
+
config.moralis = undefined;
|
|
199
|
+
try {
|
|
200
|
+
await server.moralisGetTokenAllowance(req);
|
|
201
|
+
should.fail('should have thrown');
|
|
202
|
+
} catch (err) {
|
|
203
|
+
err.message.should.equal('Moralis missing credentials');
|
|
204
|
+
should.not.exist(lastGet);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should return error if get returns error', async () => {
|
|
209
|
+
const fakeRequest2 = {
|
|
210
|
+
get: (_url, _opts, _cb) => { return _cb(new Error('Error'), null); },
|
|
211
|
+
};
|
|
212
|
+
server.request = fakeRequest2;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
await server.moralisGetTokenAllowance(req);
|
|
216
|
+
should.fail('should have thrown');
|
|
217
|
+
} catch (err) {
|
|
218
|
+
err.message.should.equal('Error');
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('#moralisGetNativeBalance', () => {
|
|
224
|
+
it('should resolve response.raw and pass through the request params', async () => {
|
|
225
|
+
req = { headers: {}, body: { address: '0xabc', chain: '0x1', toBlock: 100 } };
|
|
226
|
+
const stub = sinon.stub(Moralis.EvmApi.balance, 'getNativeBalance')
|
|
227
|
+
.resolves({ raw: { balance: '1000000000000000000' } });
|
|
228
|
+
|
|
229
|
+
const data = await server.moralisGetNativeBalance(req);
|
|
230
|
+
|
|
231
|
+
data.should.deep.equal({ balance: '1000000000000000000' });
|
|
232
|
+
stub.getCall(0).args[0].should.deep.equal({ address: '0xabc', chain: '0x1', toBlock: 100 });
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('#moralisGetTokenPrice', () => {
|
|
237
|
+
it('should resolve response.raw and pass through the request params', async () => {
|
|
238
|
+
req = {
|
|
239
|
+
headers: {},
|
|
240
|
+
body: { address: '0xabc', chain: '0x1', include: 'percent_change', exchange: 'uniswapv2', toBlock: 100 }
|
|
241
|
+
};
|
|
242
|
+
const stub = sinon.stub(Moralis.EvmApi.token, 'getTokenPrice')
|
|
243
|
+
.resolves({ raw: { usdPrice: 5 } });
|
|
244
|
+
|
|
245
|
+
const data = await server.moralisGetTokenPrice(req);
|
|
246
|
+
|
|
247
|
+
data.should.deep.equal({ usdPrice: 5 });
|
|
248
|
+
stub.getCall(0).args[0].should.deep.equal({
|
|
249
|
+
address: '0xabc',
|
|
250
|
+
chain: '0x1',
|
|
251
|
+
include: 'percent_change',
|
|
252
|
+
exchange: 'uniswapv2',
|
|
253
|
+
toBlock: 100,
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe('#moralisGetMultipleERC20TokenPrices', () => {
|
|
259
|
+
it('should resolve response.raw and call the SDK with (params, body)', async () => {
|
|
260
|
+
req = {
|
|
261
|
+
headers: {},
|
|
262
|
+
body: {
|
|
263
|
+
chain: '0x1',
|
|
264
|
+
include: 'percent_change',
|
|
265
|
+
tokens: [{ tokenAddress: '0xaaa' }, { tokenAddress: '0xbbb' }],
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const stub = sinon.stub(Moralis.EvmApi.token, 'getMultipleTokenPrices')
|
|
269
|
+
.resolves({ raw: [{ tokenAddress: '0xaaa', usdPrice: 1 }] });
|
|
270
|
+
|
|
271
|
+
const data = await server.moralisGetMultipleERC20TokenPrices(req);
|
|
272
|
+
|
|
273
|
+
data.should.deep.equal([{ tokenAddress: '0xaaa', usdPrice: 1 }]);
|
|
274
|
+
stub.getCall(0).args[0].should.deep.equal({ chain: '0x1', include: 'percent_change' });
|
|
275
|
+
stub.getCall(0).args[1].should.deep.equal({ tokens: req.body.tokens });
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('#moralisGetERC20TokenBalancesWithPricesByWallet', () => {
|
|
280
|
+
beforeEach(() => {
|
|
281
|
+
req = {
|
|
282
|
+
headers: {},
|
|
283
|
+
body: { address: '0xabc' }
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should call the tokens endpoint with server-side credentials', async () => {
|
|
288
|
+
const data = await server.moralisGetERC20TokenBalancesWithPricesByWallet(req);
|
|
289
|
+
should.exist(data);
|
|
290
|
+
lastGet.url.should.equal('https://deep-index.moralis.io/api/v2.2/wallets/0xabc/tokens');
|
|
291
|
+
lastGet.opts.headers['X-Api-Key'].should.equal('moralisApiKey1');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should include all optional query params when provided', async () => {
|
|
295
|
+
req.body = {
|
|
296
|
+
address: '0xabc',
|
|
297
|
+
chain: '0x1',
|
|
298
|
+
toBlock: 100,
|
|
299
|
+
tokenAddresses: '0xtoken1',
|
|
300
|
+
excludeSpam: true,
|
|
301
|
+
cursor: 'abc',
|
|
302
|
+
limit: 10,
|
|
303
|
+
excludeNative: true,
|
|
304
|
+
};
|
|
305
|
+
await server.moralisGetERC20TokenBalancesWithPricesByWallet(req);
|
|
306
|
+
|
|
307
|
+
lastGet.url.should.contain('chain=0x1');
|
|
308
|
+
lastGet.url.should.contain('to_block=100');
|
|
309
|
+
lastGet.url.should.contain('token_addresses=0xtoken1');
|
|
310
|
+
lastGet.url.should.contain('exclude_spam=true');
|
|
311
|
+
lastGet.url.should.contain('cursor=abc');
|
|
312
|
+
lastGet.url.should.contain('limit=10');
|
|
313
|
+
lastGet.url.should.contain('exclude_native=true');
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should reject if address is missing', async () => {
|
|
317
|
+
req.body = {};
|
|
318
|
+
try {
|
|
319
|
+
await server.moralisGetERC20TokenBalancesWithPricesByWallet(req);
|
|
320
|
+
should.fail('should have thrown');
|
|
321
|
+
} catch (err) {
|
|
322
|
+
err.message.should.contain('request missing arguments');
|
|
323
|
+
should.not.exist(lastGet);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should return error if moralis is commented in config', async () => {
|
|
328
|
+
config.moralis = undefined;
|
|
329
|
+
try {
|
|
330
|
+
await server.moralisGetERC20TokenBalancesWithPricesByWallet(req);
|
|
331
|
+
should.fail('should have thrown');
|
|
332
|
+
} catch (err) {
|
|
333
|
+
err.message.should.equal('Moralis missing credentials');
|
|
334
|
+
should.not.exist(lastGet);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('#moralisGetTransactionVerbose', () => {
|
|
340
|
+
beforeEach(() => {
|
|
341
|
+
req = {
|
|
342
|
+
headers: {},
|
|
343
|
+
body: { transactionHash: '0xhash', chain: '0x89' }
|
|
344
|
+
};
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should resolve response.raw and pass through the request params', async () => {
|
|
348
|
+
const stub = sinon.stub(Moralis.EvmApi.transaction, 'getTransactionVerbose')
|
|
349
|
+
.resolves({ raw: { hash: '0xhash', logs: [] } });
|
|
350
|
+
|
|
351
|
+
const data = await server.moralisGetTransactionVerbose(req);
|
|
352
|
+
|
|
353
|
+
data.should.deep.equal({ hash: '0xhash', logs: [] });
|
|
354
|
+
stub.getCall(0).args[0].should.deep.equal({ transactionHash: '0xhash', chain: '0x89' });
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should reject if the SDK call fails', async () => {
|
|
358
|
+
sinon.stub(Moralis.EvmApi.transaction, 'getTransactionVerbose').rejects(new Error('sdk error'));
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
await server.moralisGetTransactionVerbose(req);
|
|
362
|
+
should.fail('should have thrown');
|
|
363
|
+
} catch (err) {
|
|
364
|
+
err.message.should.equal('sdk error');
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('should resolve null when the SDK finds no matching transaction', async () => {
|
|
369
|
+
sinon.stub(Moralis.EvmApi.transaction, 'getTransactionVerbose').resolves(null);
|
|
370
|
+
|
|
371
|
+
const data = await server.moralisGetTransactionVerbose(req);
|
|
372
|
+
should.equal(data, null);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
describe('#moralisGetSolWalletPortfolio', () => {
|
|
377
|
+
beforeEach(() => {
|
|
378
|
+
req = {
|
|
379
|
+
headers: {},
|
|
380
|
+
body: { address: 'solWalletAddress' }
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should resolve response.raw and default to mainnet', async () => {
|
|
385
|
+
const stub = sinon.stub(Moralis.SolApi.account, 'getPortfolio')
|
|
386
|
+
.resolves({ raw: { tokens: [] } });
|
|
387
|
+
|
|
388
|
+
const data = await server.moralisGetSolWalletPortfolio(req);
|
|
389
|
+
|
|
390
|
+
data.should.deep.equal({ tokens: [] });
|
|
391
|
+
stub.getCall(0).args[0].should.deep.equal({ address: 'solWalletAddress', network: 'mainnet' });
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should prefer network over chain when both are provided', async () => {
|
|
395
|
+
const stub = sinon.stub(Moralis.SolApi.account, 'getPortfolio').resolves({ raw: {} });
|
|
396
|
+
req.body.network = 'testnet';
|
|
397
|
+
req.body.chain = 'devnet';
|
|
398
|
+
|
|
399
|
+
await server.moralisGetSolWalletPortfolio(req);
|
|
400
|
+
stub.getCall(0).args[0].network.should.equal('testnet');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should map hex-encoded and numeric chain ids to the matching network', async () => {
|
|
404
|
+
const stub = sinon.stub(Moralis.SolApi.account, 'getPortfolio').resolves({ raw: {} });
|
|
405
|
+
|
|
406
|
+
req.body.chain = '0x66';
|
|
407
|
+
await server.moralisGetSolWalletPortfolio(req);
|
|
408
|
+
stub.getCall(0).args[0].network.should.equal('testnet');
|
|
409
|
+
|
|
410
|
+
req.body.chain = undefined;
|
|
411
|
+
req.body.network = 101; // 0x65
|
|
412
|
+
await server.moralisGetSolWalletPortfolio(req);
|
|
413
|
+
stub.getCall(1).args[0].network.should.equal('devnet');
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe('#moralisGetMultipleSolTokenPrices', () => {
|
|
418
|
+
beforeEach(() => {
|
|
419
|
+
req = {
|
|
420
|
+
headers: {},
|
|
421
|
+
body: {
|
|
422
|
+
addresses: ['So11111111111111111111111111111111111111112'],
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should call the solana gateway prices endpoint with server-side credentials', async () => {
|
|
428
|
+
const data = await server.moralisGetMultipleSolTokenPrices(req);
|
|
429
|
+
should.exist(data);
|
|
430
|
+
lastPost.url.should.equal('https://solana-gateway.moralis.io/token/mainnet/prices');
|
|
431
|
+
lastPost.opts.headers['X-Api-Key'].should.equal('moralisApiKey1');
|
|
432
|
+
lastPost.opts.body.addresses.should.deep.equal(req.body.addresses);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should only use the devnet network when explicitly requested, defaulting to mainnet otherwise', async () => {
|
|
436
|
+
req.body.network = 'devnet';
|
|
437
|
+
await server.moralisGetMultipleSolTokenPrices(req);
|
|
438
|
+
lastPost.url.should.equal('https://solana-gateway.moralis.io/token/devnet/prices');
|
|
439
|
+
|
|
440
|
+
req.body.network = 'whatever';
|
|
441
|
+
await server.moralisGetMultipleSolTokenPrices(req);
|
|
442
|
+
lastPost.url.should.equal('https://solana-gateway.moralis.io/token/mainnet/prices');
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should reject when addresses is missing or not an array', async () => {
|
|
446
|
+
delete req.body.addresses;
|
|
447
|
+
try {
|
|
448
|
+
await server.moralisGetMultipleSolTokenPrices(req);
|
|
449
|
+
should.fail('should have thrown');
|
|
450
|
+
} catch (err) {
|
|
451
|
+
err.message.should.contain('request missing arguments');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
req.body.addresses = 'notAnArray';
|
|
455
|
+
try {
|
|
456
|
+
await server.moralisGetMultipleSolTokenPrices(req);
|
|
457
|
+
should.fail('should have thrown');
|
|
458
|
+
} catch (err) {
|
|
459
|
+
err.message.should.contain('request missing arguments');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
should.not.exist(lastPost);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should return error if moralis is commented in config', async () => {
|
|
466
|
+
config.moralis = undefined;
|
|
467
|
+
try {
|
|
468
|
+
await server.moralisGetMultipleSolTokenPrices(req);
|
|
469
|
+
should.fail('should have thrown');
|
|
470
|
+
} catch (err) {
|
|
471
|
+
err.message.should.equal('Moralis missing credentials');
|
|
472
|
+
should.not.exist(lastPost);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
});
|
package/ts_build/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.3",
|
|
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.3",
|
|
45
|
+
"@bitpay-labs/bitcore-tss": "^11.10.3",
|
|
46
|
+
"@bitpay-labs/crypto-wallet-core": "^11.10.3",
|
|
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
|
{
|