@bitpay-labs/bitcore-wallet-service 11.11.1 → 11.11.3

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 (105) hide show
  1. package/Dockerfile +1 -1
  2. package/package.json +5 -5
  3. package/src/lib/chain/btc/index.ts +3 -2
  4. package/src/lib/chain/eth/index.ts +134 -117
  5. package/src/lib/chain/sol/index.ts +1 -1
  6. package/src/lib/chain/xrp/index.ts +1 -1
  7. package/src/lib/common/constants.ts +1 -1
  8. package/src/lib/common/utils.ts +18 -1
  9. package/src/lib/model/tsskeygen.ts +16 -0
  10. package/src/lib/routes/maintenance.ts +11 -2
  11. package/src/lib/routes/middleware/authTssRequest.ts +28 -16
  12. package/src/lib/routes/middleware/verifyTssMessage.ts +4 -1
  13. package/src/lib/routes/services.ts +7 -1
  14. package/src/lib/routes/tss.ts +33 -30
  15. package/src/lib/routes/wallets.ts +3 -1
  16. package/src/lib/server.ts +198 -105
  17. package/src/lib/storage.ts +129 -20
  18. package/src/lib/tss.ts +111 -7
  19. package/test/chain/eth.test.ts +70 -0
  20. package/test/chain/sol.test.ts +1 -1
  21. package/test/chain/xrp.test.ts +1 -1
  22. package/test/expressapp.test.ts +243 -0
  23. package/test/integration/addressReads.test.ts +503 -0
  24. package/test/integration/bcmonitor.test.ts +44 -0
  25. package/test/integration/server.test.ts +251 -49
  26. package/test/integration/tss.test.ts +428 -0
  27. package/test/integration/walletReads.test.ts +488 -0
  28. package/ts_build/package.json +4 -4
  29. package/ts_build/src/lib/chain/btc/index.d.ts.map +1 -1
  30. package/ts_build/src/lib/chain/btc/index.js +3 -2
  31. package/ts_build/src/lib/chain/btc/index.js.map +1 -1
  32. package/ts_build/src/lib/chain/eth/index.d.ts +8 -1
  33. package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
  34. package/ts_build/src/lib/chain/eth/index.js +114 -102
  35. package/ts_build/src/lib/chain/eth/index.js.map +1 -1
  36. package/ts_build/src/lib/chain/sol/index.js +1 -1
  37. package/ts_build/src/lib/chain/sol/index.js.map +1 -1
  38. package/ts_build/src/lib/chain/xrp/index.js +1 -1
  39. package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
  40. package/ts_build/src/lib/common/constants.d.ts +1 -1
  41. package/ts_build/src/lib/common/constants.js +1 -1
  42. package/ts_build/src/lib/common/index.d.ts +2 -1
  43. package/ts_build/src/lib/common/index.d.ts.map +1 -1
  44. package/ts_build/src/lib/common/utils.d.ts +1 -0
  45. package/ts_build/src/lib/common/utils.d.ts.map +1 -1
  46. package/ts_build/src/lib/common/utils.js +16 -1
  47. package/ts_build/src/lib/common/utils.js.map +1 -1
  48. package/ts_build/src/lib/model/tsskeygen.d.ts +10 -0
  49. package/ts_build/src/lib/model/tsskeygen.d.ts.map +1 -1
  50. package/ts_build/src/lib/model/tsskeygen.js +3 -0
  51. package/ts_build/src/lib/model/tsskeygen.js.map +1 -1
  52. package/ts_build/src/lib/routes/maintenance.d.ts.map +1 -1
  53. package/ts_build/src/lib/routes/maintenance.js +12 -4
  54. package/ts_build/src/lib/routes/maintenance.js.map +1 -1
  55. package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts +6 -2
  56. package/ts_build/src/lib/routes/middleware/authTssRequest.d.ts.map +1 -1
  57. package/ts_build/src/lib/routes/middleware/authTssRequest.js +21 -14
  58. package/ts_build/src/lib/routes/middleware/authTssRequest.js.map +1 -1
  59. package/ts_build/src/lib/routes/middleware/verifyTssMessage.d.ts.map +1 -1
  60. package/ts_build/src/lib/routes/middleware/verifyTssMessage.js +2 -1
  61. package/ts_build/src/lib/routes/middleware/verifyTssMessage.js.map +1 -1
  62. package/ts_build/src/lib/routes/services.d.ts.map +1 -1
  63. package/ts_build/src/lib/routes/services.js +6 -1
  64. package/ts_build/src/lib/routes/services.js.map +1 -1
  65. package/ts_build/src/lib/routes/tss.d.ts.map +1 -1
  66. package/ts_build/src/lib/routes/tss.js +32 -30
  67. package/ts_build/src/lib/routes/tss.js.map +1 -1
  68. package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
  69. package/ts_build/src/lib/routes/wallets.js +2 -1
  70. package/ts_build/src/lib/routes/wallets.js.map +1 -1
  71. package/ts_build/src/lib/server.d.ts +24 -3
  72. package/ts_build/src/lib/server.d.ts.map +1 -1
  73. package/ts_build/src/lib/server.js +148 -97
  74. package/ts_build/src/lib/server.js.map +1 -1
  75. package/ts_build/src/lib/storage.d.ts +26 -10
  76. package/ts_build/src/lib/storage.d.ts.map +1 -1
  77. package/ts_build/src/lib/storage.js +99 -20
  78. package/ts_build/src/lib/storage.js.map +1 -1
  79. package/ts_build/src/lib/tss.d.ts +12 -0
  80. package/ts_build/src/lib/tss.d.ts.map +1 -1
  81. package/ts_build/src/lib/tss.js +64 -5
  82. package/ts_build/src/lib/tss.js.map +1 -1
  83. package/ts_build/test/chain/eth.test.js +59 -0
  84. package/ts_build/test/chain/eth.test.js.map +1 -1
  85. package/ts_build/test/chain/sol.test.js +1 -1
  86. package/ts_build/test/chain/sol.test.js.map +1 -1
  87. package/ts_build/test/chain/xrp.test.js +1 -1
  88. package/ts_build/test/chain/xrp.test.js.map +1 -1
  89. package/ts_build/test/expressapp.test.js +222 -0
  90. package/ts_build/test/expressapp.test.js.map +1 -1
  91. package/ts_build/test/integration/addressReads.test.d.ts +2 -0
  92. package/ts_build/test/integration/addressReads.test.d.ts.map +1 -0
  93. package/ts_build/test/integration/addressReads.test.js +465 -0
  94. package/ts_build/test/integration/addressReads.test.js.map +1 -0
  95. package/ts_build/test/integration/bcmonitor.test.js +40 -0
  96. package/ts_build/test/integration/bcmonitor.test.js.map +1 -1
  97. package/ts_build/test/integration/server.test.js +228 -48
  98. package/ts_build/test/integration/server.test.js.map +1 -1
  99. package/ts_build/test/integration/tss.test.d.ts.map +1 -1
  100. package/ts_build/test/integration/tss.test.js +344 -0
  101. package/ts_build/test/integration/tss.test.js.map +1 -1
  102. package/ts_build/test/integration/walletReads.test.d.ts +2 -0
  103. package/ts_build/test/integration/walletReads.test.d.ts.map +1 -0
  104. package/ts_build/test/integration/walletReads.test.js +436 -0
  105. package/ts_build/test/integration/walletReads.test.js.map +1 -0
