@bitpay-labs/bitcore-wallet-service 11.9.0 → 11.10.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.
Files changed (90) hide show
  1. package/package.json +4 -4
  2. package/src/lib/chain/eth/index.ts +8 -7
  3. package/src/lib/chain/index.ts +1 -1
  4. package/src/lib/chain/sol/index.ts +4 -4
  5. package/src/lib/chain/xrp/index.ts +3 -3
  6. package/src/lib/errors/errordefinitions.ts +2 -0
  7. package/src/lib/expressapp.ts +6 -3
  8. package/src/lib/model/txnote.ts +3 -0
  9. package/src/lib/model/txproposal.ts +153 -68
  10. package/src/lib/model/txproposal_legacy.ts +5 -4
  11. package/src/lib/model/txproposalaction.ts +9 -0
  12. package/src/lib/routes/context.ts +10 -1
  13. package/src/lib/routes/transactions.ts +17 -2
  14. package/src/lib/routes/wallets.ts +8 -2
  15. package/src/lib/server.ts +62 -9
  16. package/src/lib/storage.ts +13 -12
  17. package/src/types/expressapp.d.ts +2 -1
  18. package/test/chain/bch.test.ts +8 -1
  19. package/test/chain/btc.test.ts +2 -2
  20. package/test/chain/doge.test.ts +4 -6
  21. package/test/chain/ltc.test.ts +4 -5
  22. package/test/chain/xrp.test.ts +4 -39
  23. package/test/model/txproposal.test.ts +64 -1
  24. package/ts_build/package.json +3 -3
  25. package/ts_build/src/lib/chain/eth/index.d.ts +6 -5
  26. package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
  27. package/ts_build/src/lib/chain/eth/index.js +5 -5
  28. package/ts_build/src/lib/chain/eth/index.js.map +1 -1
  29. package/ts_build/src/lib/chain/index.d.ts +1 -1
  30. package/ts_build/src/lib/chain/index.d.ts.map +1 -1
  31. package/ts_build/src/lib/chain/index.js.map +1 -1
  32. package/ts_build/src/lib/chain/sol/index.js +4 -4
  33. package/ts_build/src/lib/chain/sol/index.js.map +1 -1
  34. package/ts_build/src/lib/chain/xrp/index.js +3 -3
  35. package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
  36. package/ts_build/src/lib/errors/errordefinitions.d.ts +1 -0
  37. package/ts_build/src/lib/errors/errordefinitions.d.ts.map +1 -1
  38. package/ts_build/src/lib/errors/errordefinitions.js +1 -0
  39. package/ts_build/src/lib/errors/errordefinitions.js.map +1 -1
  40. package/ts_build/src/lib/expressapp.d.ts.map +1 -1
  41. package/ts_build/src/lib/expressapp.js +5 -3
  42. package/ts_build/src/lib/expressapp.js.map +1 -1
  43. package/ts_build/src/lib/model/txnote.d.ts +1 -0
  44. package/ts_build/src/lib/model/txnote.d.ts.map +1 -1
  45. package/ts_build/src/lib/model/txnote.js +1 -0
  46. package/ts_build/src/lib/model/txnote.js.map +1 -1
  47. package/ts_build/src/lib/model/txproposal.d.ts +72 -52
  48. package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
  49. package/ts_build/src/lib/model/txproposal.js +69 -17
  50. package/ts_build/src/lib/model/txproposal.js.map +1 -1
  51. package/ts_build/src/lib/model/txproposal_legacy.d.ts +3 -3
  52. package/ts_build/src/lib/model/txproposal_legacy.d.ts.map +1 -1
  53. package/ts_build/src/lib/model/txproposal_legacy.js +1 -0
  54. package/ts_build/src/lib/model/txproposal_legacy.js.map +1 -1
  55. package/ts_build/src/lib/model/txproposalaction.d.ts +2 -0
  56. package/ts_build/src/lib/model/txproposalaction.d.ts.map +1 -1
  57. package/ts_build/src/lib/model/txproposalaction.js +2 -0
  58. package/ts_build/src/lib/model/txproposalaction.js.map +1 -1
  59. package/ts_build/src/lib/routes/context.d.ts +1 -0
  60. package/ts_build/src/lib/routes/context.d.ts.map +1 -1
  61. package/ts_build/src/lib/routes/context.js +8 -1
  62. package/ts_build/src/lib/routes/context.js.map +1 -1
  63. package/ts_build/src/lib/routes/transactions.d.ts +1 -0
  64. package/ts_build/src/lib/routes/transactions.d.ts.map +1 -1
  65. package/ts_build/src/lib/routes/transactions.js +14 -2
  66. package/ts_build/src/lib/routes/transactions.js.map +1 -1
  67. package/ts_build/src/lib/routes/wallets.d.ts +1 -0
  68. package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
  69. package/ts_build/src/lib/routes/wallets.js +7 -2
  70. package/ts_build/src/lib/routes/wallets.js.map +1 -1
  71. package/ts_build/src/lib/server.d.ts +6 -2
  72. package/ts_build/src/lib/server.d.ts.map +1 -1
  73. package/ts_build/src/lib/server.js +45 -3
  74. package/ts_build/src/lib/server.js.map +1 -1
  75. package/ts_build/src/lib/storage.d.ts +6 -6
  76. package/ts_build/src/lib/storage.d.ts.map +1 -1
  77. package/ts_build/src/lib/storage.js +6 -6
  78. package/ts_build/src/lib/storage.js.map +1 -1
  79. package/ts_build/test/chain/bch.test.js +7 -0
  80. package/ts_build/test/chain/bch.test.js.map +1 -1
  81. package/ts_build/test/chain/btc.test.js +2 -2
  82. package/ts_build/test/chain/btc.test.js.map +1 -1
  83. package/ts_build/test/chain/doge.test.js +3 -5
  84. package/ts_build/test/chain/doge.test.js.map +1 -1
  85. package/ts_build/test/chain/ltc.test.js +3 -4
  86. package/ts_build/test/chain/ltc.test.js.map +1 -1
  87. package/ts_build/test/chain/xrp.test.js +3 -38
  88. package/ts_build/test/chain/xrp.test.js.map +1 -1
  89. package/ts_build/test/model/txproposal.test.js +57 -0
  90. package/ts_build/test/model/txproposal.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.9.0",
