@bitpay-labs/bitcore-wallet-service 11.8.2 → 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.
- package/bws.example.config.js +4 -2
- package/package.json +4 -4
- package/src/config.ts +4 -2
- package/src/externalservices/moonpay.ts +85 -1
- package/src/lib/blockchainexplorers/v8.ts +1 -1
- package/src/lib/chain/eth/index.ts +8 -7
- package/src/lib/chain/index.ts +1 -1
- package/src/lib/chain/sol/index.ts +4 -4
- package/src/lib/chain/xrp/index.ts +3 -3
- package/src/lib/common/utils.ts +12 -5
- package/src/lib/errors/errordefinitions.ts +2 -0
- package/src/lib/expressapp.ts +6 -3
- package/src/lib/model/txnote.ts +3 -0
- package/src/lib/model/txproposal.ts +153 -68
- package/src/lib/model/txproposal_legacy.ts +5 -4
- package/src/lib/model/txproposalaction.ts +9 -0
- package/src/lib/routes/blockchain.ts +2 -3
- package/src/lib/routes/context.ts +10 -1
- package/src/lib/routes/maintenance.ts +6 -3
- package/src/lib/routes/services.ts +12 -0
- package/src/lib/routes/transactions.ts +17 -2
- package/src/lib/routes/walletdata.ts +1 -2
- package/src/lib/routes/wallets.ts +9 -4
- package/src/lib/server.ts +62 -9
- package/src/lib/storage.ts +13 -12
- package/src/types/expressapp.d.ts +2 -1
- package/test/chain/bch.test.ts +8 -1
- package/test/chain/btc.test.ts +2 -2
- package/test/chain/doge.test.ts +4 -6
- package/test/chain/ltc.test.ts +4 -5
- package/test/chain/xrp.test.ts +4 -39
- package/test/integration/externalservices/moonpay.test.ts +149 -0
- package/test/model/txproposal.test.ts +64 -1
- package/test/utils.test.ts +29 -0
- package/ts_build/package.json +3 -3
- package/ts_build/src/config.d.ts.map +1 -1
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/externalservices/moonpay.d.ts +4 -0
- package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
- package/ts_build/src/externalservices/moonpay.js +68 -1
- package/ts_build/src/externalservices/moonpay.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.js +1 -1
- package/ts_build/src/lib/chain/eth/index.d.ts +6 -5
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +5 -5
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/chain/index.d.ts +1 -1
- package/ts_build/src/lib/chain/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/index.js.map +1 -1
- package/ts_build/src/lib/chain/sol/index.js +4 -4
- package/ts_build/src/lib/chain/sol/index.js.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.js +3 -3
- package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
- package/ts_build/src/lib/common/utils.d.ts.map +1 -1
- package/ts_build/src/lib/common/utils.js +14 -9
- package/ts_build/src/lib/common/utils.js.map +1 -1
- package/ts_build/src/lib/errors/errordefinitions.d.ts +1 -0
- package/ts_build/src/lib/errors/errordefinitions.d.ts.map +1 -1
- package/ts_build/src/lib/errors/errordefinitions.js +1 -0
- package/ts_build/src/lib/errors/errordefinitions.js.map +1 -1
- package/ts_build/src/lib/expressapp.d.ts.map +1 -1
- package/ts_build/src/lib/expressapp.js +5 -3
- package/ts_build/src/lib/expressapp.js.map +1 -1
- package/ts_build/src/lib/model/txnote.d.ts +1 -0
- package/ts_build/src/lib/model/txnote.d.ts.map +1 -1
- package/ts_build/src/lib/model/txnote.js +1 -0
- package/ts_build/src/lib/model/txnote.js.map +1 -1
- package/ts_build/src/lib/model/txproposal.d.ts +72 -52
- package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposal.js +69 -17
- package/ts_build/src/lib/model/txproposal.js.map +1 -1
- package/ts_build/src/lib/model/txproposal_legacy.d.ts +3 -3
- package/ts_build/src/lib/model/txproposal_legacy.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposal_legacy.js +1 -0
- package/ts_build/src/lib/model/txproposal_legacy.js.map +1 -1
- package/ts_build/src/lib/model/txproposalaction.d.ts +2 -0
- package/ts_build/src/lib/model/txproposalaction.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposalaction.js +2 -0
- package/ts_build/src/lib/model/txproposalaction.js.map +1 -1
- package/ts_build/src/lib/routes/blockchain.d.ts.map +1 -1
- package/ts_build/src/lib/routes/blockchain.js +2 -36
- package/ts_build/src/lib/routes/blockchain.js.map +1 -1
- package/ts_build/src/lib/routes/context.d.ts +1 -0
- package/ts_build/src/lib/routes/context.d.ts.map +1 -1
- package/ts_build/src/lib/routes/context.js +8 -1
- package/ts_build/src/lib/routes/context.js.map +1 -1
- package/ts_build/src/lib/routes/maintenance.d.ts.map +1 -1
- package/ts_build/src/lib/routes/maintenance.js +6 -36
- package/ts_build/src/lib/routes/maintenance.js.map +1 -1
- package/ts_build/src/lib/routes/services.d.ts.map +1 -1
- package/ts_build/src/lib/routes/services.js +10 -0
- package/ts_build/src/lib/routes/services.js.map +1 -1
- package/ts_build/src/lib/routes/transactions.d.ts +1 -0
- package/ts_build/src/lib/routes/transactions.d.ts.map +1 -1
- package/ts_build/src/lib/routes/transactions.js +14 -2
- package/ts_build/src/lib/routes/transactions.js.map +1 -1
- package/ts_build/src/lib/routes/walletdata.d.ts.map +1 -1
- package/ts_build/src/lib/routes/walletdata.js +1 -35
- package/ts_build/src/lib/routes/walletdata.js.map +1 -1
- package/ts_build/src/lib/routes/wallets.d.ts +1 -0
- package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
- package/ts_build/src/lib/routes/wallets.js +8 -4
- package/ts_build/src/lib/routes/wallets.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +6 -2
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +45 -3
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +6 -6
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +6 -6
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/test/chain/bch.test.js +7 -0
- package/ts_build/test/chain/bch.test.js.map +1 -1
- package/ts_build/test/chain/btc.test.js +2 -2
- package/ts_build/test/chain/btc.test.js.map +1 -1
- package/ts_build/test/chain/doge.test.js +3 -5
- package/ts_build/test/chain/doge.test.js.map +1 -1
- package/ts_build/test/chain/ltc.test.js +3 -4
- package/ts_build/test/chain/ltc.test.js.map +1 -1
- package/ts_build/test/chain/xrp.test.js +3 -38
- package/ts_build/test/chain/xrp.test.js.map +1 -1
- package/ts_build/test/integration/externalservices/moonpay.test.js +139 -0
- package/ts_build/test/integration/externalservices/moonpay.test.js.map +1 -1
- package/ts_build/test/model/txproposal.test.js +57 -0
- package/ts_build/test/model/txproposal.test.js.map +1 -1
- package/ts_build/test/utils.test.js +23 -0
- package/ts_build/test/utils.test.js.map +1 -1
|
@@ -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 {
|
|
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
|
-
|
|
32
|
-
|
|
33
|
+
sendMax?: boolean;
|
|
34
|
+
changeAddress?: Partial<IAddress>;
|
|
35
|
+
escrowAddress?: Partial<IAddress>;
|
|
33
36
|
inputs: any[];
|
|
34
37
|
outputs: Array<{
|
|
35
|
-
amount:
|
|
36
|
-
address
|
|
38
|
+
amount: NumberType;
|
|
39
|
+
address?: string;
|
|
37
40
|
toAddress?: string;
|
|
38
41
|
sourceAddress?: string;
|
|
39
42
|
message?: string;
|
|
40
43
|
data?: string;
|
|
41
|
-
gasLimit?:
|
|
44
|
+
gasLimit?: NumberType;
|
|
42
45
|
script?: string;
|
|
43
|
-
satoshis?:
|
|
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:
|
|
57
|
+
feePerKb: NumberType;
|
|
55
58
|
excludeUnconfirmedUtxos: boolean;
|
|
56
59
|
addressType: string;
|
|
57
60
|
customData: any;
|
|
58
|
-
amount:
|
|
59
|
-
fee:
|
|
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
|
-
|
|
70
|
+
raw?: Array<string> | string;
|
|
71
|
+
nonce?: NumberType | string;
|
|
69
72
|
deferNonce?: boolean;
|
|
70
|
-
gasPrice?:
|
|
71
|
-
maxGasFee?:
|
|
72
|
-
priorityGasFee?:
|
|
73
|
+
gasPrice?: NumberType;
|
|
74
|
+
maxGasFee?: NumberType;
|
|
75
|
+
priorityGasFee?: NumberType;
|
|
73
76
|
txType?: number | string;
|
|
74
|
-
gasLimit?:
|
|
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?:
|
|
81
|
-
instantAcceptanceEscrow?:
|
|
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?:
|
|
91
|
+
space?: NumberType;
|
|
87
92
|
nonceAddress?: string;
|
|
88
93
|
blockHash?: string;
|
|
89
|
-
blockHeight?:
|
|
94
|
+
blockHeight?: NumberType;
|
|
90
95
|
category?: string;
|
|
91
|
-
priorityFee?:
|
|
92
|
-
computeUnits?:
|
|
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
|
|
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
|
-
|
|
116
|
-
|
|
127
|
+
sendMax?: boolean;
|
|
128
|
+
changeAddress?: Partial<IAddress>;
|
|
129
|
+
escrowAddress?: Partial<IAddress>;
|
|
117
130
|
inputs: any[];
|
|
118
131
|
outputs: Array<{
|
|
119
|
-
amount:
|
|
120
|
-
address
|
|
132
|
+
amount: NumberType;
|
|
133
|
+
address?: string;
|
|
121
134
|
toAddress?: string;
|
|
122
135
|
sourceAddress?: string;
|
|
123
136
|
message?: string;
|
|
124
137
|
data?: string;
|
|
125
|
-
gasLimit?:
|
|
138
|
+
gasLimit?: NumberType;
|
|
126
139
|
script?: string;
|
|
127
|
-
satoshis?:
|
|
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:
|
|
151
|
+
feePerKb: NumberType;
|
|
139
152
|
excludeUnconfirmedUtxos: boolean;
|
|
140
153
|
addressType: string;
|
|
141
154
|
customData: any;
|
|
142
|
-
amount:
|
|
143
|
-
fee:
|
|
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?:
|
|
165
|
+
nonce?: NumberType;
|
|
154
166
|
deferNonce?: boolean;
|
|
155
|
-
gasPrice?:
|
|
156
|
-
maxGasFee?:
|
|
157
|
-
priorityGasFee?:
|
|
167
|
+
gasPrice?: NumberType;
|
|
168
|
+
maxGasFee?: NumberType;
|
|
169
|
+
priorityGasFee?: NumberType;
|
|
158
170
|
txType?: number | string;
|
|
159
|
-
gasLimit?:
|
|
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?:
|
|
167
|
-
instantAcceptanceEscrow?:
|
|
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?:
|
|
185
|
+
space?: NumberType;
|
|
174
186
|
nonceAddress?: string;
|
|
175
187
|
blockHash?: string;
|
|
176
|
-
blockHeight?:
|
|
188
|
+
blockHeight?: NumberType;
|
|
177
189
|
category?: string;
|
|
178
|
-
priorityFee?:
|
|
179
|
-
computeUnits?:
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
473
|
-
|
|
474
|
-
|
|
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
|
|
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
|
-
//
|
|
507
|
-
const
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
-
import * as _ from 'lodash';
|
|
3
2
|
import type * as Types from '../../types/expressapp';
|
|
4
3
|
import type { WalletService } from '../server';
|
|
5
4
|
|
|
@@ -74,10 +73,10 @@ export function registerBlockchainRoutes(router: express.Router, context: RouteC
|
|
|
74
73
|
if (req.query.network) opts.network = req.query.network as string;
|
|
75
74
|
server.getFeeLevels(opts, resolve);
|
|
76
75
|
}).then((feeLevels: any[]) => {
|
|
77
|
-
|
|
76
|
+
for (const feeLevel of feeLevels) {
|
|
78
77
|
feeLevel.feePerKB = feeLevel.feePerKb;
|
|
79
78
|
delete feeLevel.feePerKb;
|
|
80
|
-
}
|
|
79
|
+
}
|
|
81
80
|
return feeLevels;
|
|
82
81
|
})
|
|
83
82
|
);
|
|
@@ -10,6 +10,7 @@ interface RouteHelpers {
|
|
|
10
10
|
getServerWithMultiAuth: Types.GetServerWithMultiAuthFn;
|
|
11
11
|
logDeprecated: Types.LogDeprecatedFn;
|
|
12
12
|
setPublicCache: (res: express.Response, seconds: number) => void;
|
|
13
|
+
checkNumberFormat: Types.CheckNumberFormatFn;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function createRouteHelpers(returnError: Types.ReturnErrorFn): RouteHelpers {
|
|
@@ -139,11 +140,19 @@ export function createRouteHelpers(returnError: Types.ReturnErrorFn): RouteHelpe
|
|
|
139
140
|
res.setHeader('Cache-Control', `public, max-age=${seconds}, stale-if-error=${10 * seconds}`);
|
|
140
141
|
};
|
|
141
142
|
|
|
143
|
+
const checkNumberFormat = (numberFormat: string | undefined, res: express.Response) => {
|
|
144
|
+
const validFormats = ['hex', 'string', 'number']; // bigint cannot be serialized to JSON, so it's not supported in the API response
|
|
145
|
+
if (numberFormat && !validFormats.includes(numberFormat)) {
|
|
146
|
+
return returnError(Errors.INVALID_NUMBER_FORMAT, res, null);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
142
150
|
return {
|
|
143
151
|
getServer,
|
|
144
152
|
getServerWithAuth,
|
|
145
153
|
getServerWithMultiAuth,
|
|
146
154
|
logDeprecated,
|
|
147
|
-
setPublicCache
|
|
155
|
+
setPublicCache,
|
|
156
|
+
checkNumberFormat
|
|
148
157
|
};
|
|
149
158
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
-
import * as _ from 'lodash';
|
|
3
2
|
import { WalletService } from '../server';
|
|
4
3
|
import { Stats } from '../stats';
|
|
5
4
|
import type * as Types from '../../types/expressapp';
|
|
@@ -84,8 +83,12 @@ export function registerMaintenanceRoutes(router: express.Router, context: Route
|
|
|
84
83
|
router.get('/v1/txnotes/', (req, res) => {
|
|
85
84
|
getServerWithAuth(req, res, server => {
|
|
86
85
|
const opts: { minTs?: number } = {};
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
const rawMinTs = req.query.minTs;
|
|
87
|
+
if (typeof rawMinTs === 'string' && rawMinTs.trim() !== '') {
|
|
88
|
+
const minTs = Number(rawMinTs);
|
|
89
|
+
if (Number.isFinite(minTs)) {
|
|
90
|
+
opts.minTs = minTs;
|
|
91
|
+
}
|
|
89
92
|
}
|
|
90
93
|
server.getTxNotes(opts, (err, notes) => {
|
|
91
94
|
if (err) return returnError(err, res, req);
|
|
@@ -281,6 +281,18 @@ export function registerServiceRoutes(router: express.Router, context: RouteCont
|
|
|
281
281
|
});
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
+
router.post('/v1/service/moonpay/createSession', (req, res) => {
|
|
285
|
+
respondWithAuthServer(req, res, context, server => {
|
|
286
|
+
return server.externalServices.moonpay.moonpayCreateSession(req);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
router.post('/v1/service/moonpay/revokeActiveSession', (req, res) => {
|
|
291
|
+
respondWithAuthServer(req, res, context, server => {
|
|
292
|
+
return server.externalServices.moonpay.moonpayRevokeActiveSession(req);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
284
296
|
router.post('/v1/service/ramp/quote', (req, res) => {
|
|
285
297
|
respondWithAuthServer(req, res, context, server => {
|
|
286
298
|
return server.externalServices.ramp.rampGetQuote(req);
|
|
@@ -5,10 +5,11 @@ import type * as Types from '../../types/expressapp';
|
|
|
5
5
|
interface RouteContext {
|
|
6
6
|
getServerWithAuth: Types.GetServerWithAuthFn;
|
|
7
7
|
returnError: Types.ReturnErrorFn;
|
|
8
|
+
checkNumberFormat: Types.CheckNumberFormatFn;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export function registerTransactionRoutes(router: express.Router, context: RouteContext) {
|
|
11
|
-
const { getServerWithAuth, returnError } = context;
|
|
12
|
+
const { getServerWithAuth, returnError, checkNumberFormat } = context;
|
|
12
13
|
|
|
13
14
|
router.get('/v1/txproposals/', (req, res) => {
|
|
14
15
|
getServerWithAuth(req, res, server => {
|
|
@@ -21,7 +22,13 @@ export function registerTransactionRoutes(router: express.Router, context: Route
|
|
|
21
22
|
|
|
22
23
|
router.get('/v2/txproposals/', (req, res) => {
|
|
23
24
|
getServerWithAuth(req, res, server => {
|
|
24
|
-
|
|
25
|
+
checkNumberFormat(req.query.numberFormat, res);
|
|
26
|
+
|
|
27
|
+
const opts = {
|
|
28
|
+
numberFormat: req.query.numberFormat,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
server.getPendingTxs(opts, (err, pendings) => {
|
|
25
32
|
if (err) return returnError(err, res, req);
|
|
26
33
|
res.json(pendings);
|
|
27
34
|
});
|
|
@@ -45,7 +52,9 @@ export function registerTransactionRoutes(router: express.Router, context: Route
|
|
|
45
52
|
|
|
46
53
|
router.post('/v3/txproposals/', (req, res) => {
|
|
47
54
|
getServerWithAuth(req, res, server => {
|
|
55
|
+
checkNumberFormat(req.query.numberFormat, res);
|
|
48
56
|
req.body.txpVersion = 3;
|
|
57
|
+
req.body.numberFormat = req.query.numberFormat;
|
|
49
58
|
server.createTx(req.body, (err, txp) => {
|
|
50
59
|
if (err) return returnError(err, res, req);
|
|
51
60
|
res.json(txp);
|
|
@@ -67,9 +76,11 @@ export function registerTransactionRoutes(router: express.Router, context: Route
|
|
|
67
76
|
|
|
68
77
|
router.post('/v2/txproposals/:id/signatures/', (req, res) => {
|
|
69
78
|
getServerWithAuth(req, res, server => {
|
|
79
|
+
checkNumberFormat(req.query.numberFormat, res);
|
|
70
80
|
req.body.txProposalId = req.params['id'];
|
|
71
81
|
req.body.maxTxpVersion = 3;
|
|
72
82
|
req.body.supportBchSchnorr = true;
|
|
83
|
+
req.body.numberFormat = req.query.numberFormat;
|
|
73
84
|
server.signTx(req.body, (err, txp) => {
|
|
74
85
|
if (err) return returnError(err, res, req);
|
|
75
86
|
res.json(txp);
|
|
@@ -80,7 +91,9 @@ export function registerTransactionRoutes(router: express.Router, context: Route
|
|
|
80
91
|
|
|
81
92
|
router.post('/v1/txproposals/:id/prepare/', (req, res) => {
|
|
82
93
|
getServerWithAuth(req, res, server => {
|
|
94
|
+
checkNumberFormat(req.query.numberFormat, res);
|
|
83
95
|
req.body.txProposalId = req.params['id'];
|
|
96
|
+
req.body.numberFormat = req.query.numberFormat;
|
|
84
97
|
server.prepareTx(req.body, (err, txp) => {
|
|
85
98
|
if (err) return returnError(err, res, req);
|
|
86
99
|
res.json(txp);
|
|
@@ -103,7 +116,9 @@ export function registerTransactionRoutes(router: express.Router, context: Route
|
|
|
103
116
|
|
|
104
117
|
router.post('/v2/txproposals/:id/publish/', (req, res) => {
|
|
105
118
|
getServerWithAuth(req, res, server => {
|
|
119
|
+
checkNumberFormat(req.query.numberFormat, res);
|
|
106
120
|
req.body.txProposalId = req.params['id'];
|
|
121
|
+
req.body.numberFormat = req.query.numberFormat;
|
|
107
122
|
server.publishTx(req.body, (err, txp) => {
|
|
108
123
|
if (err) return returnError(err, res, req);
|
|
109
124
|
res.json(txp);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
-
import * as _ from 'lodash';
|
|
3
2
|
import { Common } from '../common';
|
|
4
3
|
import type * as Types from '../../types/expressapp';
|
|
5
4
|
import type { GetAddressesOpts } from '../../types/server';
|
|
@@ -133,7 +132,7 @@ export function registerWalletDataRoutes(router: express.Router, context: RouteC
|
|
|
133
132
|
router.get('/v1/utxos/', (req, res) => {
|
|
134
133
|
const opts: { addresses?: string[] } = {};
|
|
135
134
|
const addresses = req.query.addresses;
|
|
136
|
-
if (addresses &&
|
|
135
|
+
if (addresses && typeof addresses === 'string') {
|
|
137
136
|
opts.addresses = (req.query.addresses as string).split(',');
|
|
138
137
|
}
|
|
139
138
|
|