@@ -3,6 +3,7 @@
3
3
  import * as chai from 'chai';
4
4
  import 'chai/register-should';
5
5
  import util from 'util';
6
+ import assert from 'assert';
6
7
  import sinon from 'sinon';
7
8
  import http from 'http';
8
9
  import request from 'request';
@@ -20,6 +21,7 @@ import * as TestData from '../testdata';
20
21
  import helpers from './helpers';
21
22
  import { ClientError } from '../../src/lib/errors/clienterror';
22
23
  import { ExpressApp } from '../../src/lib/expressapp';
24
+ import { type JoinWalletOpts } from '../../src/lib/server';
23
25
 
24
26
  const should = chai.should();
25
27
  config.moralis = config.moralis ?? {
@@ -1119,7 +1121,7 @@ describe('Wallet service', function() {
1119
1121
  xPubKey: TestData.copayers[0].xPubKey_44H_0H_0H,
1120
1122
  requestPubKey: TestData.copayers[0].pubKey_1H_0,
1121
1123
  };
1122
- server.joinWallet(copayerOpts, function(err) {
1124
+ server.joinWallet(copayerOpts as JoinWalletOpts, function(err) {
1123
1125
  should.exist(err);
1124
1126
  err.message.should.contain('argument: copayerSignature missing');
1125
1127
  done();
@@ -1773,6 +1775,57 @@ describe('Wallet service', function() {
1773
1775
  wallet.copayers.length.should.equal(0);
1774
1776
  });
1775
1777
  });
1778
+
1779
+ describe('TSS wallets (concurrent join)', function() {
1780
+ it('should keep a copayer that joins while the wallet is being registered', async function() {
1781
+ const server = new WalletService();
1782
+ const copayerIds = [0, 1].map(i => Model.Copayer.xPubToCopayerId('btc', TestData.copayers[i].xPubKey_44H_0H_0H));
1783
+
1784
+ const session = TssKeyGenModel.create({
1785
+ id: 'tss-join-during-register-test-session',
1786
+ message: { partyId: 0, broadcastMessages: [], p2pMessages: [], publicKey: 'dummy', round: 0 },
1787
+ n: 2,
1788
+ copayerId: copayerIds[0],
1789
+ version: Defaults.TSS_KEYGEN_SCHEME_VERSION
1790
+ });
1791
+ session.participants[1] = copayerIds[1];
1792
+ session.sharedPublicKey = 'dummy-shared-public-key';
1793
+ await server.storage.db.collection('tss_keygen').deleteMany({ id: session.id });
1794
+ await server.storage.storeTssKeyGenSession({ doc: session });
1795
+
1796
+ const walletId = await util.promisify(server.createWallet).call(server, {
1797
+ name: 'tss wallet',
1798
+ m: 1,
1799
+ n: 1,
1800
+ pubKey: TestData.keyPair.pub,
1801
+ coin: 'btc',
1802
+ tssKeyId: session.id,
1803
+ });
1804
+ const [creatorOpts, joinerOpts] = [0, 1].map(i => helpers.getSignedCopayerOpts({
1805
+ walletId,
1806
+ name: 'copayer ' + (i + 1),
1807
+ xPubKey: TestData.copayers[i].xPubKey_44H_0H_0H,
1808
+ requestPubKey: TestData.copayers[i].pubKey_1H_0,
1809
+ }));
1810
+ const { copayerId } = await util.promisify(server.joinWallet).call(server, creatorOpts);
1811
+ const creatorServer = await helpers.getAuthServer(copayerId);
1812
+ creatorServer.checkWalletSync = function(a, b, simple, cb) {
1813
+ if (simple) return cb(null, false);
1814
+ return cb(null, true);
1815
+ };
1816
+
1817
+ blockchainExplorer.register = sinon.stub().callsFake((wallet, cb) => server.joinWallet(joinerOpts, cb));
1818
+ await util.promisify(creatorServer.getStatus).call(creatorServer, { includeExtendedInfo: true });
1819
+
1820
+ blockchainExplorer.register.calledOnce.should.equal(true);
1821
+ const registeredWallet = blockchainExplorer.register.firstCall.args[0];
1822
+ const wallet = await util.promisify(server.storage.fetchWallet).call(server.storage, walletId);
1823
+ wallet.copayers.length.should.equal(2);
1824
+ wallet.beRegistered.should.equal(true);
1825
+ wallet.beAuthPrivateKey2.should.equal(registeredWallet.beAuthPrivateKey2);
1826
+ wallet.beAuthPublicKey2.should.equal(registeredWallet.beAuthPublicKey2);
1827
+ });
1828
+ });
1776
1829
  });
1777
1830
 
1778
1831
  describe('#removeWallet', function() {
@@ -2930,30 +2983,23 @@ describe('Wallet service', function() {
2930
2983
  should.exist(address);
2931
2984
  utxo.path.should.equal(address.path);
2932
2985
  utxo.publicKeys.should.deep.equal(address.publicKeys);
2986
+ utxo.isChange.should.equal(false);
2933
2987
  done();
2934
2988
  });
2935
2989
  });
2936
2990
  });