5
+ "version": "11.10.0",
6
6
  "license": "MIT",
7
7
  "main": "ts_build/src/index.js",
8
8
  "types": "./ts_build/src/index.d.ts",
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@bitpay-labs/bitcore-logging": "^11.8.1",
47
- "@bitpay-labs/bitcore-tss": "^11.9.0",
48
- "@bitpay-labs/crypto-wallet-core": "^11.9.0",
47
+ "@bitpay-labs/bitcore-tss": "^11.10.0",
48
+ "@bitpay-labs/crypto-wallet-core": "^11.10.0",
49
49
  "@sendgrid/mail": "6.5.4",
50
50
  "abi-decoder": "2.4.0",
51
51
  "async": "0.9.2",
@@ -126,5 +126,5 @@
126
126
  "email": "kjoseph@bitpay.com"
127
127
  }
128
128
  ],
129
- "gitHead": "3491bf1216d3c6b103c591564726333d55ad8c6d"
129
+ "gitHead": "cccc9de4342d56cfcfc14d8b563d7542e4b08b6a"
130
130
  }
@@ -3,13 +3,14 @@ import _ from 'lodash';
3
3
  import { IWallet } from 'src/lib/model';
4
4
  import { IAddress } from 'src/lib/model/address';
5
5
  import { WalletService } from 'src/lib/server';
6
- import { IChain } from '../../../types/chain';
7
6
  import { Common } from '../../common';
8
7
  import { ClientError } from '../../errors/clienterror';
9
8
  import { Errors } from '../../errors/errordefinitions';
10
9
  import logger from '../../logger';
11
10
  import { ERC20Abi } from './abi-erc20';
12
11
  import { InvoiceAbi } from './abi-invoice';
12
+ import type { IChain } from '../../../types/chain';
13
+ import type { TxProposal } from '../../model/txproposal';
13
14
 
