@bitpay-labs/bitcore-wallet-service 11.11.2 → 11.11.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/package.json +4 -4
- package/src/config.ts +8 -0
- package/src/externalservices/coingecko.ts +6 -1
- package/src/externalservices/transak.ts +394 -70
- package/src/lib/blockchainexplorer.ts +4 -0
- package/src/lib/blockchainexplorers/v8.ts +1 -0
- package/src/lib/blockchainmonitor.ts +1 -0
- package/src/lib/chain/arc/index.ts +95 -0
- package/src/lib/chain/eth/index.ts +41 -23
- package/src/lib/chain/index.ts +2 -0
- package/src/lib/common/constants.ts +13 -2
- package/src/lib/common/defaults.ts +31 -0
- package/src/lib/common/utils.ts +18 -1
- package/src/lib/emailservice.ts +3 -1
- package/src/lib/errors/errordefinitions.ts +4 -0
- package/src/lib/model/preferences.ts +4 -0
- package/src/lib/model/tsskeygen.ts +16 -0
- package/src/lib/model/wallet.ts +1 -0
- package/src/lib/pushnotificationsservice.ts +2 -0
- package/src/lib/routes/maintenance.ts +11 -2
- package/src/lib/routes/middleware/authTssRequest.ts +28 -16
- package/src/lib/routes/middleware/verifyTssMessage.ts +4 -1
- package/src/lib/routes/services.ts +8 -2
- package/src/lib/routes/tss.ts +33 -30
- package/src/lib/routes/walletdata.ts +2 -0
- package/src/lib/server.ts +82 -19
- package/src/lib/storage.ts +39 -0
- package/src/lib/tss.ts +111 -7
- package/test/blockchainexplorer.test.ts +7 -0
- package/test/expressapp.test.ts +130 -0
- package/test/integration/externalservices/coinGecko.test.ts +42 -2
- package/test/integration/externalservices/transak.test.ts +545 -135
- package/test/integration/fiatrateservice.test.ts +13 -0
- package/test/integration/helpers.ts +1 -0
- package/test/integration/server.test.ts +192 -4
- package/test/integration/tss.test.ts +428 -0
- package/ts_build/package.json +4 -4
- package/ts_build/src/config.d.ts.map +1 -1
- package/ts_build/src/config.js +8 -0
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/externalservices/coingecko.d.ts.map +1 -1
- package/ts_build/src/externalservices/coingecko.js +6 -1
- package/ts_build/src/externalservices/coingecko.js.map +1 -1
- package/ts_build/src/externalservices/transak.d.ts +28 -2
- package/ts_build/src/externalservices/transak.d.ts.map +1 -1
- package/ts_build/src/externalservices/transak.js +274 -57
- package/ts_build/src/externalservices/transak.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorer.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorer.js +4 -0
- package/ts_build/src/lib/blockchainexplorer.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.js +1 -0
- package/ts_build/src/lib/blockchainexplorers/v8.js.map +1 -1
- package/ts_build/src/lib/blockchainmonitor.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainmonitor.js +1 -0
- package/ts_build/src/lib/blockchainmonitor.js.map +1 -1
- package/ts_build/src/lib/chain/arc/index.d.ts +20 -0
- package/ts_build/src/lib/chain/arc/index.d.ts.map +1 -0
- package/ts_build/src/lib/chain/arc/index.js +95 -0
- package/ts_build/src/lib/chain/arc/index.js.map +1 -0
- package/ts_build/src/lib/chain/eth/index.d.ts +1 -0
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +38 -19
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/chain/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/index.js +2 -0
- package/ts_build/src/lib/chain/index.js.map +1 -1
- package/ts_build/src/lib/common/constants.d.ts +13 -1
- package/ts_build/src/lib/common/constants.d.ts.map +1 -1
- package/ts_build/src/lib/common/constants.js +13 -2
- package/ts_build/src/lib/common/constants.js.map +1 -1
- package/ts_build/src/lib/common/defaults.d.ts +24 -0
- package/ts_build/src/lib/common/defaults.d.ts.map +1 -1
- package/ts_build/src/lib/common/defaults.js +30 -0
- package/ts_build/src/lib/common/defaults.js.map +1 -1
- package/ts_build/src/lib/common/index.d.ts +38 -1
- package/ts_build/src/lib/common/index.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.d.ts +1 -0
- package/ts_build/src/lib/common/utils.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.js +16 -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 +2 -0
- package/ts_build/src/lib/emailservice.js.map +1 -1
- package/ts_build/src/lib/errors/errordefinitions.d.ts +2 -0
- package/ts_build/src/lib/errors/errordefinitions.d.ts.map +1 -1
- package/ts_build/src/lib/errors/errordefinitions.js +2 -0
- package/ts_build/src/lib/errors/errordefinitions.js.map +1 -1
- package/ts_build/src/lib/model/preferences.d.ts +2 -0
- package/ts_build/src/lib/model/preferences.d.ts.map +1 -1
- package/ts_build/src/lib/model/preferences.js +3 -0
- package/ts_build/src/lib/model/preferences.js.map +1 -1
- package/ts_build/src/lib/model/tsskeygen.d.ts +10 -0
- package/ts_build/src/lib/model/tsskeygen.d.ts.map +1 -1
- package/ts_build/src/lib/model/tsskeygen.js +3 -0
- package/ts_build/src/lib/model/tsskeygen.js.map +1 -1
- package/ts_build/src/lib/model/wallet.d.ts.map +1 -1
- package/ts_build/src/lib/model/wallet.js +1 -0
- package/ts_build/src/lib/model/wallet.js.map +1 -1
- package/ts_build/src/lib/pushnotificationsservice.d.ts.map +1 -1
- package/ts_build/src/lib/pushnotificationsservice.js +2 -0
- package/ts_build/src/lib/pushnotificationsservice.js.map +1 -1
- package/ts_build/src/lib/routes/maintenance.d.ts.map +1 -1
- package/ts_build/src/lib/routes/maintenance.js +12 -4
- package/ts_build/src/lib/routes/maintenance.js.map +1 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts +6 -2
- package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts.map +1 -1
- package/ts_build/src/lib/routes/middleware/authTssRequest.js +21 -14
- package/ts_build/src/lib/routes/middleware/authTssRequest.js.map +1 -1
- package/ts_build/src/lib/routes/middleware/verifyTssMessage.d.ts.map +1 -1
- package/ts_build/src/lib/routes/middleware/verifyTssMessage.js +2 -1
- package/ts_build/src/lib/routes/middleware/verifyTssMessage.js.map +1 -1
- package/ts_build/src/lib/routes/services.d.ts.map +1 -1
- package/ts_build/src/lib/routes/services.js +7 -2
- package/ts_build/src/lib/routes/services.js.map +1 -1
- package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
- package/ts_build/src/lib/routes/tss.js +32 -30
- package/ts_build/src/lib/routes/tss.js.map +1 -1
- package/ts_build/src/lib/routes/walletdata.d.ts.map +1 -1
- package/ts_build/src/lib/routes/walletdata.js +2 -0
- package/ts_build/src/lib/routes/walletdata.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +23 -2
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +37 -5
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +9 -0
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +26 -0
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/src/lib/tss.d.ts +12 -0
- package/ts_build/src/lib/tss.d.ts.map +1 -1
- package/ts_build/src/lib/tss.js +64 -5
- package/ts_build/src/lib/tss.js.map +1 -1
- package/ts_build/test/blockchainexplorer.test.js +6 -0
- package/ts_build/test/blockchainexplorer.test.js.map +1 -1
- package/ts_build/test/expressapp.test.js +120 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/coinGecko.test.js +38 -2
- package/ts_build/test/integration/externalservices/coinGecko.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/transak.test.d.ts.map +1 -1
- package/ts_build/test/integration/externalservices/transak.test.js +498 -128
- package/ts_build/test/integration/externalservices/transak.test.js.map +1 -1
- package/ts_build/test/integration/fiatrateservice.test.js +11 -0
- package/ts_build/test/integration/fiatrateservice.test.js.map +1 -1
- package/ts_build/test/integration/helpers.d.ts.map +1 -1
- package/ts_build/test/integration/helpers.js +1 -0
- package/ts_build/test/integration/helpers.js.map +1 -1
- package/ts_build/test/integration/server.test.js +182 -3
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/integration/tss.test.d.ts.map +1 -1
- package/ts_build/test/integration/tss.test.js +344 -0
- package/ts_build/test/integration/tss.test.js.map +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import util from 'util';
|
|
2
|
+
import { Constants, ethers } from '@bitpay-labs/crypto-wallet-core';
|
|
3
|
+
import { ClientError } from '../../errors/clienterror';
|
|
4
|
+
import { Errors } from '../../errors/errordefinitions';
|
|
5
|
+
import { EthChain } from '../eth';
|
|
6
|
+
import { ERC20Abi } from '../eth/abi-erc20';
|
|
7
|
+
import type { TxProposal } from '../../model/txproposal';
|
|
8
|
+
|
|
9
|
+
const usdcInterface = new ethers.Interface(ERC20Abi);
|
|
10
|
+
|
|
11
|
+
export class ArcChain extends EthChain {
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.chain = 'ARC';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private isUsdcTokenView(opts) {
|
|
18
|
+
return opts.tokenAddress?.toLowerCase() === Constants.ARC.USDC_CONTRACT_ADDRESS;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected getAdditionalNativeAmount(txp: TxProposal): bigint {
|
|
22
|
+
if (txp.tokenAddress || txp.multisigContractAddress || txp.multiSendContractAddress) return 0n;
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
return (txp.outputs || []).reduce((total, output, index) => {
|
|
26
|
+
if (output.toAddress?.toLowerCase() !== Constants.ARC.USDC_CONTRACT_ADDRESS) return total;
|
|
27
|
+
const data = index === 0 && txp.data ? txp.data : output.data;
|
|
28
|
+
if (!data || data === '0x') return total;
|
|
29
|
+
const call = usdcInterface.parseTransaction({ data });
|
|
30
|
+
let amount = 0n;
|
|
31
|
+
if (call?.name === 'transfer') {
|
|
32
|
+
amount = call.args[1];
|
|
33
|
+
} else if (call?.name === 'transferFrom' && call.args[0].toLowerCase() === txp.from?.toLowerCase()) {
|
|
34
|
+
amount = call.args[2];
|
|
35
|
+
}
|
|
36
|
+
// The ERC20 interface uses 6 decimals; the same native balance uses 18.
|
|
37
|
+
return total + amount * 10n ** 12n;
|
|
38
|
+
}, 0n);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new ClientError('Invalid Arc USDC contract call data');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getWalletBalance(server, wallet, opts, cb) {
|
|
45
|
+
// Native USDC and its ERC-20 view share funds. A separate token balance would
|
|
46
|
+
// use different units and omit the native spend locked by pending proposals.
|
|
47
|
+
if (this.isUsdcTokenView(opts)) {
|
|
48
|
+
return cb(new ClientError('USDC is the native Arc balance; omit tokenAddress'));
|
|
49
|
+
}
|
|
50
|
+
return super.getWalletBalance(server, wallet, opts, cb);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getWalletSendMaxInfo(server, wallet, opts, cb) {
|
|
54
|
+
const onFee = (err, feePerKb?) => {
|
|
55
|
+
if (err) return cb(err);
|
|
56
|
+
feePerKb = Number(feePerKb);
|
|
57
|
+
if (!Number.isFinite(feePerKb) || feePerKb < Constants.ARC.MIN_GAS_PRICE) {
|
|
58
|
+
return cb(new ClientError('Arc gas price must be at least 20 gwei'));
|
|
59
|
+
}
|
|
60
|
+
return super.getWalletSendMaxInfo(server, wallet, { ...opts, feePerKb }, cb);
|
|
61
|
+
};
|
|
62
|
+
// Match the type-2 cap reserved when creating the proposal.
|
|
63
|
+
if (Number(opts.txType) === 2) {
|
|
64
|
+
return server.estimateFee({ network: wallet.network, chain: wallet.chain, txType: 2 })
|
|
65
|
+
.then(feePerKb => onFee(null, feePerKb), onFee);
|
|
66
|
+
}
|
|
67
|
+
return server._getFeePerKb(wallet, opts, onFee);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getFee(server, wallet, opts) {
|
|
71
|
+
if (this.isUsdcTokenView(opts)) {
|
|
72
|
+
throw new ClientError('USDC is the native Arc asset; omit tokenAddress');
|
|
73
|
+
}
|
|
74
|
+
const fees = await super.getFee(server, wallet, opts);
|
|
75
|
+
const gasPrice = Number(Number(opts.txType) === 2 ? fees.maxGasFee : fees.gasPrice);
|
|
76
|
+
if (!Number.isFinite(gasPrice) || gasPrice < Constants.ARC.MIN_GAS_PRICE) {
|
|
77
|
+
throw new ClientError('Arc gas price must be at least 20 gwei');
|
|
78
|
+
}
|
|
79
|
+
// Reserve the fee actually encoded in the transaction, including type-2 caps.
|
|
80
|
+
const fee = gasPrice * fees.gasLimit;
|
|
81
|
+
if (opts.sendMax && Number(opts.txType) === 2) {
|
|
82
|
+
const balance = await util.promisify(server.getBalance).call(server, {});
|
|
83
|
+
if (balance.availableAmount <= fee) throw Errors.INSUFFICIENT_FUNDS_FOR_FEE;
|
|
84
|
+
opts.outputs[0].amount = balance.availableAmount - fee;
|
|
85
|
+
}
|
|
86
|
+
return { ...fees, fee };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
checkDust(output, opts) {
|
|
90
|
+
if (!opts.tokenAddress && output.amount > 0 &&
|
|
91
|
+
output.toAddress.toLowerCase() === '0x0000000000000000000000000000000000000000') {
|
|
92
|
+
return new ClientError('Native USDC cannot be sent to the zero address on Arc');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -84,6 +84,10 @@ export class EthChain implements IChain {
|
|
|
84
84
|
return true;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
protected getAdditionalNativeAmount(_txp: TxProposal): bigint {
|
|
88
|
+
return 0n;
|
|
89
|
+
}
|
|
90
|
+
|
|
87
91
|
getWalletBalance(server, wallet, opts, cb) {
|
|
88
92
|
const bc = server._getBlockchainExplorer(wallet.chain || wallet.coin, wallet.network);
|
|
89
93
|
|
|
@@ -106,18 +110,22 @@ export class EthChain implements IChain {
|
|
|
106
110
|
let fees = 0n;
|
|
107
111
|
let amounts = 0n;
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
try {
|
|
114
|
+
txps.filter(txp => {
|
|
115
|
+
// Add gas used for tokens when getting native balance
|
|
116
|
+
if (!opts.tokenAddress) {
|
|
117
|
+
fees += txp.fee ? BigInt(txp.fee) : 0n;
|
|
118
|
+
}
|
|
119
|
+
// Filter tokens when getting native balance
|
|
120
|
+
if (txp.tokenAddress && !opts.tokenAddress) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
amounts += BigInt(txp.amount || 0) + this.getAdditionalNativeAmount(txp);
|
|
124
|
+
return true;
|
|
125
|
+
});
|
|
126
|
+
} catch (err) {
|
|
127
|
+
return cb(err);
|
|
128
|
+
}
|
|
121
129
|
|
|
122
130
|
// TODO support big int
|
|
123
131
|
const lockedSum = Number(amounts + fees) || 0; // previously set to 0 if opts.multisigContractAddress
|
|
@@ -175,8 +183,15 @@ export class EthChain implements IChain {
|
|
|
175
183
|
checkScriptOutput(_output) { }
|
|
176
184
|
|
|
177
185
|
async getFee(server, wallet, opts) {
|
|
186
|
+
const { from, txType, priorityFeePercentile, gasLimitBuffer } = opts;
|
|
187
|
+
const { coin, network, chain } = wallet;
|
|
178
188
|
const hasType2FeeOverride = opts.maxGasFee != null || opts.priorityGasFee != null;
|
|
179
189
|
let { maxGasFee, priorityGasFee } = opts;
|
|
190
|
+
if (Number(txType) === 2 && !hasType2FeeOverride) {
|
|
191
|
+
maxGasFee = await server.estimateFee({ network, chain: chain || coin, txType: 2 });
|
|
192
|
+
priorityGasFee = await server.estimatePriorityFee({ network, chain: chain || coin, percentile: priorityFeePercentile || 15 });
|
|
193
|
+
maxGasFee = Math.max(maxGasFee, priorityGasFee);
|
|
194
|
+
}
|
|
180
195
|
if (hasType2FeeOverride) {
|
|
181
196
|
const chain = wallet.chain || wallet.coin;
|
|
182
197
|
// Keep overrides consistent with CWC's chain-specific minimum priority fee.
|
|
@@ -185,8 +200,8 @@ export class EthChain implements IChain {
|
|
|
185
200
|
throw new ClientError(`priorityGasFee must be at least ${minimum} wei for ${chain}`);
|
|
186
201
|
}
|
|
187
202
|
}
|
|
188
|
-
//
|
|
189
|
-
const feeOpts = opts.gasPrice != null ||
|
|
203
|
+
// Reserve the same rate used for construction, including the automatic type-2 fee cap.
|
|
204
|
+
const feeOpts = opts.gasPrice != null || maxGasFee != null
|
|
190
205
|
? { ...opts, feePerKb: opts.gasPrice ?? maxGasFee }
|
|
191
206
|
: opts;
|
|
192
207
|
// Fixed fees are converted to a gas price below, once the gas limits are known.
|
|
@@ -196,8 +211,6 @@ export class EthChain implements IChain {
|
|
|
196
211
|
: await util.promisify(server._getFeePerKb).call(server, wallet, feeOpts);
|
|
197
212
|
let feePerKb = inFeePerKb;
|
|
198
213
|
let gasPrice = inFeePerKb;
|
|
199
|
-
const { from, txType, priorityFeePercentile, gasLimitBuffer } = opts;
|
|
200
|
-
const { coin, network, chain } = wallet;
|
|
201
214
|
let inGasLimit = 0; // Per recepient gas limit
|
|
202
215
|
let gasLimit = 0; // Gas limit for all recepients. used for contract interactions that rollup recepients
|
|
203
216
|
let fee = 0;
|
|
@@ -297,9 +310,9 @@ export class EthChain implements IChain {
|
|
|
297
310
|
fee += feePerKb * gasLimit;
|
|
298
311
|
logger.info(`[${from}] Top level gas limit with buffer: ${gasLimit}`);
|
|
299
312
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
313
|
+
// BWC verifies an explicit feePerKb against the request, separately from the type-2 fee cap.
|
|
314
|
+
if (Number(txType) === 2 && Common.Utils.isNumber(opts.feePerKb)) {
|
|
315
|
+
feePerKb = opts.feePerKb;
|
|
303
316
|
}
|
|
304
317
|
return { feePerKb, gasPrice, gasLimit, maxGasFee, priorityGasFee, fee };
|
|
305
318
|
}
|
|
@@ -482,10 +495,15 @@ export class EthChain implements IChain {
|
|
|
482
495
|
"gasLimit":200000
|
|
483
496
|
}]
|
|
484
497
|
*/
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
498
|
+
let txpTotalAmount;
|
|
499
|
+
try {
|
|
500
|
+
txpTotalAmount =
|
|
501
|
+
(opts.multisigContractAddress || opts.tokenAddress) && txp.payProUrl
|
|
502
|
+
? getInvoiceValue(txp)
|
|
503
|
+
: txp.getTotalAmountBigInt() + this.getAdditionalNativeAmount(txp);
|
|
504
|
+
} catch (err) {
|
|
505
|
+
return cb(err);
|
|
506
|
+
}
|
|
489
507
|
|
|
490
508
|
if (totalAmount < txpTotalAmount) {
|
|
491
509
|
return cb(Errors.INSUFFICIENT_FUNDS);
|
package/src/lib/chain/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { IWallet, TxProposal } from '../model';
|
|
|
4
4
|
import { WalletService } from '../server';
|
|
5
5
|
import logger from './../logger';
|
|
6
6
|
import { ArbChain } from './arb';
|
|
7
|
+
import { ArcChain } from './arc';
|
|
7
8
|
import { BaseChain } from './base';
|
|
8
9
|
import { BchChain } from './bch';
|
|
9
10
|
import { BtcChain } from './btc';
|
|
@@ -24,6 +25,7 @@ const chains: { [chain: string]: IChain } = {
|
|
|
24
25
|
ETH: new EthChain(),
|
|
25
26
|
MATIC: new MaticChain(),
|
|
26
27
|
ARB: new ArbChain(),
|
|
28
|
+
ARC: new ArcChain(),
|
|
27
29
|
BASE: new BaseChain(),
|
|
28
30
|
OP: new OpChain(),
|
|
29
31
|
XRP: new XrpChain(),
|
|
@@ -10,6 +10,7 @@ export const Constants = {
|
|
|
10
10
|
ARB: 'arb',
|
|
11
11
|
BASE: 'base',
|
|
12
12
|
OP: 'op',
|
|
13
|
+
ARC: 'arc',
|
|
13
14
|
XRP: 'xrp',
|
|
14
15
|
DOGE: 'doge',
|
|
15
16
|
LTC: 'ltc',
|
|
@@ -38,7 +39,8 @@ export const Constants = {
|
|
|
38
39
|
EUROC: 'euroc',
|
|
39
40
|
USDT: 'usdt',
|
|
40
41
|
WETH: 'weth',
|
|
41
|
-
SOL: 'sol'
|
|
42
|
+
SOL: 'sol',
|
|
43
|
+
ARC: 'arc'
|
|
42
44
|
},
|
|
43
45
|
|
|
44
46
|
BITPAY_SUPPORTED_ETH_ERC20: {
|
|
@@ -60,6 +62,7 @@ export const Constants = {
|
|
|
60
62
|
|
|
61
63
|
BITPAY_USD_STABLECOINS: {
|
|
62
64
|
// used for rates
|
|
65
|
+
ARC: 'arc',
|
|
63
66
|
USDC: 'usdc',
|
|
64
67
|
PYUSD: 'pyusd',
|
|
65
68
|
USDP: 'usdp',
|
|
@@ -88,6 +91,7 @@ export const Constants = {
|
|
|
88
91
|
ARB: 'arb',
|
|
89
92
|
BASE: 'base',
|
|
90
93
|
OP: 'op',
|
|
94
|
+
ARC: 'arc',
|
|
91
95
|
},
|
|
92
96
|
|
|
93
97
|
EVM_CHAINS_WITH_ETH_GAS: {
|
|
@@ -112,6 +116,7 @@ export const Constants = {
|
|
|
112
116
|
arb: ['livenet', 'sepolia', 'holesky', 'regtest'],
|
|
113
117
|
base: ['livenet', 'sepolia', 'holesky', 'regtest'],
|
|
114
118
|
op: ['livenet', 'sepolia', 'holesky', 'regtest'],
|
|
119
|
+
arc: ['livenet', 'testnet'],
|
|
115
120
|
sol: ['livenet', 'testnet', 'devnet', 'regtest']
|
|
116
121
|
} as { [chain: string]: Array<string> },
|
|
117
122
|
|
|
@@ -156,6 +161,9 @@ export const Constants = {
|
|
|
156
161
|
mainnet: 'livenet',
|
|
157
162
|
testnet: 'sepolia'
|
|
158
163
|
},
|
|
164
|
+
arc: {
|
|
165
|
+
mainnet: 'livenet'
|
|
166
|
+
},
|
|
159
167
|
sol: {
|
|
160
168
|
mainnet: 'livenet',
|
|
161
169
|
testnet: 'devnet'
|
|
@@ -200,6 +208,8 @@ export const Constants = {
|
|
|
200
208
|
ARB_TOKEN_OPTS: CWC.Constants.ARB_TOKEN_OPTS,
|
|
201
209
|
OP_TOKEN_OPTS: CWC.Constants.OP_TOKEN_OPTS,
|
|
202
210
|
BASE_TOKEN_OPTS: CWC.Constants.BASE_TOKEN_OPTS,
|
|
211
|
+
ARC_TOKEN_OPTS: CWC.Constants.ARC_TOKEN_OPTS,
|
|
212
|
+
ARC: CWC.Constants.ARC,
|
|
203
213
|
SOL_TOKEN_OPTS: CWC.Constants.SOL_TOKEN_OPTS,
|
|
204
214
|
|
|
205
215
|
BITPAY_CONTRACTS: {
|
|
@@ -219,6 +229,7 @@ export const Constants = {
|
|
|
219
229
|
bch: { testnet: 300, livenet: 0 },
|
|
220
230
|
eth: { testnet: 300, livenet: 0 },
|
|
221
231
|
matic: { testnet: 300, livenet: 12 }, // MATIC set to 12 because blocks normally occur every 1-2 seconds
|
|
232
|
+
arc: { testnet: 0, livenet: 0 },
|
|
222
233
|
xrp: { testnet: 300, livenet: 0 },
|
|
223
234
|
doge: { testnet: 300, livenet: 0 },
|
|
224
235
|
ltc: { testnet: 300, livenet: 0 }
|
|
@@ -226,7 +237,7 @@ export const Constants = {
|
|
|
226
237
|
|
|
227
238
|
// May be different from the defaults if we rollback
|
|
228
239
|
// or want to beta test new versions.
|
|
229
|
-
TSS_KEYGEN_SCHEME_VERSION_MAX: 1.
|
|
240
|
+
TSS_KEYGEN_SCHEME_VERSION_MAX: 1.3,
|
|
230
241
|
TSS_SIGGEN_SCHEME_VERSION_MAX: 1.1,
|
|
231
242
|
|
|
232
243
|
TSS_KEYGEN_SCHEME_VERSION_MIN: 1,
|
|
@@ -194,6 +194,34 @@ export const Defaults = {
|
|
|
194
194
|
defaultValue: 2000000000
|
|
195
195
|
}
|
|
196
196
|
],
|
|
197
|
+
// Fallbacks must meet Arc's testnet gas-price floor. Live estimates still take precedence.
|
|
198
|
+
arc: [
|
|
199
|
+
{
|
|
200
|
+
name: 'urgent',
|
|
201
|
+
nbBlocks: 1,
|
|
202
|
+
defaultValue: 20000000000
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'priority',
|
|
206
|
+
nbBlocks: 2,
|
|
207
|
+
defaultValue: 20000000000
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'normal',
|
|
211
|
+
nbBlocks: 3,
|
|
212
|
+
defaultValue: 20000000000
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'economy',
|
|
216
|
+
nbBlocks: 4,
|
|
217
|
+
defaultValue: 20000000000
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: 'superEconomy',
|
|
221
|
+
nbBlocks: 4,
|
|
222
|
+
defaultValue: 20000000000
|
|
223
|
+
}
|
|
224
|
+
],
|
|
197
225
|
xrp: [
|
|
198
226
|
{
|
|
199
227
|
name: 'normal',
|
|
@@ -362,6 +390,7 @@ export const Defaults = {
|
|
|
362
390
|
arb: 1000000000000, // 50 Gwei,
|
|
363
391
|
base: 1000000000000, // 50 Gwei,
|
|
364
392
|
op: 1000000000000, // 50 Gwei,
|
|
393
|
+
arc: 1000000000000,
|
|
365
394
|
xrp: 1000000000000,
|
|
366
395
|
doge: 100000000 * 100,
|
|
367
396
|
ltc: 10000 * 1000, // 10k sat/b
|
|
@@ -376,6 +405,7 @@ export const Defaults = {
|
|
|
376
405
|
arb: 0,
|
|
377
406
|
base: 0,
|
|
378
407
|
op: 0,
|
|
408
|
+
arc: 0,
|
|
379
409
|
xrp: 0,
|
|
380
410
|
doge: 0,
|
|
381
411
|
ltc: 0,
|
|
@@ -390,6 +420,7 @@ export const Defaults = {
|
|
|
390
420
|
arb: 1 * 1e18, // 1 eth
|
|
391
421
|
base: 1 * 1e18, // 1 eth
|
|
392
422
|
op: 1 * 1e18, // 1 eth
|
|
423
|
+
arc: 1 * 1e18, // 1 USDC
|
|
393
424
|
xrp: 1 * 1e6, // 1 xrp
|
|
394
425
|
doge: 400 * 1e8,
|
|
395
426
|
ltc: 0.05 * 1e8
|
package/src/lib/common/utils.ts
CHANGED
|
@@ -571,5 +571,22 @@ export const Utils = {
|
|
|
571
571
|
return retval;
|
|
572
572
|
},
|
|
573
573
|
|
|
574
|
-
sleep(ms) {
|
|
574
|
+
sleep(ms) {
|
|
575
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
convertXPubKeyForNetwork(xPubKey: string, chain: string, network: string) {
|
|
579
|
+
// Convert named network for non-UTXO chains to generic name (e.g. sepolia -> testnet)
|
|
580
|
+
network = !Bitcore_[chain] ? Utils.getGenericName(network) : network;
|
|
581
|
+
const lib = Bitcore_[chain] || BitcoreLib;
|
|
582
|
+
$.checkArgument(xPubKey, 'xPubKey is required at <convertXPubKeyForNetwork()>');
|
|
583
|
+
$.checkArgument(chain, 'chain is required at <convertXPubKeyForNetwork()>');
|
|
584
|
+
$.checkArgument(lib.Networks.get(network), 'invalid network at <convertXPubKeyForNetwork()>: ' + network);
|
|
585
|
+
const hdPubKeyObj = new lib.HDPublicKey(xPubKey).toObject();
|
|
586
|
+
hdPubKeyObj.network = network;
|
|
587
|
+
delete hdPubKeyObj.checksum;
|
|
588
|
+
delete hdPubKeyObj.xpubkey;
|
|
589
|
+
const newHdPubKey = lib.HDPublicKey.fromObject(hdPubKeyObj);
|
|
590
|
+
return newHdPubKey.toString();
|
|
591
|
+
}
|
|
575
592
|
};
|
package/src/lib/emailservice.ts
CHANGED
|
@@ -337,6 +337,7 @@ export class EmailService {
|
|
|
337
337
|
bch: 'BCH',
|
|
338
338
|
eth: 'ETH',
|
|
339
339
|
matic: 'MATIC',
|
|
340
|
+
arc: 'USDC',
|
|
340
341
|
xrp: 'XRP',
|
|
341
342
|
doge: 'DOGE',
|
|
342
343
|
ltc: 'LTC',
|
|
@@ -685,6 +686,7 @@ export class EmailService {
|
|
|
685
686
|
arb: 'arbitrum-one',
|
|
686
687
|
base: 'base',
|
|
687
688
|
op: 'optimistic-ethereum',
|
|
689
|
+
arc: 'arc',
|
|
688
690
|
sol: 'solana',
|
|
689
691
|
};
|
|
690
692
|
|
|
@@ -739,4 +741,4 @@ export class EmailService {
|
|
|
739
741
|
});
|
|
740
742
|
});
|
|
741
743
|
}
|
|
742
|
-
}
|
|
744
|
+
}
|
|
@@ -37,6 +37,8 @@ interface Errors<T> {
|
|
|
37
37
|
// Solana Errors
|
|
38
38
|
INSUFFICIENT_SOL_FEE: T;
|
|
39
39
|
LOCKED_SOL_FEE: T;
|
|
40
|
+
INSUFFICIENT_ARC_FEE: T;
|
|
41
|
+
LOCKED_ARC_FEE: T;
|
|
40
42
|
HISTORY_LIMIT_EXCEEDED: T;
|
|
41
43
|
MAIN_ADDRESS_GAP_REACHED: T;
|
|
42
44
|
MULTI_TX_UNSUPPORTED: T;
|
|
@@ -114,6 +116,8 @@ const errors: Errors<string> = {
|
|
|
114
116
|
LOCKED_OP_FEE: 'Your linked OP wallet does not have enough ETH for fee',
|
|
115
117
|
INSUFFICIENT_SOL_FEE: 'Your linked SOL wallet does not have enough SOL for fee',
|
|
116
118
|
LOCKED_SOL_FEE: 'Your linked SOL wallet does not have enough SOL for fee',
|
|
119
|
+
INSUFFICIENT_ARC_FEE: 'Your linked ARC wallet does not have enough USDC for fee',
|
|
120
|
+
LOCKED_ARC_FEE: 'Your linked ARC wallet USDC is locked by pending spend proposals',
|
|
117
121
|
INVALID_ADDRESS: 'Invalid address',
|
|
118
122
|
INVALID_CHANGE_ADDRESS: 'Invalid change address',
|
|
119
123
|
KEY_IN_COPAYER: 'Key already registered',
|
|
@@ -12,6 +12,7 @@ export interface IPreferences {
|
|
|
12
12
|
opTokenAddresses?: string[];
|
|
13
13
|
baseTokenAddresses?: string[];
|
|
14
14
|
arbTokenAddresses?: string[];
|
|
15
|
+
arcTokenAddresses?: string[];
|
|
15
16
|
solTokenAddresses?: string[];
|
|
16
17
|
multisigMaticInfo: object[];
|
|
17
18
|
}
|
|
@@ -29,6 +30,7 @@ export class Preferences {
|
|
|
29
30
|
opTokenAddresses: string[];
|
|
30
31
|
baseTokenAddresses: string[];
|
|
31
32
|
arbTokenAddresses: string[];
|
|
33
|
+
arcTokenAddresses: string[];
|
|
32
34
|
solTokenAddresses: string[];
|
|
33
35
|
multisigMaticInfo: object[];
|
|
34
36
|
|
|
@@ -50,6 +52,7 @@ export class Preferences {
|
|
|
50
52
|
x.opTokenAddresses = opts.opTokenAddresses;
|
|
51
53
|
x.baseTokenAddresses = opts.baseTokenAddresses;
|
|
52
54
|
x.arbTokenAddresses = opts.arbTokenAddresses;
|
|
55
|
+
x.arcTokenAddresses = opts.arcTokenAddresses;
|
|
53
56
|
x.solTokenAddresses = opts.solTokenAddresses;
|
|
54
57
|
x.multisigMaticInfo = opts.multisigMaticInfo;
|
|
55
58
|
// you can't put useDust here since this is copayer's specific.
|
|
@@ -72,6 +75,7 @@ export class Preferences {
|
|
|
72
75
|
x.opTokenAddresses = obj.opTokenAddresses;
|
|
73
76
|
x.baseTokenAddresses = obj.baseTokenAddresses;
|
|
74
77
|
x.arbTokenAddresses = obj.arbTokenAddresses;
|
|
78
|
+
x.arcTokenAddresses = obj.arcTokenAddresses;
|
|
75
79
|
x.solTokenAddresses = obj.solTokenAddresses;
|
|
76
80
|
x.multisigMaticInfo = obj.multisigMaticInfo;
|
|
77
81
|
return x;
|
|
@@ -80,9 +80,18 @@ export interface ITssKeyGenModel {
|
|
|
80
80
|
*/
|
|
81
81
|
timeLimit?: number;
|
|
82
82
|
/**
|
|
83
|
+
* @deprecated Use bwsJoinSecrets instead.
|
|
83
84
|
* The BWC-generated secret for joining a BWS wallet
|
|
84
85
|
*/
|
|
85
86
|
bwsJoinSecret?: string;
|
|
87
|
+
/**
|
|
88
|
+
* The BWC-generated secrets for joining a BWS wallet
|
|
89
|
+
*/
|
|
90
|
+
bwsJoinSecrets?: {
|
|
91
|
+
[chainNetwork: string]: {
|
|
92
|
+
[partyId: number]: string;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
86
95
|
/**
|
|
87
96
|
* The mongo doc version
|
|
88
97
|
*/
|
|
@@ -105,6 +114,11 @@ export class TssKeyGenModel implements ITssKeyGenModel {
|
|
|
105
114
|
createdOn: number;
|
|
106
115
|
timeLimit?: number;
|
|
107
116
|
bwsJoinSecret?: string;
|
|
117
|
+
bwsJoinSecrets?: {
|
|
118
|
+
[chainNetwork: string]: {
|
|
119
|
+
[partyId: number]: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
108
122
|
__v: number;
|
|
109
123
|
|
|
110
124
|
|
|
@@ -151,6 +165,7 @@ export class TssKeyGenModel implements ITssKeyGenModel {
|
|
|
151
165
|
x.keyShares = new Array(n);
|
|
152
166
|
x.createdOn = Date.now();
|
|
153
167
|
x.timeLimit = Math.min(timeLimit || Defaults.TSS_KEYGEN_TIME_LIMIT, 60) * ONE_MINUTE; // capped at 60 minutes
|
|
168
|
+
x.bwsJoinSecrets = {};
|
|
154
169
|
x.__v = 0;
|
|
155
170
|
return x;
|
|
156
171
|
}
|
|
@@ -169,6 +184,7 @@ export class TssKeyGenModel implements ITssKeyGenModel {
|
|
|
169
184
|
x.createdOn = obj.createdOn;
|
|
170
185
|
x.timeLimit = obj.timeLimit;
|
|
171
186
|
x.bwsJoinSecret = obj.bwsJoinSecret;
|
|
187
|
+
x.bwsJoinSecrets = obj.bwsJoinSecrets;
|
|
172
188
|
x.__v = obj.__v;
|
|
173
189
|
return x;
|
|
174
190
|
}
|
package/src/lib/model/wallet.ts
CHANGED
|
@@ -474,6 +474,7 @@ export class PushNotificationsService {
|
|
|
474
474
|
bch: 'BCH',
|
|
475
475
|
eth: 'ETH',
|
|
476
476
|
matic: 'MATIC',
|
|
477
|
+
arc: 'USDC',
|
|
477
478
|
xrp: 'XRP',
|
|
478
479
|
doge: 'DOGE',
|
|
479
480
|
ltc: 'LTC',
|
|
@@ -748,6 +749,7 @@ export class PushNotificationsService {
|
|
|
748
749
|
arb: 'arbitrum-one',
|
|
749
750
|
base: 'base',
|
|
750
751
|
op: 'optimistic-ethereum',
|
|
752
|
+
arc: 'arc',
|
|
751
753
|
sol: 'solana',
|
|
752
754
|
};
|
|
753
755
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
+
import { ClientError } from '../errors/clienterror';
|
|
2
3
|
import { WalletService } from '../server';
|
|
3
4
|
import { Stats } from '../stats';
|
|
4
5
|
import type * as Types from '../../types/expressapp';
|
|
@@ -27,6 +28,14 @@ export function registerMaintenanceRoutes(router: express.Router, context: Route
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
router.get('/v1/stats/', (req, res) => {
|
|
31
|
+
const { network, coin } = req.query;
|
|
32
|
+
if (network !== undefined && typeof network !== 'string') {
|
|
33
|
+
return returnError(new ClientError('Invalid network'), res, req);
|
|
34
|
+
}
|
|
35
|
+
if (coin !== undefined && typeof coin !== 'string') {
|
|
36
|
+
return returnError(new ClientError('Invalid coin'), res, req);
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
setPublicCache(res, 1 * ONE_MINUTE);
|
|
31
40
|
const opts: {
|
|
32
41
|
network?: string;
|
|
@@ -35,8 +44,8 @@ export function registerMaintenanceRoutes(router: express.Router, context: Route
|
|
|
35
44
|
to?: string;
|
|
36
45
|
} = {};
|
|
37
46
|
|
|
38
|
-
if (
|
|
39
|
-
if (
|
|
47
|
+
if (network) opts.network = network;
|
|
48
|
+
if (coin) opts.coin = coin;
|
|
40
49
|
if (req.query.from) opts.from = req.query.from as string;
|
|
41
50
|
if (req.query.to) opts.to = req.query.to as string;
|
|
42
51
|
|
|
@@ -11,9 +11,7 @@ import type { ITssSigMessageObject, TssSigGenModel } from '../../model/tsssign';
|
|
|
11
11
|
function verifyMessage(req: express.Request, signature: string, pubKey: string) {
|
|
12
12
|
const message = getMessage(req);
|
|
13
13
|
const isValid = !!Utils.verifyMessage(message, signature, pubKey);
|
|
14
|
-
|
|
15
|
-
throw Errors.NOT_AUTHORIZED.withMessage('Invalid signature');
|
|
16
|
-
}
|
|
14
|
+
return isValid;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
/**
|
|
@@ -21,7 +19,8 @@ function verifyMessage(req: express.Request, signature: string, pubKey: string)
|
|
|
21
19
|
* It checks if the request has valid credentials, verifies the signature,
|
|
22
20
|
* and retrieves the TSS session based on the request path.
|
|
23
21
|
*/
|
|
24
|
-
export function authTssKeyGenRequest(): express.RequestHandler {
|
|
22
|
+
export function authTssKeyGenRequest(params: { bypassExpired?: boolean } = {}): express.RequestHandler {
|
|
23
|
+
const { bypassExpired } = params;
|
|
25
24
|
return async function(req, res, next) {
|
|
26
25
|
try {
|
|
27
26
|
const storage = WalletService.getStorage();
|
|
@@ -46,7 +45,10 @@ export function authTssKeyGenRequest(): express.RequestHandler {
|
|
|
46
45
|
// This is a brand new session. Caller should be party 0 with the initial round message.
|
|
47
46
|
if (body?.message?.round === 0 && body?.message?.partyId === 0) {
|
|
48
47
|
const pubKey = body?.message?.publicKey;
|
|
49
|
-
verifyMessage(req, signature, pubKey)
|
|
48
|
+
if (!verifyMessage(req, signature, pubKey)) {
|
|
49
|
+
// Self verification, but at least ensures that the caller owns the message's private key
|
|
50
|
+
throw Errors.NOT_AUTHORIZED.withMessage('Invalid signature');
|
|
51
|
+
}
|
|
50
52
|
return next();
|
|
51
53
|
} else {
|
|
52
54
|
// Session should have been initialized by the client already in BWC > TssKeyGen.newKey()
|
|
@@ -54,7 +56,7 @@ export function authTssKeyGenRequest(): express.RequestHandler {
|
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
if (session.isExpired()) {
|
|
59
|
+
if (!bypassExpired && session.isExpired()) {
|
|
58
60
|
throw Errors.TSS_SESSION_EXPIRED;
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -103,7 +105,9 @@ export function authTssKeyGenRequest(): express.RequestHandler {
|
|
|
103
105
|
throw Errors.NOT_AUTHORIZED.withMessage('Copayer not found in session');
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
verifyMessage(req, signature, pubKey)
|
|
108
|
+
if (!verifyMessage(req, signature, pubKey)) {
|
|
109
|
+
throw Errors.NOT_AUTHORIZED.withMessage('Invalid signature');
|
|
110
|
+
}
|
|
107
111
|
|
|
108
112
|
req.session = session;
|
|
109
113
|
return next();
|
|
@@ -116,7 +120,8 @@ export function authTssKeyGenRequest(): express.RequestHandler {
|
|
|
116
120
|
};
|
|
117
121
|
};
|
|
118
122
|
|
|
119
|
-
export function authTssSignRequest(): express.RequestHandler {
|
|
123
|
+
export function authTssSignRequest(params: { joinable?: boolean } = {}): express.RequestHandler {
|
|
124
|
+
const { joinable } = params;
|
|
120
125
|
return async function(req, res, next) {
|
|
121
126
|
try {
|
|
122
127
|
const storage = WalletService.getStorage();
|
|
@@ -132,16 +137,22 @@ export function authTssSignRequest(): express.RequestHandler {
|
|
|
132
137
|
|
|
133
138
|
const { id } = req.params as { [key: string]: string };
|
|
134
139
|
const body = req.body as { message: ITssSigMessageObject | undefined };
|
|
135
|
-
// Only some requests carry a body.message (e.g. POST /store only sends the final signature).
|
|
136
|
-
// Omitting it is safe since routes that don't receive a message don't consume one.
|
|
137
|
-
const verifyMsg = !!body?.message;
|
|
138
140
|
|
|
139
141
|
const copayer = await storage.fetchCopayerLookup(copayerId);
|
|
140
142
|
if (!copayer) {
|
|
141
143
|
throw Errors.NOT_AUTHORIZED.withMessage('Copayer not found');
|
|
142
144
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
// I'm not aware of an instance where copayer.requestPubKeys.length > 1, but we iterate over them to find the key for the given signature.
|
|
146
|
+
const pubKey = copayer.requestPubKeys.find(pubKey => verifyMessage(req, signature, pubKey.key))?.key;
|
|
147
|
+
if (!pubKey) {
|
|
148
|
+
throw Errors.NOT_AUTHORIZED.withMessage('Invalid signature');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Only some requests carry a body.message (e.g. POST /store only sends the final signature).
|
|
152
|
+
// Omitting it is safe since routes that don't receive a message don't change state.
|
|
153
|
+
// `verifyMsgFields` just indicates whether to check the message's fields for expected values (e.g. publicKey, partyId).
|
|
154
|
+
const verifyMsgFields = !!body?.message;
|
|
155
|
+
if (verifyMsgFields && pubKey !== body?.message?.publicKey) {
|
|
145
156
|
throw Errors.NOT_AUTHORIZED.withMessage('Invalid public key');
|
|
146
157
|
}
|
|
147
158
|
|
|
@@ -152,10 +163,12 @@ export function authTssSignRequest(): express.RequestHandler {
|
|
|
152
163
|
throw Errors.TSS_SESSION_EXPIRED;
|
|
153
164
|
}
|
|
154
165
|
const party = session.participants.find(p => p.copayerId === copayerId);
|
|
155
|
-
|
|
166
|
+
// A copayer joins an existing session by sending its round 0 message (participant is added in TssSign.processMessage)
|
|
167
|
+
const isJoining = !party && body?.message?.round === 0;
|
|
168
|
+
if (!party && !(joinable && isJoining)) {
|
|
156
169
|
throw Errors.NOT_AUTHORIZED.withMessage('Copayer not found in session');
|
|
157
170
|
}
|
|
158
|
-
if (
|
|
171
|
+
if (verifyMsgFields && party && party.partyId !== body?.message?.partyId) {
|
|
159
172
|
throw Errors.NOT_AUTHORIZED.withMessage('Copayer not found in session');
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -164,7 +177,6 @@ export function authTssSignRequest(): express.RequestHandler {
|
|
|
164
177
|
throw Errors.TSS_SESSION_NOT_FOUND;
|
|
165
178
|
}
|
|
166
179
|
|
|
167
|
-
verifyMessage(req, signature, pubKey);
|
|
168
180
|
return next();
|
|
169
181
|
} catch (err) {
|
|
170
182
|
if (err instanceof ClientError) {
|