2937
2991
  });
2938
2992
 
2993
+ it('should identify UTXOs for change addresses', async function() {
2994
+ const address = await util.promisify(server.createAddress).call(server, { isChange: true });
2995
+ await helpers.stubUtxos(server, wallet, [1], { addresses: [address] });
2939
2996
 
2940
- it('should get UTXOs for wallet addresses', function(done) {
2941
- helpers.stubUtxos(server, wallet, [1, 2]).then(function() {
2942
- server.getUtxos({}, function(err, utxos) {
2943
- should.not.exist(err);
2944
- should.exist(utxos);
2945
- utxos.length.should.equal(2);
2946
- utxos.reduce((sum, u) => sum += u.satoshis, 0).should.equal(3 * 1e8);
2947
- server.getAddresses({ noChange: true }, function(err, addresses) {
2948
- const utxo = utxos[0];
2949
- const address = addresses.find(a => a.address === utxo.address);
2950
- should.exist(address);
2951
- utxo.path.should.equal(address.path);
2952
- utxo.publicKeys.should.deep.equal(address.publicKeys);
2953
- done();
2954
- });
2955
- });
2956
- });
2997
+ const utxos = await util.promisify(server.getUtxos).call(server, {});
2998
+ utxos.length.should.equal(1);
2999
+ utxos[0].address.should.equal(address.address);
3000
+ utxos[0].path.should.equal(address.path);
3001
+ utxos[0].publicKeys.should.deep.equal(address.publicKeys);
3002
+ utxos[0].isChange.should.equal(true);
2957
3003
  });
2958
3004
 
2959
3005
  it('should return empty UTXOs for specific addresses if network mismatch', function(done) {
@@ -4951,7 +4997,6 @@ describe('Wallet service', function() {
4951
4997
  toAddress: addressStr,
4952
4998
  amount: ts,
4953
4999
  }],
4954
- gasPrice: 1,
4955
5000
  feeLevel: level,
4956
5001
  from: fromAddr,
4957
5002
  }, flags);
@@ -6186,36 +6231,36 @@ describe('Wallet service', function() {
6186
6231
  }));
6187
6232
  });