14
15
  const {
15
16
  Constants,
@@ -98,24 +99,24 @@ export class EthChain implements IChain {
98
99
  // getPendingTxs returns all txps when given a native currency
99
100
  server.getPendingTxs(opts, (err, txps) => {
100
101
  if (err) return cb(err);
101
- let fees = 0;
102
- let amounts = 0;
102
+ let fees = 0n;
103
+ let amounts = 0n;
103
104
 
104
105
  txps.filter(txp => {
105
106
  // Add gas used for tokens when getting native balance
106
107
  if (!opts.tokenAddress) {
107
- fees += txp.fee || 0;
108
+ fees += txp.fee ? BigInt(txp.fee) : 0n;
108
109
  }
109
110
  // Filter tokens when getting native balance
110
111
  if (txp.tokenAddress && !opts.tokenAddress) {
111
112
  return false;
112
113
  }
113
- amounts += txp.amount;
114
+ amounts += txp.amount ? BigInt(txp.amount) : 0n;
114
115
  return true;
115
116
  });
116
117
 
117
118
  // TODO support big int
118
- const lockedSum = (amounts + fees) || 0; // previously set to 0 if opts.multisigContractAddress
119
+ const lockedSum = Number(amounts + fees) || 0; // previously set to 0 if opts.multisigContractAddress
119
120
  const convertedBalance = this.convertBitcoreBalance(balance, lockedSum);
120
121
  server.storage.fetchAddresses(server.walletId, (err, addresses: IAddress[]) => {
121
122
  if (err) return cb(err);
@@ -286,7 +287,7 @@ export class EthChain implements IChain {
286
287
  });
287
288
  }
288
289
 
289
- getBitcoreTx(txp, opts = { signed: true }) {
290
+ getBitcoreTx(txp: TxProposal, opts = { signed: true }) {
290
291
  const {
291
292
  data,
292
293
  outputs,
@@ -94,7 +94,7 @@ class ChainProxy {
94
94
  return this.get(wallet.chain).getFee(server, wallet, opts);
95
95
  }
96
96
 
97
- getBitcoreTx(txp: TxProposal, opts = { signed: true }) {
97
+ getBitcoreTx(txp: TxProposal<any>, opts = { signed: true }) {
98
98
  return this.get(txp.chain).getBitcoreTx(txp, { signed: opts.signed });
99
99
  }
100
100
 
@@ -56,18 +56,18 @@ export class SolChain implements IChain {
56
56
  const { fees, amounts } = txps.reduce((acc, txp) => {
57
57
  // Add gas used for tokens when getting native balance
58
58
  if (!opts.tokenAddress) {
59
- acc.fees += txp.fee || 0;
59
+ acc.fees += txp.fee ? BigInt(txp.fee) : 0n;
60
60
  }
61
61
 
62
62
  // Filter tokens when getting native balance
63
63
  if (!(txp.tokenAddress && !opts.tokenAddress)) {
64
- acc.amounts += txp.amount;
64
+ acc.amounts += txp.amount ? BigInt(txp.amount) : 0n;
65
65
  }
66
66
 
67
67
  return acc;
68
- }, { fees: 0, amounts: 0 });
68
+ }, { fees: 0n, amounts: 0n });
69
69
 
70
- const lockedSum = (amounts + fees) || 0; // previously set to 0 if opts.multisigContractAddress
70
+ const lockedSum = Number(amounts + fees) || 0; // previously set to 0 if opts.multisigContractAddress
71
71
  const reserveAmount = opts.tokenAddress ? 0 : reserve;
72
72
  const convertedBalance = this.convertBitcoreBalance(balance, lockedSum, reserveAmount);
73
73
  server.storage.fetchAddresses(server.walletId, (err, addresses: IAddress[]) => {
@@ -65,9 +65,9 @@ export class XrpChain implements IChain {
65
65
  server.getPendingTxs(opts, (err, txps) => {
66
66
  if (err) return cb(err);
67
67
  const lockedSum = txps.reduce((sum, txp) => {
68
- return sum + txp.amount + (txp.fee || 0);
69
- }, 0) || 0;
70
- const convertedBalance = this.convertBitcoreBalance(balance, lockedSum, reserve);
68
+ return sum + BigInt(txp.amount || 0) + BigInt(txp.fee || 0);
69
+ }, 0n) || 0n;
70
+ const convertedBalance = this.convertBitcoreBalance(balance, Number(lockedSum), reserve);
71
71
  server.storage.fetchAddresses(server.walletId, (err, addresses: IAddress[]) => {
72
72
  if (err) return cb(err);
73
73
  if (addresses.length > 0) {
@@ -61,6 +61,7 @@ interface Errors<T> {
61
61
  WALLET_NOT_FOUND: T;
62
62
  WALLET_NEED_SCAN: T;
63
63
  WRONG_SIGNING_METHOD: T;
64
+ INVALID_NUMBER_FORMAT: T;
64
65
  TSS_SESSION_NOT_FOUND: T;
65
66
  TSS_INVALID_PASSWORD: T;
66
67
  TSS_ROUND_ALREADY_DONE: T;
@@ -132,6 +133,7 @@ const errors: Errors<string> = {
132
133
  WALLET_NOT_FOUND: 'Wallet not found',
133
134
  WALLET_NEED_SCAN: 'Wallet needs addresses scan',
134
135
  WRONG_SIGNING_METHOD: 'Wrong signed method for coin/network',
136
+ INVALID_NUMBER_FORMAT: 'Invalid number format. Supported formats are: number, string, hex',
135
137
  TSS_SESSION_NOT_FOUND: 'Session not found',
136
138
  TSS_INVALID_PASSWORD: 'Invalid password',
137
139
  TSS_ROUND_ALREADY_DONE: 'Your message is for a round that has already finished',
@@ -49,7 +49,8 @@ export class ExpressApp {
49
49
  getServerWithAuth,
50
50
  getServerWithMultiAuth,
51
51
  logDeprecated,
52
- setPublicCache
52
+ setPublicCache,
53
+ checkNumberFormat
53
54
  } = createRouteHelpers(returnError);
54
55
 
55
56
  registerWalletRoutes(router, {
@@ -58,12 +59,14 @@ export class ExpressApp {
58
59
  getServerWithAuth,
59
60
  getServerWithMultiAuth,
60
61
  logDeprecated,
61
- returnError
62
+ returnError,
63
+ checkNumberFormat
62
64
  });
63
65
 
64
66
  registerTransactionRoutes(router, {
65
67
  getServerWithAuth,
66
- returnError
68
+ returnError,
69
+ checkNumberFormat
67
70
  });
68
71
 
69
72
  registerAdvertisementRoutes(router, {
@@ -16,6 +16,9 @@ export class TxNote {
16
16
  editedOn: number;
17
17
  editedBy: string;
18
18
 
19
+ // Non-persisted fields - populated when fetching from storage
20
+ editedByName?: string;
21
+
19
22
  static create(opts) {
20
23
  opts = opts || {};
21
24
 
@@ -1,11 +1,13 @@
1
+ import { Utils as CWCUtils } from '@bitpay-labs/crypto-wallet-core';
1
2
  import _ from 'lodash';
2
3
  import { singleton } from 'preconditions';
3
4
  import Uuid from 'uuid';
4
5
  import { ChainService } from '../chain/index';
5
6
  import { Common } from '../common';
6
7
  import logger from '../logger';
7
- import { IAddress } from './address';
8
- import { TxProposalLegacy } from './txproposal_legacy';
8
+ import { type IAddress } from './address';
9
+ import { type ITxNote, TxNote } from './txnote';
10
+ import { type ITxProposalLegacy, TxProposalLegacy } from './txproposal_legacy';
9
11
  import { TxProposalAction } from './txproposalaction';
10
12
 
11
13
  const $ = singleton();
@@ -13,9 +15,9 @@ const { Constants, Defaults, Utils } = Common;
13
15
 
14
16
  type TxProposalStatus = 'temporary' | 'pending' | 'accepted' | 'rejected' | 'broadcasted';
15
17
 
16
- export interface ITxProposal {
18
+ export interface ITxProposal<NumberType = number> {
19
+ version: number;
17
20
  type?: string;
18
- creatorName: string;
19
21
  createdOn: number;
20
22
  txid: string;
21
23
  txids?: Array<string>;
@@ -28,19 +30,20 @@ export interface ITxProposal {
28
30
  message: string;
29
31
  payProUrl?: string;
30
32
  from: string;
31
- changeAddress?: IAddress;
32
- escrowAddress?: IAddress;
33
+ sendMax?: boolean;
34
+ changeAddress?: Partial<IAddress>;
35
+ escrowAddress?: Partial<IAddress>;
33
36
  inputs: any[];
34
37
  outputs: Array<{
35
- amount: number;
36
- address: string;
38
+ amount: NumberType;
39
+ address?: string;
37
40
  toAddress?: string;
38
41
  sourceAddress?: string;
39
42
  message?: string;
40
43
  data?: string;
41
- gasLimit?: number;
44
+ gasLimit?: NumberType;
42
45
  script?: string;
43
- satoshis?: number;
46
+ satoshis?: NumberType;
44
47
  tag?: number;
45
48
  }>;
46
49
  outputOrder: number[];
@@ -51,13 +54,12 @@ export interface ITxProposal {
51
54
  status: TxProposalStatus;
52
55
  actions: any[];
53
56
  feeLevel: string;
54
- feePerKb: number;
57
+ feePerKb: NumberType;
55
58
  excludeUnconfirmedUtxos: boolean;
56
59
  addressType: string;
57
60
  customData: any;
58
- amount: string;
59
- fee: number;
60
- version: number;
61
+ amount: NumberType;
62
+ fee: NumberType;
61
63
  broadcastedOn: number;
62
64
  inputPaths: string | any[];
63
65
  proposalSignature: string;
@@ -65,41 +67,51 @@ export interface ITxProposal {
65
67
  proposalSignaturePubKeySig: string;
66
68
  signingMethod: string;
67
69
  lowFees?: boolean;
68
- nonce?: number | string;
70
+ raw?: Array<string> | string;
71
+ nonce?: NumberType | string;
69
72
  deferNonce?: boolean;
70
- gasPrice?: number;
71
- maxGasFee?: number;
72
- priorityGasFee?: number;
73
+ gasPrice?: NumberType;
74
+ maxGasFee?: NumberType;
75
+ priorityGasFee?: NumberType;
73
76
  txType?: number | string;
74
- gasLimit?: number; // Backward compatibility for BWC <= 8.9.0
77
+ gasLimit?: NumberType; // Backward compatibility for BWC <= 8.9.0
75
78
  data?: string; // Backward compatibility for BWC <= 8.9.0
76
79
  tokenAddress?: string;
77
80
  multisigContractAddress?: string;
81
+ multisigTxId?: string;
78
82
  destinationTag?: string;
79
83
  invoiceID?: string;
80
- lockUntilBlockHeight?: number;
81
- instantAcceptanceEscrow?: number;
84
+ lockUntilBlockHeight?: NumberType;
85
+ instantAcceptanceEscrow?: NumberType;
82
86
  isTokenSwap?: boolean;
87
+ multiSendContractAddress?: string;
83
88
  enableRBF?: boolean;
84
89
  replaceTxByFee?: boolean;
85
90
  multiTx?: boolean; // proposal contains multiple transactions
86
- space?: number;
91
+ space?: NumberType;
87
92
  nonceAddress?: string;
88
93
  blockHash?: string;
89
- blockHeight?: number;
94
+ blockHeight?: NumberType;
90
95
  category?: string;
91
- priorityFee?: number;
92
- computeUnits?: number;
96
+ priorityFee?: NumberType;
97
+ computeUnits?: NumberType;
93
98
  memo?: string;
94
99
  fromAta?: string;
95
100
  decimals?: number;
96
101
  refreshOnPublish?: boolean;
97
102
  prePublishRaw?: string;
103
+
104
+ // Non-persistent fields - populated on fetch
105
+ creatorName: string;
106
+ derivationStrategy?: string;
107
+ note?: ITxNote;
98
108
  }
99
109
 
100
- export class TxProposal implements ITxProposal {
110
+ export type NumberFormat = 'hex' | 'number' | 'string' | 'bigint';
111
+
112
+ export class TxProposal<NumberType = number> implements ITxProposal<NumberType> {
113
+ version: number;
101
114
  type?: string;
102
- creatorName: string;
103
115
  createdOn: number;
104
116
  id: string;
105
117
  txid: string;
@@ -112,19 +124,20 @@ export class TxProposal implements ITxProposal {
112
124
  message: string;
113
125
  payProUrl?: string;
114
126
  from: string;
115
- changeAddress?: IAddress;
116
- escrowAddress?: IAddress;
127
+ sendMax?: boolean;
128
+ changeAddress?: Partial<IAddress>;
129
+ escrowAddress?: Partial<IAddress>;
117
130
  inputs: any[];
118
131
  outputs: Array<{
119
- amount: number;
120
- address: string;
132
+ amount: NumberType;
133
+ address?: string;
121
134
  toAddress?: string;
122
135
  sourceAddress?: string;
123
136
  message?: string;
124
137
  data?: string;
125
- gasLimit?: number;
138
+ gasLimit?: NumberType;
126
139
  script?: string;
127
- satoshis?: number;
140
+ satoshis?: NumberType;
128
141
  tag?: number;
129
142
  }>;
130
143
  outputOrder: number[];
@@ -135,13 +148,12 @@ export class TxProposal implements ITxProposal {
135
148
  status: TxProposalStatus;
136
149
  actions: any[] = [];
137
150
  feeLevel: string;
138
- feePerKb: number;
151
+ feePerKb: NumberType;
139
152
  excludeUnconfirmedUtxos: boolean;
140
153
  addressType: string;
141
154
  customData: any;
142
- amount: string;
143
- fee: number;
144
- version: number;
155
+ amount: NumberType;
156
+ fee: NumberType;
145
157
  broadcastedOn: number;
146
158
  inputPaths: string | any[];
147
159
  proposalSignature: string;
@@ -150,38 +162,43 @@ export class TxProposal implements ITxProposal {
150
162
  signingMethod: string;
151
163
  lowFees?: boolean;
152
164
  raw?: Array<string> | string;
153
- nonce?: number | string;
165
+ nonce?: NumberType;
154
166
  deferNonce?: boolean;
155
- gasPrice?: number;
156
- maxGasFee?: number;
157
- priorityGasFee?: number;
167
+ gasPrice?: NumberType;
168
+ maxGasFee?: NumberType;
169
+ priorityGasFee?: NumberType;
158
170
  txType?: number | string;
159
- gasLimit?: number; // Backward compatibility for BWC <= 8.9.0
171
+ gasLimit?: NumberType; // Backward compatibility for BWC <= 8.9.0
160
172
  data?: string; // Backward compatibility for BWC <= 8.9.0
161
173
  tokenAddress?: string;
162
174
  multisigContractAddress?: string;
163
175
  multisigTxId?: string;
164
176
  destinationTag?: string;
165
177
  invoiceID?: string;
166
- lockUntilBlockHeight?: number;
167
- instantAcceptanceEscrow?: number;
178
+ lockUntilBlockHeight?: NumberType;
179
+ instantAcceptanceEscrow?: NumberType;
168
180
  isTokenSwap?: boolean;
169
181
  multiSendContractAddress?: string;
170
182
  enableRBF?: boolean;
171
183
  replaceTxByFee?: boolean;
172
184
  multiTx?: boolean;
173
- space?: number;
185
+ space?: NumberType;
174
186
  nonceAddress?: string;
175
187
  blockHash?: string;
176
- blockHeight?: number;
188
+ blockHeight?: NumberType;
177
189
  category?: string;
178
- priorityFee?: number;
179
- computeUnits?: number;
190
+ priorityFee?: NumberType;
191
+ computeUnits?: NumberType;
180
192
  memo?: string;
181
193
  fromAta?: string;
182
194
  decimals?: number;
183
195
  refreshOnPublish?: boolean;
184
196
  prePublishRaw?: string;
197
+
198
+ // Non-persistent fields - populated on fetch
199
+ creatorName: string;
200
+ derivationStrategy?: string;
201
+ note?: TxNote;
185
202
 
186
203
  static create(opts) {
187
204
  opts = opts || {};
@@ -211,6 +228,7 @@ export class TxProposal implements ITxProposal {
211
228
  x.signingMethod = opts.signingMethod;
212
229
  x.message = opts.message;
213
230
  x.payProUrl = opts.payProUrl;
231
+ x.sendMax = opts.sendMax;
214
232
  x.changeAddress = opts.changeAddress;
215
233
  x.escrowAddress = opts.escrowAddress;
216
234
  x.instantAcceptanceEscrow = opts.instantAcceptanceEscrow;
@@ -305,6 +323,8 @@ export class TxProposal implements ITxProposal {
305
323
  return x;
306
324
  }
307
325
 
326
+ static fromObj(obj: Partial<ITxProposal>): TxProposal;
327
+ static fromObj(obj: Partial<ITxProposalLegacy>): TxProposalLegacy;
308
328
  static fromObj(obj) {
309
329
  if (!(obj.version >= 3)) {
310
330
  return TxProposalLegacy.fromObj(obj);
@@ -317,6 +337,7 @@ export class TxProposal implements ITxProposal {
317
337
  x.id = obj.id;
318
338
  x.walletId = obj.walletId;
319
339
  x.creatorId = obj.creatorId;
340
+ x.creatorName = obj.creatorName;
320
341
  x.coin = obj.coin || Defaults.COIN;
321
342
  x.chain = obj.chain?.toLowerCase() || Utils.getChain(x.coin); // getChain -> backwards compatibility
322
343
  x.network = obj.network;
@@ -324,6 +345,8 @@ export class TxProposal implements ITxProposal {
324
345
  x.amount = obj.amount;
325
346
  x.message = obj.message;
326
347
  x.payProUrl = obj.payProUrl;
348
+ x.derivationStrategy = obj.derivationStrategy;
349
+ x.sendMax = obj.sendMax;
327
350
  x.changeAddress = obj.changeAddress;
328
351
  x.escrowAddress = obj.escrowAddress;
329
352
  x.instantAcceptanceEscrow = obj.instantAcceptanceEscrow;
@@ -337,9 +360,7 @@ export class TxProposal implements ITxProposal {
337
360
  x.txids = obj.txids;
338
361
  x.broadcastedOn = obj.broadcastedOn;
339
362
  x.inputPaths = obj.inputPaths;
340
- x.actions = _.map(obj.actions, action => {
341
- return TxProposalAction.fromObj(action);
342
- });
363
+ x.actions = (obj.actions || []).map(action => TxProposalAction.fromObj(action));
343
364
  x.outputOrder = obj.outputOrder;
344
365
  x.fee = obj.fee;
345
366
  x.feeLevel = obj.feeLevel;
@@ -410,7 +431,7 @@ export class TxProposal implements ITxProposal {
410
431
 
411
432
  setInputs(inputs) {
412
433
  this.inputs = inputs || [];
413
- this.inputPaths = _.map(inputs, 'path') || [];
434
+ this.inputPaths = this.inputs.map(input => input.path);
414
435
  }
415
436
 
416
437
  _updateStatus() {
@@ -440,8 +461,10 @@ export class TxProposal implements ITxProposal {
440
461
  return signatures;
441
462
  }
442
463
 
443
- getRawTx() {
444
- const t = ChainService.getBitcoreTx(this);
464
+ getRawTx(numberFormat?: NumberFormat) {
465
+ // Casting numberFormat to 'number' is to sidestep TS errors, but is not necessarily true.
466
+ const txp = numberFormat ? TxProposal.formatNumbers(this, numberFormat as 'number') : this as TxProposal<number>;
467
+ const t = ChainService.getBitcoreTx(txp);
445
468
  return t.uncheckedSerialize();
446
469
  }
447
470
 
@@ -451,7 +474,7 @@ export class TxProposal implements ITxProposal {
451
474
  * @return {Number} total amount of all outputs excluding change output
452
475
  */
453
476
  getTotalAmount() {
454
- return Number((this.outputs || []).reduce((total, o) => total += BigInt(o.amount), 0n));
477
+ return Number(((this as TxProposal<number>).outputs || []).reduce((total, o) => total += BigInt(o.amount), 0n));
455
478
  }
456
479
 
457
480
  /**
@@ -460,7 +483,7 @@ export class TxProposal implements ITxProposal {
460
483
  * @return {String[]} copayerIds that performed actions in this proposal (accept / reject)
461
484
  */
462
485
  getActors() {
463
- return _.map(this.actions, 'copayerId');
486
+ return (this.actions || []).map(a => a.copayerId);
464
487
  }
465
488
 
466
489
  /**
@@ -469,12 +492,9 @@ export class TxProposal implements ITxProposal {
469
492
  * @return {String[]} copayerIds that approved the tx proposal (accept)
470
493
  */
471
494
  getApprovers() {
472
- return _.map(
473
- _.filter(this.actions, a => {
474
- return a.type == 'accept';
475
- }),
476
- 'copayerId'
477
- );
495
+ return (this.actions || [])
496
+ .filter(a => a.type == 'accept')
497
+ .map(a => a.copayerId);
478
498
  }
479
499
 
480
500
  /**
@@ -484,9 +504,7 @@ export class TxProposal implements ITxProposal {
484
504
  * @return {Object} type / createdOn
485
505
  */
486
506
  getActionBy(copayerId) {
487
- return _.find(this.actions, {
488
- copayerId
489
- });
507
+ return (this.actions || []).find(a => a.copayerId == copayerId);
490
508
  }
491
509
 
492
510
  addAction(copayerId, type, comment, signatures?, xpub?) {
@@ -501,10 +519,11 @@ export class TxProposal implements ITxProposal {
501
519
  this._updateStatus();
502
520
  }
503
521
 
504
- sign(copayerId, signatures, xpub) {
522
+ sign(copayerId, signatures, xpub, numberFormat?: NumberFormat) {
505
523
  try {
506
- // Tests signatures are OK
507
- const tx = ChainService.getBitcoreTx(this);
524
+ // numberFormat as 'number' is to sidestep TS errors, but is not necessarily true.
525
+ const txp = numberFormat ? TxProposal.formatNumbers(this, numberFormat as 'number') : this as TxProposal<number>;
526
+ const tx = ChainService.getBitcoreTx(txp);
508
527
  ChainService.addSignaturesToBitcoreTx(
509
528
  this.chain,
510
529
  tx,
@@ -570,4 +589,70 @@ export class TxProposal implements ITxProposal {
570
589
  this.status = 'broadcasted';
571
590
  this.broadcastedOn = Math.floor(Date.now() / 1000);
572
591
  }
592
+
593
+
594
+ /**
595
+ * Replaces tx-building number values with the specified number format.
596
+ * This is to ensure consistency across BWC and BWS when handling large numbers, especially for chains like ETH and SOL.
597
+ * @param {TxProposal|ITxProposal} txp Transaction Proposal
598
+ * @param {'number'|'string'|'bigint'|'hex'} numberFormat The desired number format for the tx-building values. Can be 'number', 'string', 'bigint', or 'hex'.
599
+ */
600
+ static formatNumbers<T>(txp: TxProposal<T>, numberFormat: 'string'): TxProposal<string>;
601
+ static formatNumbers<T>(txp: TxProposal<T>, numberFormat: 'hex'): TxProposal<string>;
602
+ static formatNumbers<T>(txp: TxProposal<T>, numberFormat: 'bigint'): TxProposal<bigint>;
603
+ static formatNumbers<T>(txp: TxProposal<T>, numberFormat: 'number'): TxProposal<number>;
604
+ static formatNumbers<T>(txp: ITxProposal<T>, numberFormat: 'string'): ITxProposal<string>;
605
+ static formatNumbers<T>(txp: ITxProposal<T>, numberFormat: 'hex'): ITxProposal<string>;
606
+ static formatNumbers<T>(txp: ITxProposal<T>, numberFormat: 'bigint'): ITxProposal<bigint>;
607
+ static formatNumbers<T>(txp: ITxProposal<T>, numberFormat: 'number'): ITxProposal<number>;
608
+ static formatNumbers<T>(txp: TxProposal<T> | ITxProposal<T>, numberFormat: NumberFormat = 'number') {
609
+ let convertFn;
610
+ switch (numberFormat) {
611
+ case 'number':
612
+ convertFn = parseInt;
613
+ break;
614
+ case 'string':
615
+ convertFn = (n) => typeof n === 'string' && n.startsWith('0x') ? BigInt(n).toString() : n.toString();
616
+ break;
617
+ case 'hex':
618
+ convertFn = (n) => CWCUtils.toHex(n);
619
+ break;
620
+ case 'bigint':
621
+ convertFn = (n) => BigInt(n);
622
+ break;
623
+ default:
624
+ logger.warn(`Invalid numberFormat: ${numberFormat}, no conversion will be applied to tx proposal ${txp.id}`);
625
+ return txp;
626
+ }
627
+
628
+ const primitiveTypes = new Set(['number', 'string', 'bigint']);
629
+ const convert = (key, value) => {
630
+ if ((numberFormat === 'hex' || typeof value !== numberFormat) && primitiveTypes.has(typeof value)) {
631
+ try {
632
+ value = convertFn(value);
633
+ } catch (e) {
634
+ logger.warn(`Failed to convert ${txp.id} > ${key} with value ${value} to ${numberFormat}: ${e.message}`);
635
+ }
636
+ }
637
+ return value;
638
+ };
639
+
640
+ const _txp = txp instanceof TxProposal ? TxProposal.fromObj(txp.toObject()) : TxProposal.fromObj(txp as ITxProposal<number>).toObject();
641
+
642
+ const topKeys = ['amount', 'feePerKb', 'fee', 'nonce', 'gasPrice', 'maxGasFee', 'priorityGasFee', 'gasLimit', 'lockUntilBlockHeight', 'instantAcceptanceEscrow', 'space', 'blockHeight', 'computeUnits', 'decimals'];
643
+ for (const key of topKeys) {
644
+ const value = _txp[key];
645
+ _txp[key] = convert(key, value);
646
+ }
647
+
648
+ const outputKeys = ['amount', 'gasLimit', 'satoshis'];
649
+ for (let i = 0; i < _txp.outputs.length; i++) {
650
+ for (const key of outputKeys) {
651
+ const value = _txp.outputs[i][key];
652
+ _txp.outputs[i][key] = convert(`output.${i}.${key}`, value);
653
+ }
654
+ }
655
+
656
+ return _txp;
657
+ }
573
658
  }
@@ -15,7 +15,7 @@ function throwUnsupportedError() {
15
15
  throw new Error(msg);
16
16
  }
17
17
 
18
- export interface ITxProposal {
18
+ export interface ITxProposalLegacy {
19
19
  version: string;
20
20
  type: string;
21
21
  createdOn: number;
@@ -35,7 +35,7 @@ export interface ITxProposal {
35
35
  walletN: number;
36
36
  status: string;
37
37
  txid: string;
38
- broadcastedOn: string;
38
+ broadcastedOn: number;
39
39
  inputPaths: string;
40
40
  actions: any[];
41
41
  outputOrder: number;
@@ -49,8 +49,9 @@ export interface ITxProposal {
49
49
  addressType: string;
50
50
  derivationStrategy: string;
51
51
  customData: any;
52
- }
53
- export class TxProposalLegacy {
52
+ };
53
+
54
+ export class TxProposalLegacy implements ITxProposalLegacy {
54
55
  version: string;
55
56
  type: string;
56
57
  createdOn: number;
@@ -6,6 +6,9 @@ export interface ITxProposalAction {
6
6
  signatures: string[];
7
7
  xpub: string;
8
8
  comment: string;
9
+
10
+ // Non-persistent fields
11
+ copayerName?: string;
9
12
  }
10
13
  export class TxProposalAction {
11
14
  version: string;
@@ -16,6 +19,9 @@ export class TxProposalAction {
16
19
  xpub: string;
17
20
  comment: string;
18
21
 
22
+ // Non-persistent fields
23
+ copayerName?: string;
24
+
19
25
  static create(opts) {
20
26
  opts = opts || {};
21
27
 
@@ -43,6 +49,9 @@ export class TxProposalAction {
43
49
  x.xpub = obj.xpub;
44
50
  x.comment = obj.comment;
45
51
 
52
+ // copayerName is not stored in the actions collection, but it is returned by the server on fetchTxProposal, so we need to set it here.
53
+ x.copayerName = obj.copayerName;
54
+
46
55
  return x;
47
56
  }
48
57
  }