@bitpay-labs/bitcore-wallet-service 11.11.1 → 11.11.2
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/Dockerfile +1 -1
- package/package.json +5 -5
- package/src/lib/chain/btc/index.ts +3 -2
- package/src/lib/chain/eth/index.ts +134 -117
- package/src/lib/chain/sol/index.ts +1 -1
- package/src/lib/chain/xrp/index.ts +1 -1
- package/src/lib/routes/wallets.ts +3 -1
- package/src/lib/server.ts +136 -88
- package/src/lib/storage.ts +90 -20
- package/test/chain/eth.test.ts +70 -0
- package/test/chain/sol.test.ts +1 -1
- package/test/chain/xrp.test.ts +1 -1
- package/test/expressapp.test.ts +144 -0
- package/test/integration/addressReads.test.ts +503 -0
- package/test/integration/bcmonitor.test.ts +44 -0
- package/test/integration/server.test.ts +198 -48
- package/test/integration/walletReads.test.ts +488 -0
- package/ts_build/package.json +3 -3
- package/ts_build/src/lib/chain/btc/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/btc/index.js +3 -2
- package/ts_build/src/lib/chain/btc/index.js.map +1 -1
- package/ts_build/src/lib/chain/eth/index.d.ts +8 -1
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +114 -102
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/chain/sol/index.js +1 -1
- package/ts_build/src/lib/chain/sol/index.js.map +1 -1
- package/ts_build/src/lib/chain/xrp/index.js +1 -1
- package/ts_build/src/lib/chain/xrp/index.js.map +1 -1
- package/ts_build/src/lib/routes/wallets.d.ts.map +1 -1
- package/ts_build/src/lib/routes/wallets.js +2 -1
- package/ts_build/src/lib/routes/wallets.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +1 -1
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +128 -94
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/storage.d.ts +17 -10
- package/ts_build/src/lib/storage.d.ts.map +1 -1
- package/ts_build/src/lib/storage.js +73 -20
- package/ts_build/src/lib/storage.js.map +1 -1
- package/ts_build/test/chain/eth.test.js +59 -0
- package/ts_build/test/chain/eth.test.js.map +1 -1
- package/ts_build/test/chain/sol.test.js +1 -1
- package/ts_build/test/chain/sol.test.js.map +1 -1
- package/ts_build/test/chain/xrp.test.js +1 -1
- package/ts_build/test/chain/xrp.test.js.map +1 -1
- package/ts_build/test/expressapp.test.js +132 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/addressReads.test.d.ts +2 -0
- package/ts_build/test/integration/addressReads.test.d.ts.map +1 -0
- package/ts_build/test/integration/addressReads.test.js +465 -0
- package/ts_build/test/integration/addressReads.test.js.map +1 -0
- package/ts_build/test/integration/bcmonitor.test.js +40 -0
- package/ts_build/test/integration/bcmonitor.test.js.map +1 -1
- package/ts_build/test/integration/server.test.js +181 -48
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/integration/walletReads.test.d.ts +2 -0
- package/ts_build/test/integration/walletReads.test.d.ts.map +1 -0
- package/ts_build/test/integration/walletReads.test.js +436 -0
- 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';
|
|
@@ -2930,30 +2931,23 @@ describe('Wallet service', function() {
|
|
|
2930
2931
|
should.exist(address);
|
|
2931
2932
|
utxo.path.should.equal(address.path);
|
|
2932
2933
|
utxo.publicKeys.should.deep.equal(address.publicKeys);
|
|
2934
|
+
utxo.isChange.should.equal(false);
|
|
2933
2935
|
done();
|
|
2934
2936
|
});
|
|
2935
2937
|
});
|
|
2936
2938
|
});
|
|
2937
2939
|
});
|
|
2938
2940
|
|
|
2941
|
+
it('should identify UTXOs for change addresses', async function() {
|
|
2942
|
+
const address = await util.promisify(server.createAddress).call(server, { isChange: true });
|
|
2943
|
+
await helpers.stubUtxos(server, wallet, [1], { addresses: [address] });
|
|
2939
2944
|
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
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
|
-
});
|
|
2945
|
+
const utxos = await util.promisify(server.getUtxos).call(server, {});
|
|
2946
|
+
utxos.length.should.equal(1);
|
|
2947
|
+
utxos[0].address.should.equal(address.address);
|
|
2948
|
+
utxos[0].path.should.equal(address.path);
|
|
2949
|
+
utxos[0].publicKeys.should.deep.equal(address.publicKeys);
|
|
2950
|
+
utxos[0].isChange.should.equal(true);
|
|
2957
2951
|
});
|
|
2958
2952
|
|
|
2959
2953
|
it('should return empty UTXOs for specific addresses if network mismatch', function(done) {
|
|
@@ -4951,7 +4945,6 @@ describe('Wallet service', function() {
|
|
|
4951
4945
|
toAddress: addressStr,
|
|
4952
4946
|
amount: ts,
|
|
4953
4947
|
}],
|
|
4954
|
-
gasPrice: 1,
|
|
4955
4948
|
feeLevel: level,
|
|
4956
4949
|
from: fromAddr,
|
|
4957
4950
|
}, flags);
|
|
@@ -6186,36 +6179,36 @@ describe('Wallet service', function() {
|
|
|
6186
6179
|
}));
|
|
6187
6180
|
});
|
|
6188
6181
|
|
|
6189
|
-
it('should allow to create a TX with fee and no inputs', function(
|
|
6190
|
-
|
|
6191
|
-
server.createAddress(
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6182
|
+
it('should allow to create a TX with fee and no inputs', async function() {
|
|
6183
|
+
const feeLevels = sinon.stub(server, 'getFeeLevels').callsArgWith(1, new Error('Unexpected fee estimation'));
|
|
6184
|
+
await util.promisify(server.createAddress).call(server, {});
|
|
6185
|
+
await helpers.stubUtxos(server, wallet, [1, 2]);
|
|
6186
|
+
const amount = 0.8 * 1e8;
|
|
6187
|
+
|
|
6188
|
+
for (const fee of [252000000000000, '252000000000000']) {
|
|
6189
|
+
const txOpts = {
|
|
6190
|
+
outputs: [{
|
|
6191
|
+
toAddress: '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A',
|
|
6192
|
+
amount: amount,
|
|
6193
|
+
}],
|
|
6194
|
+
message: 'some message',
|
|
6195
|
+
customData: 'some custom data',
|
|
6196
|
+
fee,
|
|
6197
|
+
};
|
|
6198
|
+
const tx = await util.promisify(server.createTx).call(server, txOpts);
|
|
6199
|
+
should.exist(tx);
|
|
6200
|
+
tx.outputs.should.deep.equal([{
|
|
6201
|
+
toAddress: '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A',
|
|
6202
|
+
gasLimit: 21000,
|
|
6203
|
+
amount: amount,
|
|
6204
|
+
}]);
|
|
6205
|
+
tx.fee.should.equal(Number(fee));
|
|
6206
|
+
tx.gasPrice.should.equal(12000000000);
|
|
6207
|
+
(tx.gasPrice * tx.outputs[0].gasLimit).should.equal(Number(fee));
|
|
6208
|
+
const [raw] = tx.getRawTx();
|
|
6209
|
+
CWC.ethers.Transaction.from(raw).gasPrice.should.equal(12000000000n);
|
|
6210
|
+
}
|
|
6211
|
+
feeLevels.called.should.be.false;
|
|
6219
6212
|
});
|
|
6220
6213
|
|
|
6221
6214
|
it('should allow to create a TX with multiple outputs and set the correct fee', function(done) {
|
|
@@ -6283,6 +6276,163 @@ describe('Wallet service', function() {
|
|
|
6283
6276
|
});
|
|
6284
6277
|
});
|
|
6285
6278
|
|
|
6279
|
+
describe('#createTX EVM gas overrides', function() {
|
|
6280
|
+
let server: WalletService;
|
|
6281
|
+
let wallet: Model.Wallet;
|
|
6282
|
+
const destination = '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A';
|
|
6283
|
+
const makeOpts = (overrides = {}) => ({
|
|
6284
|
+
outputs: [{ toAddress: destination, amount: 1000000, gasLimit: 100000 }],
|
|
6285
|
+
nonce: 1,
|
|
6286
|
+
...overrides
|
|
6287
|
+
});
|
|
6288
|
+
|
|
6289
|
+
beforeEach(async function() {
|
|
6290
|
+
({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'eth' }));
|
|
6291
|
+
await util.promisify(server.createAddress).call(server, {});
|
|
6292
|
+
await helpers.stubUtxos(server, wallet, [1]);
|
|
6293
|
+
});
|
|
6294
|
+
|
|
6295
|
+
for (const scenario of [
|
|
6296
|
+
{ name: 'type 0', fees: { gasPrice: 3000000000 }, tokenAddress: undefined },
|
|
6297
|
+
{ name: 'type 0 token', fees: { gasPrice: 3000000000 }, tokenAddress: TOKENS[0] },
|
|
6298
|
+
{ name: 'type 2 native', fees: { txType: 2, maxGasFee: 3000000000, priorityGasFee: 1000000000 }, tokenAddress: undefined },
|
|
6299
|
+
{ name: 'type 2 token', fees: { txType: 2, maxGasFee: 3000000000, priorityGasFee: 1000000000 }, tokenAddress: TOKENS[0] }
|
|
6300
|
+
]) {
|
|
6301
|
+
it(`should preserve ${scenario.name} overrides through publication, signing and broadcast`, async function() {
|
|
6302
|
+
const feeLevels = sinon.stub(server, 'getFeeLevels').callsArgWith(1, new Error('Unexpected fee estimation'));
|
|
6303
|
+
const maxFee = sinon.stub(server, 'estimateFee').rejects(new Error('Unexpected fee estimation'));
|
|
6304
|
+
const priorityFee = sinon.stub(server, 'estimatePriorityFee').rejects(new Error('Unexpected fee estimation'));
|
|
6305
|
+
const txp = await util.promisify(server.createTx).call(server, makeOpts({
|
|
6306
|
+
...scenario.fees, tokenAddress: scenario.tokenAddress, feeLevel: 'economy'
|
|
6307
|
+
}));
|
|
6308
|
+
await util.promisify(server.publishTx).call(server,
|
|
6309
|
+
helpers.getProposalSignatureOpts(txp, TestData.copayers[0].privKey_1H_0));
|
|
6310
|
+
await util.promisify(server.signTx).call(server, {
|
|
6311
|
+
txProposalId: txp.id,
|
|
6312
|
+
signatures: helpers.clientSign(txp, TestData.copayers[0].xPrivKey_44H_0H_0H)
|
|
6313
|
+
});
|
|
6314
|
+
const accepted = await util.promisify(server.getTx).call(server, { txProposalId: txp.id });
|
|
6315
|
+
accepted.status.should.equal('accepted');
|
|
6316
|
+
const signedRaw = accepted.getRawTx();
|
|
6317
|
+
helpers.stubBroadcast(accepted.txid);
|
|
6318
|
+
await util.promisify(server.broadcastTx).call(server, { txProposalId: txp.id });
|
|
6319
|
+
blockchainExplorer.broadcast.calledOnce.should.be.true;
|
|
6320
|
+
blockchainExplorer.broadcast.firstCall.args[0].should.deep.equal(signedRaw);
|
|
6321
|
+
const stored = await util.promisify(server.getTx).call(server, { txProposalId: txp.id });
|
|
6322
|
+
stored.status.should.equal('broadcasted');
|
|
6323
|
+
stored.txid.should.equal(accepted.txid);
|
|
6324
|
+
should.not.exist(stored.feeLevel);
|
|
6325
|
+
stored.fee.should.equal(300000000000000);
|
|
6326
|
+
for (const [key, value] of Object.entries(scenario.fees)) {
|
|
6327
|
+
stored[key].should.equal(value);
|
|
6328
|
+
}
|
|
6329
|
+
const [raw] = stored.getRawTx();
|
|
6330
|
+
const tx = CWC.ethers.Transaction.from(raw);
|
|
6331
|
+
tx.isSigned().should.be.true;
|
|
6332
|
+
tx.type.should.equal(scenario.fees.txType || 0);
|
|
6333
|
+
if (scenario.fees.txType === 2) {
|
|
6334
|
+
tx.maxFeePerGas.should.equal(3000000000n);
|
|
6335
|
+
tx.maxPriorityFeePerGas.should.equal(1000000000n);
|
|
6336
|
+
} else {
|
|
6337
|
+
tx.gasPrice.should.equal(3000000000n);
|
|
6338
|
+
}
|
|
6339
|
+
feeLevels.called.should.be.false;
|
|
6340
|
+
maxFee.called.should.be.false;
|
|
6341
|
+
priorityFee.called.should.be.false;
|
|
6342
|
+
});
|
|
6343
|
+
}
|
|
6344
|
+
|
|
6345
|
+
it('should preserve a zero priority fee estimated for ARB', async function() {
|
|
6346
|
+
({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'arb' }));
|
|
6347
|
+
await util.promisify(server.createAddress).call(server, {});
|
|
6348
|
+
sinon.replace(blockchainExplorer, 'getBalance',
|
|
6349
|
+
sinon.stub().callsArgWith(1, null, { confirmed: 1e18, unconfirmed: 0, balance: 1e18 }));
|
|
6350
|
+
helpers.stubFeeLevels({});
|
|
6351
|
+
sinon.stub(server, 'estimateFee').resolves(3000000000);
|
|
6352
|
+
sinon.stub(server, 'estimatePriorityFee').resolves(0);
|
|
6353
|
+
const txp = await util.promisify(server.createTx).call(server, makeOpts({ txType: 2 }));
|
|
6354
|
+
txp.priorityGasFee.should.equal(0);
|
|
6355
|
+
const [raw] = txp.getRawTx();
|
|
6356
|
+
CWC.ethers.Transaction.from(raw).maxPriorityFeePerGas.should.equal(0n);
|
|
6357
|
+
});
|
|
6358
|
+
|
|
6359
|
+
it('should preserve automatic type 2 fee estimation without overrides', async function() {
|
|
6360
|
+
helpers.stubFeeLevels({});
|
|
6361
|
+
sinon.stub(server, 'estimateFee').resolves(4500000000);
|
|
6362
|
+
sinon.stub(server, 'estimatePriorityFee').resolves(1000000000);
|
|
6363
|
+
const txp = await util.promisify(server.createTx).call(server, makeOpts({ txType: 2 }));
|
|
6364
|
+
txp.feeLevel.should.equal('normal');
|
|
6365
|
+
txp.gasPrice.should.equal(1000000000);
|
|
6366
|
+
txp.fee.should.equal(100000000000000);
|
|
6367
|
+
txp.maxGasFee.should.equal(4500000000);
|
|
6368
|
+
txp.priorityGasFee.should.equal(1000000000);
|
|
6369
|
+
const [raw] = txp.getRawTx();
|
|
6370
|
+
const tx = CWC.ethers.Transaction.from(raw);
|
|
6371
|
+
tx.maxFeePerGas.should.equal(4500000000n);
|
|
6372
|
+
tx.maxPriorityFeePerGas.should.equal(1000000000n);
|
|
6373
|
+
});
|
|
6374
|
+
|
|
6375
|
+
it('should enforce the existing MATIC minimum for explicit priority fees', async function() {
|
|
6376
|
+
({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'matic' }));
|
|
6377
|
+
await util.promisify(server.createAddress).call(server, {});
|
|
6378
|
+
sinon.replace(blockchainExplorer, 'getBalance',
|
|
6379
|
+
sinon.stub().callsArgWith(1, null, { confirmed: 1e18, unconfirmed: 0, balance: 1e18 }));
|
|
6380
|
+
const fees = { txType: 2, maxGasFee: 40000000000, priorityGasFee: 30000000000 };
|
|
6381
|
+
await assert.rejects(util.promisify(server.createTx).call(server,
|
|
6382
|
+
makeOpts({ ...fees, priorityGasFee: fees.priorityGasFee - 1 })), {
|
|
6383
|
+
message: 'priorityGasFee must be at least 30000000000 wei for matic'
|
|
6384
|
+
});
|
|
6385
|
+
const txp = await util.promisify(server.createTx).call(server, makeOpts(fees));
|
|
6386
|
+
const [raw] = txp.getRawTx();
|
|
6387
|
+
CWC.ethers.Transaction.from(raw).maxPriorityFeePerGas.should.equal(30000000000n);
|
|
6388
|
+
txp.fee.should.equal(fees.maxGasFee * 100000);
|
|
6389
|
+
});
|
|
6390
|
+
|
|
6391
|
+
it('should reject invalid or conflicting gas overrides', async function() {
|
|
6392
|
+
const requests = [
|
|
6393
|
+
...[
|
|
6394
|
+
{ key: 'gasPrice', values: [0, 1.5, '1000', Number.MAX_SAFE_INTEGER + 1] },
|
|
6395
|
+
{ key: 'maxGasFee', values: [0] },
|
|
6396
|
+
{ key: 'priorityGasFee', values: [0] }
|
|
6397
|
+
].flatMap(({ key, values }) =>
|
|
6398
|
+
values.map(value => ({
|
|
6399
|
+
params: { [key]: value }, message: `${key} must be a positive safe integer in wei`
|
|
6400
|
+
}))
|
|
6401
|
+
),
|
|
6402
|
+
{ params: { gasPrice: 1000, txType: 2 }, message: 'gasPrice requires a type 0 transaction' },
|
|
6403
|
+
{ params: { maxGasFee: 1000 }, message: 'maxGasFee and priorityGasFee require a type 2 transaction' },
|
|
6404
|
+
{ params: { priorityGasFee: 1000, txType: 0 }, message: 'maxGasFee and priorityGasFee require a type 2 transaction' },
|
|
6405
|
+
{ params: { maxGasFee: 1000, txType: 2 }, message: 'maxGasFee and priorityGasFee must be supplied together' },
|
|
6406
|
+
{ params: { priorityGasFee: 1000, txType: 2 }, message: 'maxGasFee and priorityGasFee must be supplied together' },
|
|
6407
|
+
{ params: { gasPrice: 1000, feePerKb: 1000 }, message: 'Gas fee overrides cannot be combined with feePerKb or fee' },
|
|
6408
|
+
{ params: { gasPrice: 1000, fee: 1000 }, message: 'Gas fee overrides cannot be combined with feePerKb or fee' },
|
|
6409
|
+
{ params: { gasPrice: 1000, sendMax: true }, message: 'Gas fee overrides are not supported with sendMax' },
|
|
6410
|
+
{ params: { txType: 2, maxGasFee: 1000, priorityGasFee: 2000 }, message: 'priorityGasFee must not exceed maxGasFee' }
|
|
6411
|
+
];
|
|
6412
|
+
for (const { params, message } of requests) {
|
|
6413
|
+
await assert.rejects(util.promisify(server.createTx).call(server, makeOpts(params)), { message });
|
|
6414
|
+
}
|
|
6415
|
+
await assert.rejects(util.promisify(server._validateAndSanitizeTxOpts).call(server,
|
|
6416
|
+
{ ...wallet, chain: 'btc' }, makeOpts({ gasPrice: 1000 })), {
|
|
6417
|
+
message: 'Gas fee overrides are only supported for EVM chains'
|
|
6418
|
+
});
|
|
6419
|
+
});
|
|
6420
|
+
|
|
6421
|
+
for (const tokenAddress of [undefined, TOKENS[0]]) {
|
|
6422
|
+
it(`should reserve the overridden fee for ${tokenAddress ? 'token' : 'native'} proposals`, async function() {
|
|
6423
|
+
helpers.stubFeeLevels({});
|
|
6424
|
+
// Enough for the default fee, but not the overridden fee.
|
|
6425
|
+
await helpers.stubUtxos(server, wallet, [0.0002]);
|
|
6426
|
+
const defaultTxp = await util.promisify(server.createTx).call(server, makeOpts({ tokenAddress }));
|
|
6427
|
+
defaultTxp.fee.should.equal(100000000000000);
|
|
6428
|
+
await assert.rejects(util.promisify(server.createTx).call(server, makeOpts({
|
|
6429
|
+
txType: 2, maxGasFee: 3000000000, priorityGasFee: 1000000000, tokenAddress
|
|
6430
|
+
})), { code: tokenAddress ? 'INSUFFICIENT_ETH_FEE' : 'INSUFFICIENT_FUNDS_FOR_FEE' });
|
|
6431
|
+
});
|
|
6432
|
+
}
|
|
6433
|
+
|
|
6434
|
+
});
|
|
6435
|
+
|
|
6286
6436
|
describe('cashAddr backwards compat', function() {
|
|
6287
6437
|
// LEGACY MODE
|
|
6288
6438
|
it('should create a BCH tx proposal with cashaddr outputs (w/o prefix) and return Copay addr', function(done) {
|