6188
6233
 
6189
- it('should allow to create a TX with fee and no inputs', function(done) {
6190
- helpers.stubFeeLevels({});
6191
- server.createAddress({}, from => {
6192
- helpers.stubUtxos(server, wallet, [1, 2]).then(function() {
6193
- const amount = 0.8 * 1e8;
6194
- const txOpts = {
6195
- outputs: [{
6196
- toAddress: '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A',
6197
- amount: amount,
6198
- }],
6199
- from,
6200
- message: 'some message',
6201
- customData: 'some custom data',
6202
- fee: 252000000000000,
6203
- };
6204
- server.createTx(txOpts, function(err, tx) {
6205
- should.not.exist(err);
6206
- should.exist(tx);
6207
- tx.outputs.should.deep.equal([{
6208
- toAddress: '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A',
6209
- gasLimit: 21000,
6210
- amount: amount,
6211
- }]);
6212
- tx.gasPrice.should.equal(12000000000);
6213
- tx.outputs[0].gasLimit.should.equal(21000);
6214
- (tx.gasPrice * tx.outputs[0].gasLimit).should.equal(txOpts.fee);
6215
- done();
6216
- });
6217
- });
6218
- });
6234
+ it('should allow to create a TX with fee and no inputs', async function() {
6235
+ const feeLevels = sinon.stub(server, 'getFeeLevels').callsArgWith(1, new Error('Unexpected fee estimation'));
6236
+ await util.promisify(server.createAddress).call(server, {});
6237
+ await helpers.stubUtxos(server, wallet, [1, 2]);
6238
+ const amount = 0.8 * 1e8;
6239
+
6240
+ for (const fee of [252000000000000, '252000000000000']) {
6241
+ const txOpts = {
6242
+ outputs: [{
6243
+ toAddress: '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A',
6244
+ amount: amount,
6245
+ }],
6246
+ message: 'some message',
6247
+ customData: 'some custom data',
6248
+ fee,
6249
+ };
6250
+ const tx = await util.promisify(server.createTx).call(server, txOpts);
6251
+ should.exist(tx);
6252
+ tx.outputs.should.deep.equal([{
6253
+ toAddress: '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A',
6254
+ gasLimit: 21000,
6255
+ amount: amount,
6256
+ }]);
6257
+ tx.fee.should.equal(Number(fee));
6258
+ tx.gasPrice.should.equal(12000000000);
6259
+ (tx.gasPrice * tx.outputs[0].gasLimit).should.equal(Number(fee));
6260
+ const [raw] = tx.getRawTx();
6261
+ CWC.ethers.Transaction.from(raw).gasPrice.should.equal(12000000000n);
6262
+ }
6263
+ feeLevels.called.should.be.false;
6219
6264
  });
6220
6265
 
6221
6266
  it('should allow to create a TX with multiple outputs and set the correct fee', function(done) {
@@ -6283,6 +6328,163 @@ describe('Wallet service', function() {
6283
6328
  });
6284
6329
  });
6285
6330
 
6331
+ describe('#createTX EVM gas overrides', function() {
6332
+ let server: WalletService;
6333
+ let wallet: Model.Wallet;
6334
+ const destination = '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A';
6335
+ const makeOpts = (overrides = {}) => ({
6336
+ outputs: [{ toAddress: destination, amount: 1000000, gasLimit: 100000 }],
6337
+ nonce: 1,
6338
+ ...overrides
6339
+ });
6340
+
6341
+ beforeEach(async function() {
6342
+ ({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'eth' }));
6343
+ await util.promisify(server.createAddress).call(server, {});
6344
+ await helpers.stubUtxos(server, wallet, [1]);
6345
+ });
6346
+
6347
+ for (const scenario of [
6348
+ { name: 'type 0', fees: { gasPrice: 3000000000 }, tokenAddress: undefined },
6349
+ { name: 'type 0 token', fees: { gasPrice: 3000000000 }, tokenAddress: TOKENS[0] },
6350
+ { name: 'type 2 native', fees: { txType: 2, maxGasFee: 3000000000, priorityGasFee: 1000000000 }, tokenAddress: undefined },
6351
+ { name: 'type 2 token', fees: { txType: 2, maxGasFee: 3000000000, priorityGasFee: 1000000000 }, tokenAddress: TOKENS[0] }
6352
+ ]) {
6353
+ it(`should preserve ${scenario.name} overrides through publication, signing and broadcast`, async function() {
6354
+ const feeLevels = sinon.stub(server, 'getFeeLevels').callsArgWith(1, new Error('Unexpected fee estimation'));
6355
+ const maxFee = sinon.stub(server, 'estimateFee').rejects(new Error('Unexpected fee estimation'));
6356
+ const priorityFee = sinon.stub(server, 'estimatePriorityFee').rejects(new Error('Unexpected fee estimation'));
6357
+ const txp = await util.promisify(server.createTx).call(server, makeOpts({
6358
+ ...scenario.fees, tokenAddress: scenario.tokenAddress, feeLevel: 'economy'
6359
+ }));
6360
+ await util.promisify(server.publishTx).call(server,
6361
+ helpers.getProposalSignatureOpts(txp, TestData.copayers[0].privKey_1H_0));
6362
+ await util.promisify(server.signTx).call(server, {
6363
+ txProposalId: txp.id,
6364
+ signatures: helpers.clientSign(txp, TestData.copayers[0].xPrivKey_44H_0H_0H)
6365
+ });
6366
+ const accepted = await util.promisify(server.getTx).call(server, { txProposalId: txp.id });
6367
+ accepted.status.should.equal('accepted');
6368
+ const signedRaw = accepted.getRawTx();
6369
+ helpers.stubBroadcast(accepted.txid);
6370
+ await util.promisify(server.broadcastTx).call(server, { txProposalId: txp.id });
6371
+ blockchainExplorer.broadcast.calledOnce.should.be.true;
6372
+ blockchainExplorer.broadcast.firstCall.args[0].should.deep.equal(signedRaw);
6373
+ const stored = await util.promisify(server.getTx).call(server, { txProposalId: txp.id });
6374
+ stored.status.should.equal('broadcasted');
6375
+ stored.txid.should.equal(accepted.txid);
6376
+ should.not.exist(stored.feeLevel);
6377
+ stored.fee.should.equal(300000000000000);
6378
+ for (const [key, value] of Object.entries(scenario.fees)) {
6379
+ stored[key].should.equal(value);
6380
+ }
6381
+ const [raw] = stored.getRawTx();
6382
+ const tx = CWC.ethers.Transaction.from(raw);
6383
+ tx.isSigned().should.be.true;
6384
+ tx.type.should.equal(scenario.fees.txType || 0);
6385
+ if (scenario.fees.txType === 2) {
6386
+ tx.maxFeePerGas.should.equal(3000000000n);
6387
+ tx.maxPriorityFeePerGas.should.equal(1000000000n);
6388
+ } else {
6389
+ tx.gasPrice.should.equal(3000000000n);
6390
+ }
6391
+ feeLevels.called.should.be.false;
6392
+ maxFee.called.should.be.false;
6393
+ priorityFee.called.should.be.false;
6394
+ });
6395
+ }
6396
+
6397
+ it('should preserve a zero priority fee estimated for ARB', async function() {
6398
+ ({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'arb' }));
6399
+ await util.promisify(server.createAddress).call(server, {});
6400
+ sinon.replace(blockchainExplorer, 'getBalance',
6401
+ sinon.stub().callsArgWith(1, null, { confirmed: 1e18, unconfirmed: 0, balance: 1e18 }));
6402
+ helpers.stubFeeLevels({});
6403
+ sinon.stub(server, 'estimateFee').resolves(3000000000);
6404
+ sinon.stub(server, 'estimatePriorityFee').resolves(0);
6405
+ const txp = await util.promisify(server.createTx).call(server, makeOpts({ txType: 2 }));
6406
+ txp.priorityGasFee.should.equal(0);
6407
+ const [raw] = txp.getRawTx();
6408
+ CWC.ethers.Transaction.from(raw).maxPriorityFeePerGas.should.equal(0n);
6409
+ });
6410
+
6411
+ it('should preserve automatic type 2 fee estimation without overrides', async function() {
6412
+ helpers.stubFeeLevels({});
6413
+ sinon.stub(server, 'estimateFee').resolves(4500000000);
6414
+ sinon.stub(server, 'estimatePriorityFee').resolves(1000000000);
6415
+ const txp = await util.promisify(server.createTx).call(server, makeOpts({ txType: 2 }));
6416
+ txp.feeLevel.should.equal('normal');
6417
+ txp.gasPrice.should.equal(1000000000);
6418
+ txp.fee.should.equal(100000000000000);
6419
+ txp.maxGasFee.should.equal(4500000000);
6420
+ txp.priorityGasFee.should.equal(1000000000);
6421
+ const [raw] = txp.getRawTx();
6422
+ const tx = CWC.ethers.Transaction.from(raw);
6423
+ tx.maxFeePerGas.should.equal(4500000000n);
6424
+ tx.maxPriorityFeePerGas.should.equal(1000000000n);
6425
+ });
6426
+
6427
+ it('should enforce the existing MATIC minimum for explicit priority fees', async function() {
6428
+ ({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'matic' }));
6429
+ await util.promisify(server.createAddress).call(server, {});
6430
+ sinon.replace(blockchainExplorer, 'getBalance',
6431
+ sinon.stub().callsArgWith(1, null, { confirmed: 1e18, unconfirmed: 0, balance: 1e18 }));
6432
+ const fees = { txType: 2, maxGasFee: 40000000000, priorityGasFee: 30000000000 };
6433
+ await assert.rejects(util.promisify(server.createTx).call(server,
6434
+ makeOpts({ ...fees, priorityGasFee: fees.priorityGasFee - 1 })), {
6435
+ message: 'priorityGasFee must be at least 30000000000 wei for matic'
6436
+ });
6437
+ const txp = await util.promisify(server.createTx).call(server, makeOpts(fees));
6438
+ const [raw] = txp.getRawTx();
6439
+ CWC.ethers.Transaction.from(raw).maxPriorityFeePerGas.should.equal(30000000000n);
6440
+ txp.fee.should.equal(fees.maxGasFee * 100000);
6441
+ });
6442
+
6443
+ it('should reject invalid or conflicting gas overrides', async function() {
6444
+ const requests = [
6445
+ ...[
6446
+ { key: 'gasPrice', values: [0, 1.5, '1000', Number.MAX_SAFE_INTEGER + 1] },
6447
+ { key: 'maxGasFee', values: [0] },
6448
+ { key: 'priorityGasFee', values: [0] }
6449
+ ].flatMap(({ key, values }) =>
6450
+ values.map(value => ({
6451
+ params: { [key]: value }, message: `${key} must be a positive safe integer in wei`
6452
+ }))
6453
+ ),
6454
+ { params: { gasPrice: 1000, txType: 2 }, message: 'gasPrice requires a type 0 transaction' },
6455
+ { params: { maxGasFee: 1000 }, message: 'maxGasFee and priorityGasFee require a type 2 transaction' },
6456
+ { params: { priorityGasFee: 1000, txType: 0 }, message: 'maxGasFee and priorityGasFee require a type 2 transaction' },
6457
+ { params: { maxGasFee: 1000, txType: 2 }, message: 'maxGasFee and priorityGasFee must be supplied together' },
6458
+ { params: { priorityGasFee: 1000, txType: 2 }, message: 'maxGasFee and priorityGasFee must be supplied together' },
6459
+ { params: { gasPrice: 1000, feePerKb: 1000 }, message: 'Gas fee overrides cannot be combined with feePerKb or fee' },
6460
+ { params: { gasPrice: 1000, fee: 1000 }, message: 'Gas fee overrides cannot be combined with feePerKb or fee' },
6461
+ { params: { gasPrice: 1000, sendMax: true }, message: 'Gas fee overrides are not supported with sendMax' },
6462
+ { params: { txType: 2, maxGasFee: 1000, priorityGasFee: 2000 }, message: 'priorityGasFee must not exceed maxGasFee' }
6463
+ ];
6464
+ for (const { params, message } of requests) {
6465
+ await assert.rejects(util.promisify(server.createTx).call(server, makeOpts(params)), { message });
6466
+ }
6467
+ await assert.rejects(util.promisify(server._validateAndSanitizeTxOpts).call(server,
6468
+ { ...wallet, chain: 'btc' }, makeOpts({ gasPrice: 1000 })), {
6469
+ message: 'Gas fee overrides are only supported for EVM chains'
6470
+ });
6471
+ });
6472
+
6473
+ for (const tokenAddress of [undefined, TOKENS[0]]) {
6474
+ it(`should reserve the overridden fee for ${tokenAddress ? 'token' : 'native'} proposals`, async function() {
6475
+ helpers.stubFeeLevels({});
6476
+ // Enough for the default fee, but not the overridden fee.
6477
+ await helpers.stubUtxos(server, wallet, [0.0002]);
6478
+ const defaultTxp = await util.promisify(server.createTx).call(server, makeOpts({ tokenAddress }));
6479
+ defaultTxp.fee.should.equal(100000000000000);
6480
+ await assert.rejects(util.promisify(server.createTx).call(server, makeOpts({
6481
+ txType: 2, maxGasFee: 3000000000, priorityGasFee: 1000000000, tokenAddress
6482
+ })), { code: tokenAddress ? 'INSUFFICIENT_ETH_FEE' : 'INSUFFICIENT_FUNDS_FOR_FEE' });
6483
+ });
6484
+ }
6485
+
6486
+ });
6487
+
6286
6488
  describe('cashAddr backwards compat', function() {
6287
6489
  // LEGACY MODE
6288
6490
  it('should create a BCH tx proposal with cashaddr outputs (w/o prefix) and return Copay addr', function(done) {