@bitpay-labs/bitcore-wallet-service 11.6.5 → 11.7.0
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 +22 -11
- package/src/externalservices/moonpay.ts +3 -0
- package/src/lib/blockchainexplorers/v8.ts +42 -0
- package/src/lib/chain/eth/index.ts +3 -2
- package/src/lib/expressapp.ts +25 -0
- package/src/lib/model/txproposal.ts +8 -0
- package/src/lib/routes/aave.ts +48 -0
- package/src/lib/routes/tss.ts +2 -2
- package/src/lib/server.ts +183 -6
- package/src/lib/tss.ts +9 -4
- package/test/expressapp.test.ts +170 -0
- package/test/integration/server.test.ts +333 -0
- package/test/v8.test.ts +103 -0
- package/ts_build/package.json +22 -11
- package/ts_build/src/externalservices/banxa.d.ts.map +1 -1
- package/ts_build/src/externalservices/banxa.js +0 -1
- package/ts_build/src/externalservices/banxa.js.map +1 -1
- package/ts_build/src/externalservices/changelly.d.ts +2 -2
- 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/lib/blockchainexplorers/v8.d.ts +12 -0
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.js +39 -0
- package/ts_build/src/lib/blockchainexplorers/v8.js.map +1 -1
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +3 -2
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/expressapp.d.ts.map +1 -1
- package/ts_build/src/lib/expressapp.js +23 -0
- package/ts_build/src/lib/expressapp.js.map +1 -1
- package/ts_build/src/lib/model/txproposal.d.ts +3 -0
- package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposal.js +6 -0
- package/ts_build/src/lib/model/txproposal.js.map +1 -1
- package/ts_build/src/lib/routes/aave.d.ts +12 -0
- package/ts_build/src/lib/routes/aave.d.ts.map +1 -0
- package/ts_build/src/lib/routes/aave.js +47 -0
- package/ts_build/src/lib/routes/aave.js.map +1 -0
- package/ts_build/src/lib/routes/tss.js +2 -2
- package/ts_build/src/lib/routes/tss.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 +131 -6
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/tss.d.ts +1 -0
- package/ts_build/src/lib/tss.d.ts.map +1 -1
- package/ts_build/src/lib/tss.js +6 -2
- package/ts_build/src/lib/tss.js.map +1 -1
- package/ts_build/test/expressapp.test.js +162 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/server.test.js +281 -0
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/v8.test.js +89 -0
- package/ts_build/test/v8.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.
|
|
5
|
+
"version": "11.7.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "ts_build/src/index.js",
|
|
8
8
|
"types": "./ts_build/src/index.d.ts",
|
|
@@ -24,10 +24,9 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"start": "npm run clean && npm run build && ./start.sh",
|
|
26
26
|
"stop": "./stop.sh",
|
|
27
|
-
"coverage": "npm run compile &&
|
|
28
|
-
"test": "npm run compile && mocha --exit
|
|
27
|
+
"coverage": "npm run compile && nyc mocha 'ts_build/test/**/*.test.js'",
|
|
28
|
+
"test": "npm run compile && mocha --exit 'ts_build/test/**/*.test.js'",
|
|
29
29
|
"test:ci": "BWS_LOG_LEVEL=none npm run test",
|
|
30
|
-
"coveralls": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage",
|
|
31
30
|
"build": "tsc && npm run cp:templates",
|
|
32
31
|
"build:prod": "tsc -p tsconfig.prod.json && npm run cp:templates",
|
|
33
32
|
"build:docker": "docker build -t bitpay/bitcore-wallet-service:latest -f ./Dockerfile ../..",
|
|
@@ -44,8 +43,8 @@
|
|
|
44
43
|
"cp:templates": "cp -r ./templates ./ts_build/templates"
|
|
45
44
|
},
|
|
46
45
|
"dependencies": {
|
|
47
|
-
"@bitpay-labs/bitcore-tss": "^11.
|
|
48
|
-
"@bitpay-labs/crypto-wallet-core": "^11.
|
|
46
|
+
"@bitpay-labs/bitcore-tss": "^11.7.0",
|
|
47
|
+
"@bitpay-labs/crypto-wallet-core": "^11.7.0",
|
|
49
48
|
"@sendgrid/mail": "6.5.4",
|
|
50
49
|
"abi-decoder": "2.4.0",
|
|
51
50
|
"async": "0.9.2",
|
|
@@ -55,7 +54,6 @@
|
|
|
55
54
|
"express": "4.19.2",
|
|
56
55
|
"express-rate-limit": "2.14.2",
|
|
57
56
|
"inherits": "2.0.4",
|
|
58
|
-
"istanbul": "0.4.5",
|
|
59
57
|
"json-stable-stringify": "1.0.1",
|
|
60
58
|
"juice": "^11.0.1",
|
|
61
59
|
"lodash": "4.17.19",
|
|
@@ -87,15 +85,28 @@
|
|
|
87
85
|
"@types/nodemailer": "^4.6.6",
|
|
88
86
|
"@types/request": "^2.48.1",
|
|
89
87
|
"chai": "^5.2.0",
|
|
90
|
-
"coveralls": "^3.0.2",
|
|
91
88
|
"memdown": "^1.0.0",
|
|
92
89
|
"mocha": "^5.2.0",
|
|
93
|
-
"
|
|
90
|
+
"nyc": "^17.1.0",
|
|
94
91
|
"sinon": "^7.1.1",
|
|
95
92
|
"supertest": "*",
|
|
96
|
-
"tsx": "^4.21.0",
|
|
97
93
|
"typescript": "^5.7.3"
|
|
98
94
|
},
|
|
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
|
+
},
|
|
99
110
|
"bitcoreNode": "./bitcorenode",
|
|
100
111
|
"contributors": [
|
|
101
112
|
{
|
|
@@ -115,5 +126,5 @@
|
|
|
115
126
|
"email": "kjoseph@bitpay.com"
|
|
116
127
|
}
|
|
117
128
|
],
|
|
118
|
-
"gitHead": "
|
|
129
|
+
"gitHead": "6c5a1566aa725431db579f3c89a5c9f186bf5a30"
|
|
119
130
|
}
|
|
@@ -214,6 +214,9 @@ export class MoonpayService {
|
|
|
214
214
|
qs.push('externalTransactionId=' + encodeURIComponent(req.body.externalTransactionId));
|
|
215
215
|
qs.push('redirectURL=' + encodeURIComponent(req.body.redirectURL));
|
|
216
216
|
if (req.body.lockAmount) qs.push('lockAmount=' + encodeURIComponent(req.body.lockAmount));
|
|
217
|
+
if (req.body.colorCode) qs.push('colorCode=' + encodeURIComponent(req.body.colorCode));
|
|
218
|
+
if (req.body.theme) qs.push('theme=' + encodeURIComponent(req.body.theme));
|
|
219
|
+
if (req.body.language) qs.push('language=' + encodeURIComponent(req.body.language));
|
|
217
220
|
if (req.body.showWalletAddressForm)
|
|
218
221
|
qs.push('showWalletAddressForm=' + encodeURIComponent(req.body.showWalletAddressForm));
|
|
219
222
|
if (req.body.paymentMethod) qs.push('paymentMethod=' + encodeURIComponent(req.body.paymentMethod));
|
|
@@ -486,6 +486,48 @@ export class V8 {
|
|
|
486
486
|
});
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
getAaveUserAccountData(opts: { address: string; version?: string }, cb) {
|
|
490
|
+
const url = this.baseUrl + '/aave/account/' + opts.address + '?version=' + (opts.version || 'v3');
|
|
491
|
+
logger.debug('[v8.js] GETTING AAVE USER ACCOUNT DATA %o', url);
|
|
492
|
+
this.request
|
|
493
|
+
.get(url, {})
|
|
494
|
+
.then(accountData => {
|
|
495
|
+
accountData = JSON.parse(accountData);
|
|
496
|
+
return cb(null, accountData);
|
|
497
|
+
})
|
|
498
|
+
.catch(err => {
|
|
499
|
+
return cb(err);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
getAaveReserveData(opts: { asset: string; version?: string }, cb) {
|
|
504
|
+
const url = this.baseUrl + '/aave/reserve/' + opts.asset + '?version=' + (opts.version || 'v3');
|
|
505
|
+
logger.debug('[v8.js] GETTING AAVE RESERVE DATA %o', url);
|
|
506
|
+
this.request
|
|
507
|
+
.get(url, {})
|
|
508
|
+
.then(reserveData => {
|
|
509
|
+
reserveData = JSON.parse(reserveData);
|
|
510
|
+
return cb(null, reserveData);
|
|
511
|
+
})
|
|
512
|
+
.catch(err => {
|
|
513
|
+
return cb(err);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
getAaveReserveTokensAddresses(opts: { asset: string; version?: string }, cb) {
|
|
518
|
+
const url = this.baseUrl + '/aave/reserve-tokens/' + opts.asset + '?version=' + (opts.version || 'v3');
|
|
519
|
+
logger.debug('[v8.js] GETTING AAVE RESERVE TOKENS ADDRESSES %o', url);
|
|
520
|
+
this.request
|
|
521
|
+
.get(url, {})
|
|
522
|
+
.then(tokensAddresses => {
|
|
523
|
+
tokensAddresses = JSON.parse(tokensAddresses);
|
|
524
|
+
return cb(null, tokensAddresses);
|
|
525
|
+
})
|
|
526
|
+
.catch(err => {
|
|
527
|
+
return cb(err);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
489
531
|
getMultisigTxpsInfo(opts: { multisigContractAddress: string }, cb) {
|
|
490
532
|
const url = this.baseUrl + '/ethmultisig/txps/' + opts.multisigContractAddress;
|
|
491
533
|
logger.debug('[v8.js] CHECKING CONTRACT TXPS INFO %o', url);
|
|
@@ -317,9 +317,10 @@ export class EthChain implements IChain {
|
|
|
317
317
|
}
|
|
318
318
|
const unsignedTxs = [];
|
|
319
319
|
|
|
320
|
+
const nonceNum = txp.nonce != null ? Number(txp.nonce) : 0;
|
|
320
321
|
if (multiSendContractAddress) {
|
|
321
322
|
const multiSendParams = {
|
|
322
|
-
nonce:
|
|
323
|
+
nonce: nonceNum,
|
|
323
324
|
recipients,
|
|
324
325
|
contractAddress: multiSendContractAddress
|
|
325
326
|
};
|
|
@@ -330,7 +331,7 @@ export class EthChain implements IChain {
|
|
|
330
331
|
// Uses gas limit from the txp output level
|
|
331
332
|
const params = {
|
|
332
333
|
...recipients[index],
|
|
333
|
-
nonce:
|
|
334
|
+
nonce: nonceNum + Number(index),
|
|
334
335
|
recipients: [recipients[index]]
|
|
335
336
|
};
|
|
336
337
|
unsignedTxs.push(Transactions.create({ ...txp, chain, ...params }));
|
package/src/lib/expressapp.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { Common } from './common';
|
|
|
13
13
|
import { ClientError } from './errors/clienterror';
|
|
14
14
|
import { Errors } from './errors/errordefinitions';
|
|
15
15
|
import { logger, transports } from './logger';
|
|
16
|
+
import { AaveRouter } from './routes/aave';
|
|
16
17
|
import { error } from './routes/helpers';
|
|
17
18
|
import { createWalletLimiter } from './routes/middleware/createWalletLimiter';
|
|
18
19
|
import { LogMiddleware } from './routes/middleware/log';
|
|
@@ -973,6 +974,16 @@ export class ExpressApp {
|
|
|
973
974
|
});
|
|
974
975
|
});
|
|
975
976
|
|
|
977
|
+
router.post('/v1/token/allowance', async (req, res) => {
|
|
978
|
+
try {
|
|
979
|
+
const server = getServer(req, res);
|
|
980
|
+
const allowance = await server.getTokenAllowance(req.body);
|
|
981
|
+
res.json(allowance);
|
|
982
|
+
} catch (err) {
|
|
983
|
+
returnError(err, res, req);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
|
|
976
987
|
router.get('/v1/sendmaxinfo/', (req, res) => {
|
|
977
988
|
getServerWithAuth(req, res, server => {
|
|
978
989
|
const q = req.query;
|
|
@@ -1069,6 +1080,19 @@ export class ExpressApp {
|
|
|
1069
1080
|
});
|
|
1070
1081
|
*/
|
|
1071
1082
|
|
|
1083
|
+
router.post('/v1/txproposals/:id/prepare/', (req, res) => {
|
|
1084
|
+
getServerWithAuth(req, res, server => {
|
|
1085
|
+
req.body.txProposalId = req.params['id'];
|
|
1086
|
+
server.prepareTx(req.body, (err, txp) => {
|
|
1087
|
+
if (err) return returnError(err, res, req);
|
|
1088
|
+
res.json(txp);
|
|
1089
|
+
res.end();
|
|
1090
|
+
});
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
|
|
1072
1096
|
//
|
|
1073
1097
|
router.post('/v1/txproposals/:id/publish/', (req, res) => {
|
|
1074
1098
|
getServerWithAuth(req, res, server => {
|
|
@@ -2391,6 +2415,7 @@ export class ExpressApp {
|
|
|
2391
2415
|
});
|
|
2392
2416
|
|
|
2393
2417
|
/** Imported routes */
|
|
2418
|
+
router.use(new AaveRouter({ returnError, getServer }).router);
|
|
2394
2419
|
router.use(new TssRouter({ returnError, opts }).router);
|
|
2395
2420
|
|
|
2396
2421
|
|
|
@@ -66,6 +66,7 @@ export interface ITxProposal {
|
|
|
66
66
|
signingMethod: string;
|
|
67
67
|
lowFees?: boolean;
|
|
68
68
|
nonce?: number | string;
|
|
69
|
+
deferNonce?: boolean;
|
|
69
70
|
gasPrice?: number;
|
|
70
71
|
maxGasFee?: number;
|
|
71
72
|
priorityGasFee?: number;
|
|
@@ -150,6 +151,7 @@ export class TxProposal implements ITxProposal {
|
|
|
150
151
|
lowFees?: boolean;
|
|
151
152
|
raw?: Array<string> | string;
|
|
152
153
|
nonce?: number | string;
|
|
154
|
+
deferNonce?: boolean;
|
|
153
155
|
gasPrice?: number;
|
|
154
156
|
maxGasFee?: number;
|
|
155
157
|
priorityGasFee?: number;
|
|
@@ -273,6 +275,7 @@ export class TxProposal implements ITxProposal {
|
|
|
273
275
|
x.txType = opts.txType;
|
|
274
276
|
x.from = opts.from;
|
|
275
277
|
x.nonce = opts.nonce;
|
|
278
|
+
x.deferNonce = opts.deferNonce;
|
|
276
279
|
x.gasLimit = opts.gasLimit; // Backward compatibility for BWC <= 8.9.0
|
|
277
280
|
x.data = opts.data; // Backward compatibility for BWC <= 8.9.0
|
|
278
281
|
x.tokenAddress = opts.tokenAddress;
|
|
@@ -363,6 +366,7 @@ export class TxProposal implements ITxProposal {
|
|
|
363
366
|
x.txType = obj.txType;
|
|
364
367
|
x.from = obj.from;
|
|
365
368
|
x.nonce = obj.nonce;
|
|
369
|
+
x.deferNonce = obj.deferNonce;
|
|
366
370
|
x.gasLimit = obj.gasLimit; // Backward compatibility for BWC <= 8.9.0
|
|
367
371
|
x.data = obj.data; // Backward compatibility for BWC <= 8.9.0
|
|
368
372
|
x.tokenAddress = obj.tokenAddress;
|
|
@@ -531,6 +535,10 @@ export class TxProposal implements ITxProposal {
|
|
|
531
535
|
return !!this.refreshOnPublish;
|
|
532
536
|
}
|
|
533
537
|
|
|
538
|
+
hasMutableTxData() {
|
|
539
|
+
return this.isRepublishEnabled() || !!this.deferNonce;
|
|
540
|
+
}
|
|
541
|
+
|
|
534
542
|
isTemporary() {
|
|
535
543
|
return this.status === 'temporary';
|
|
536
544
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import * as Types from '../../types/expressapp';
|
|
3
|
+
|
|
4
|
+
interface AaveRouterOpts {
|
|
5
|
+
returnError: Types.ReturnErrorFn;
|
|
6
|
+
getServer: Types.GetServerFn;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class AaveRouter {
|
|
10
|
+
router: express.Router;
|
|
11
|
+
|
|
12
|
+
constructor(params: AaveRouterOpts) {
|
|
13
|
+
const { returnError, getServer } = params;
|
|
14
|
+
const router = express.Router();
|
|
15
|
+
|
|
16
|
+
router.post('/v1/service/aave/userAccountData', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const server = getServer(req, res);
|
|
19
|
+
const accountData = await server.getAaveUserAccountData(req.body);
|
|
20
|
+
res.json(accountData);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
returnError(err, res, req);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
router.post('/v1/service/aave/reserveData', async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const server = getServer(req, res);
|
|
29
|
+
const reserveData = await server.getAaveReserveData(req.body);
|
|
30
|
+
res.json(reserveData);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
returnError(err, res, req);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
router.post('/v1/service/aave/reserveTokensAddresses', async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
const server = getServer(req, res);
|
|
39
|
+
const tokensAddresses = await server.getAaveReserveTokensAddresses(req.body);
|
|
40
|
+
res.json(tokensAddresses);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
returnError(err, res, req);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
this.router = router;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/lib/routes/tss.ts
CHANGED
|
@@ -100,8 +100,8 @@ export class TssRouter {
|
|
|
100
100
|
try {
|
|
101
101
|
const { id, round } = req.params as { [key: string]: string };
|
|
102
102
|
const copayerId = req.headers['x-identity'];
|
|
103
|
-
const { messages, signature } = await TssSign.getMessagesForParty({ id, round: parseInt(round), copayerId });
|
|
104
|
-
return res.json({ messages, signature });
|
|
103
|
+
const { messages, signature, participants } = await TssSign.getMessagesForParty({ id, round: parseInt(round), copayerId });
|
|
104
|
+
return res.json({ messages, signature, participants });
|
|
105
105
|
} catch (err) {
|
|
106
106
|
return returnError(err ?? 'unknown', res, req);
|
|
107
107
|
}
|
package/src/lib/server.ts
CHANGED
|
@@ -2559,6 +2559,120 @@ export class WalletService implements IWalletService {
|
|
|
2559
2559
|
});
|
|
2560
2560
|
}
|
|
2561
2561
|
|
|
2562
|
+
/**
|
|
2563
|
+
* Get ERC20 token allowance for a given owner/spender pair.
|
|
2564
|
+
* @param {Object} opts
|
|
2565
|
+
* @param {string} opts.chain - EVM chain name (e.g. 'eth', 'matic')
|
|
2566
|
+
* @param {string} opts.network - Network name (e.g. 'mainnet', 'sepolia')
|
|
2567
|
+
* @param {string} opts.tokenAddress - Token contract address
|
|
2568
|
+
* @param {string} opts.ownerAddress - Token owner address
|
|
2569
|
+
* @param {string} opts.spenderAddress - Spender contract address
|
|
2570
|
+
* @returns {Promise<number>} Token allowance amount
|
|
2571
|
+
*/
|
|
2572
|
+
getTokenAllowance(opts) {
|
|
2573
|
+
if (!checkRequired(opts, ['chain', 'network', 'tokenAddress', 'ownerAddress', 'spenderAddress'])) {
|
|
2574
|
+
return Promise.reject(new ClientError('getTokenAllowance request missing arguments'));
|
|
2575
|
+
}
|
|
2576
|
+
const bc = this._getBlockchainExplorer(opts.chain, opts.network);
|
|
2577
|
+
return new Promise((resolve, reject) => {
|
|
2578
|
+
if (!bc) return reject(new Error('Could not get blockchain explorer instance'));
|
|
2579
|
+
bc.getTokenAllowance(opts, (err, allowance) => {
|
|
2580
|
+
if (err) {
|
|
2581
|
+
this.logw('Error getting token allowance:', err);
|
|
2582
|
+
return reject(err);
|
|
2583
|
+
}
|
|
2584
|
+
return resolve(allowance);
|
|
2585
|
+
});
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
/**
|
|
2590
|
+
* Get Aave user account data (health factor, collateral, debt, etc.).
|
|
2591
|
+
* @param {Object} opts
|
|
2592
|
+
* @param {string} opts.chain - EVM chain name (e.g. 'eth', 'matic')
|
|
2593
|
+
* @param {string} opts.network - Network name (e.g. 'mainnet', 'sepolia')
|
|
2594
|
+
* @param {string} opts.address - User wallet address
|
|
2595
|
+
* @param {string} [opts.version='v3'] - Aave protocol version ('v2' or 'v3')
|
|
2596
|
+
* @returns {Promise<Object>} Aave account data (totalCollateralBase, totalDebtBase, healthFactor, etc.)
|
|
2597
|
+
*/
|
|
2598
|
+
getAaveUserAccountData(opts) {
|
|
2599
|
+
if (!checkRequired(opts, ['chain', 'network', 'address'])) {
|
|
2600
|
+
return Promise.reject(new ClientError('getAaveUserAccountData request missing arguments'));
|
|
2601
|
+
}
|
|
2602
|
+
if (opts.version && !['v2', 'v3'].includes(opts.version)) {
|
|
2603
|
+
return Promise.reject(new ClientError('Invalid Aave version. Supported: v2, v3'));
|
|
2604
|
+
}
|
|
2605
|
+
const bc = this._getBlockchainExplorer(opts.chain, opts.network);
|
|
2606
|
+
return new Promise((resolve, reject) => {
|
|
2607
|
+
if (!bc) return reject(new Error('Could not get blockchain explorer instance'));
|
|
2608
|
+
bc.getAaveUserAccountData(opts, (err, accountData) => {
|
|
2609
|
+
if (err) {
|
|
2610
|
+
this.logw('Error getting Aave user account data:', err);
|
|
2611
|
+
return reject(err);
|
|
2612
|
+
}
|
|
2613
|
+
return resolve(accountData);
|
|
2614
|
+
});
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
/**
|
|
2619
|
+
* Get Aave reserve data (variable borrow rate, etc.).
|
|
2620
|
+
* @param {Object} opts
|
|
2621
|
+
* @param {string} opts.chain - EVM chain name (e.g. 'eth', 'matic')
|
|
2622
|
+
* @param {string} opts.network - Network name (e.g. 'mainnet', 'sepolia')
|
|
2623
|
+
* @param {string} opts.asset - Reserve asset token address
|
|
2624
|
+
* @param {string} [opts.version='v3'] - Aave protocol version ('v2' or 'v3')
|
|
2625
|
+
* @returns {Promise<Object>} Aave reserve data (currentVariableBorrowRate, etc.)
|
|
2626
|
+
*/
|
|
2627
|
+
getAaveReserveData(opts) {
|
|
2628
|
+
if (!checkRequired(opts, ['chain', 'network', 'asset'])) {
|
|
2629
|
+
return Promise.reject(new ClientError('getAaveReserveData request missing arguments'));
|
|
2630
|
+
}
|
|
2631
|
+
if (opts.version && !['v2', 'v3'].includes(opts.version)) {
|
|
2632
|
+
return Promise.reject(new ClientError('Invalid Aave version. Supported: v2, v3'));
|
|
2633
|
+
}
|
|
2634
|
+
const bc = this._getBlockchainExplorer(opts.chain, opts.network);
|
|
2635
|
+
return new Promise((resolve, reject) => {
|
|
2636
|
+
if (!bc) return reject(new Error('Could not get blockchain explorer instance'));
|
|
2637
|
+
bc.getAaveReserveData(opts, (err, reserveData) => {
|
|
2638
|
+
if (err) {
|
|
2639
|
+
this.logw('Error getting Aave reserve data:', err);
|
|
2640
|
+
return reject(err);
|
|
2641
|
+
}
|
|
2642
|
+
return resolve(reserveData);
|
|
2643
|
+
});
|
|
2644
|
+
});
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
/**
|
|
2648
|
+
* Get Aave reserve token addresses (aToken, variableDebtToken, etc.).
|
|
2649
|
+
* @param {Object} opts
|
|
2650
|
+
* @param {string} opts.chain - EVM chain name (e.g. 'eth', 'matic')
|
|
2651
|
+
* @param {string} opts.network - Network name (e.g. 'mainnet', 'sepolia')
|
|
2652
|
+
* @param {string} opts.asset - Reserve asset token address
|
|
2653
|
+
* @param {string} [opts.version='v3'] - Aave protocol version ('v2' or 'v3')
|
|
2654
|
+
* @returns {Promise<Object>} Aave reserve token addresses (variableDebtTokenAddress, etc.)
|
|
2655
|
+
*/
|
|
2656
|
+
getAaveReserveTokensAddresses(opts) {
|
|
2657
|
+
if (!checkRequired(opts, ['chain', 'network', 'asset'])) {
|
|
2658
|
+
return Promise.reject(new ClientError('getAaveReserveTokensAddresses request missing arguments'));
|
|
2659
|
+
}
|
|
2660
|
+
if (opts.version && !['v2', 'v3'].includes(opts.version)) {
|
|
2661
|
+
return Promise.reject(new ClientError('Invalid Aave version. Supported: v2, v3'));
|
|
2662
|
+
}
|
|
2663
|
+
const bc = this._getBlockchainExplorer(opts.chain, opts.network);
|
|
2664
|
+
return new Promise((resolve, reject) => {
|
|
2665
|
+
if (!bc) return reject(new Error('Could not get blockchain explorer instance'));
|
|
2666
|
+
bc.getAaveReserveTokensAddresses(opts, (err, tokensAddresses) => {
|
|
2667
|
+
if (err) {
|
|
2668
|
+
this.logw('Error getting Aave reserve tokens addresses:', err);
|
|
2669
|
+
return reject(err);
|
|
2670
|
+
}
|
|
2671
|
+
return resolve(tokensAddresses);
|
|
2672
|
+
});
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2562
2676
|
getMultisigTxpsInfo(opts) {
|
|
2563
2677
|
const bc = this._getBlockchainExplorer(opts.chain || Defaults.EVM_CHAIN, opts.network);
|
|
2564
2678
|
return new Promise((resolve, reject) => {
|
|
@@ -2694,7 +2808,7 @@ export class WalletService implements IWalletService {
|
|
|
2694
2808
|
},
|
|
2695
2809
|
async next => {
|
|
2696
2810
|
// SOL is skipped since its a non necessary field that is expected to be provided by the client.
|
|
2697
|
-
if (!opts.nonce && !Constants.SVM_CHAINS[wallet.chain.toUpperCase()]) {
|
|
2811
|
+
if (!opts.nonce && !Constants.SVM_CHAINS[wallet.chain.toUpperCase()] && !opts.deferNonce) {
|
|
2698
2812
|
try {
|
|
2699
2813
|
opts.nonce = await ChainService.getTransactionCount(this, wallet, opts.from);
|
|
2700
2814
|
} catch (error) {
|
|
@@ -2794,7 +2908,8 @@ export class WalletService implements IWalletService {
|
|
|
2794
2908
|
memo: opts.memo,
|
|
2795
2909
|
fromAta: opts.fromAta,
|
|
2796
2910
|
decimals: opts.decimals,
|
|
2797
|
-
refreshOnPublish: opts.refreshOnPublish
|
|
2911
|
+
refreshOnPublish: opts.refreshOnPublish,
|
|
2912
|
+
deferNonce: opts.deferNonce
|
|
2798
2913
|
};
|
|
2799
2914
|
txp = TxProposal.create(txOpts);
|
|
2800
2915
|
next();
|
|
@@ -2906,7 +3021,7 @@ export class WalletService implements IWalletService {
|
|
|
2906
3021
|
this.storage.fetchTx(this.walletId, opts.txProposalId, (err, txp) => {
|
|
2907
3022
|
if (err) return cb(err);
|
|
2908
3023
|
if (!txp) return cb(Errors.TX_NOT_FOUND);
|
|
2909
|
-
if (!txp.isTemporary() && !txp.
|
|
3024
|
+
if (!txp.isTemporary() && !txp.hasMutableTxData()) return cb(null, txp);
|
|
2910
3025
|
|
|
2911
3026
|
const copayer = wallet.getCopayer(this.copayerId);
|
|
2912
3027
|
|
|
@@ -2920,7 +3035,7 @@ export class WalletService implements IWalletService {
|
|
|
2920
3035
|
let signingKey = this._getSigningKey(raw, opts.proposalSignature, copayer.requestPubKeys);
|
|
2921
3036
|
if (!signingKey) {
|
|
2922
3037
|
// If the txp has been published previously, we will verify the signature against the previously published raw tx
|
|
2923
|
-
if (txp.
|
|
3038
|
+
if (txp.hasMutableTxData() && txp.prePublishRaw) {
|
|
2924
3039
|
raw = txp.prePublishRaw;
|
|
2925
3040
|
signingKey = this._getSigningKey(raw, opts.proposalSignature, copayer.requestPubKeys);
|
|
2926
3041
|
}
|
|
@@ -2943,7 +3058,7 @@ export class WalletService implements IWalletService {
|
|
|
2943
3058
|
txp.status = 'pending';
|
|
2944
3059
|
ChainService.refreshTxData(this, txp, opts, (err, txp) => {
|
|
2945
3060
|
if (err) return cb(err);
|
|
2946
|
-
if (txp.
|
|
3061
|
+
if (txp.hasMutableTxData() && !txp.prePublishRaw) {
|
|
2947
3062
|
// We save the original raw transaction for verification on republish
|
|
2948
3063
|
txp.prePublishRaw = raw;
|
|
2949
3064
|
}
|
|
@@ -3207,7 +3322,7 @@ export class WalletService implements IWalletService {
|
|
|
3207
3322
|
try {
|
|
3208
3323
|
const txps = await this.getPendingTxsPromise({});
|
|
3209
3324
|
for (const t of txps) {
|
|
3210
|
-
if (t.id !== txp.id && t.nonce <= txp.nonce && t.status !== 'rejected') {
|
|
3325
|
+
if (t.id !== txp.id && t.nonce != null && txp.nonce != null && t.nonce <= txp.nonce && t.status !== 'rejected') {
|
|
3211
3326
|
return cb(Errors.TX_NONCE_CONFLICT);
|
|
3212
3327
|
}
|
|
3213
3328
|
}
|
|
@@ -3268,6 +3383,68 @@ export class WalletService implements IWalletService {
|
|
|
3268
3383
|
});
|
|
3269
3384
|
}
|
|
3270
3385
|
|
|
3386
|
+
/**
|
|
3387
|
+
* Prepare a transaction proposal for signing.
|
|
3388
|
+
* Assigns JIT values (nonce, and in the future: fee, gas) to a deferred txp.
|
|
3389
|
+
* Called by the client just before signing.
|
|
3390
|
+
* @param {Object} opts
|
|
3391
|
+
* @param {string} opts.txProposalId - The identifier of the transaction.
|
|
3392
|
+
*/
|
|
3393
|
+
prepareTx(opts, cb) {
|
|
3394
|
+
if (!checkRequired(opts, ['txProposalId'], cb)) return;
|
|
3395
|
+
|
|
3396
|
+
this._runLocked(cb, cb => {
|
|
3397
|
+
this.getWallet({}, (err, wallet) => {
|
|
3398
|
+
if (err) return cb(err);
|
|
3399
|
+
|
|
3400
|
+
this.storage.fetchTx(this.walletId, opts.txProposalId, async (err, txp) => {
|
|
3401
|
+
if (err) return cb(err);
|
|
3402
|
+
if (!txp) return cb(Errors.TX_NOT_FOUND);
|
|
3403
|
+
if (!txp.isPending()) return cb(Errors.TX_NOT_PENDING);
|
|
3404
|
+
|
|
3405
|
+
if (!txp.deferNonce) {
|
|
3406
|
+
// Not a deferred-nonce txp. Return it as-is
|
|
3407
|
+
return cb(null, txp);
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
if (!Constants.EVM_CHAINS[wallet.chain.toUpperCase()]) {
|
|
3411
|
+
return cb(null, txp);
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
try {
|
|
3415
|
+
// 1. Get confirmed nonce from blockchain
|
|
3416
|
+
const confirmedNonce = await ChainService.getTransactionCount(this, wallet, txp.from);
|
|
3417
|
+
|
|
3418
|
+
// 2. Get pending TXP nonces from BWS's own database
|
|
3419
|
+
const pendingTxps = await this.getPendingTxsPromise({});
|
|
3420
|
+
const pendingNonces = pendingTxps
|
|
3421
|
+
.filter(t => t.id !== txp.id && t.nonce != null && t.status !== 'rejected')
|
|
3422
|
+
.map(t => Number(t.nonce));
|
|
3423
|
+
|
|
3424
|
+
// 3. Calculate gap-free nonce
|
|
3425
|
+
let suggestedNonce = Number(confirmedNonce);
|
|
3426
|
+
const allNonces = pendingNonces.sort((a, b) => a - b);
|
|
3427
|
+
for (const n of allNonces) {
|
|
3428
|
+
if (n === suggestedNonce) {
|
|
3429
|
+
suggestedNonce++;
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
txp.nonce = suggestedNonce;
|
|
3434
|
+
|
|
3435
|
+
// 4. Store the updated txp
|
|
3436
|
+
this.storage.storeTx(this.walletId, txp, err => {
|
|
3437
|
+
if (err) return cb(err);
|
|
3438
|
+
return cb(null, txp);
|
|
3439
|
+
});
|
|
3440
|
+
} catch (err) {
|
|
3441
|
+
return cb(err);
|
|
3442
|
+
}
|
|
3443
|
+
});
|
|
3444
|
+
});
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3271
3448
|
_processBroadcast(txp, opts, cb) {
|
|
3272
3449
|
$.checkState(txp.txid, 'Failed state: txp.txid undefined at <_processBroadcast()>');
|
|
3273
3450
|
opts = opts || {};
|
package/src/lib/tss.ts
CHANGED
|
@@ -279,9 +279,9 @@ class TssKeyGenClass {
|
|
|
279
279
|
export const TssKeyGen = new TssKeyGenClass();
|
|
280
280
|
|
|
281
281
|
class TssSignClass {
|
|
282
|
-
async getMessagesForParty(params: { id: string; round: number; copayerId: string }): Promise<{ messages?: ITssSigMessageObject[]; signature?: ITssSigMessageObject['signature'] }> {
|
|
282
|
+
async getMessagesForParty(params: { id: string; round: number; copayerId: string }): Promise<{ messages?: ITssSigMessageObject[]; signature?: ITssSigMessageObject['signature']; participants?: string[] }> {
|
|
283
283
|
const { id, round, copayerId } = params;
|
|
284
|
-
|
|
284
|
+
|
|
285
285
|
const storage = WalletService.getStorage();
|
|
286
286
|
const session = await storage.fetchTssSigSession({ id });
|
|
287
287
|
if (!session) {
|
|
@@ -297,14 +297,19 @@ class TssSignClass {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
const otherPartyMsgs = session.rounds[round].filter(m => m.fromPartyId != party.partyId);
|
|
300
|
+
const participants = otherPartyMsgs.map(m => {
|
|
301
|
+
const p = session.participants.find(p => p.partyId === m.fromPartyId);
|
|
302
|
+
return p?.copayerId;
|
|
303
|
+
}).filter(Boolean) as string[];
|
|
304
|
+
|
|
300
305
|
if (otherPartyMsgs.length === session.m - 1) {
|
|
301
306
|
const messages = otherPartyMsgs.map(m => m.messages);
|
|
302
307
|
for (const m of messages) {
|
|
303
308
|
m.p2pMessages = m.p2pMessages.filter(m => m.to == party.partyId);
|
|
304
309
|
}
|
|
305
|
-
return { messages, signature: session.signature };
|
|
310
|
+
return { messages, signature: session.signature, participants };
|
|
306
311
|
}
|
|
307
|
-
return {};
|
|
312
|
+
return { participants };
|
|
308
313
|
}
|
|
309
314
|
|
|
310
315
|
async processMessage(params: { id: string; message: ITssSigMessageObject; m?: string | number; copayerId: string }) {
|