@bitpay-labs/bitcore-wallet-service 11.6.6 → 11.7.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/package.json +22 -11
- package/src/externalservices/moonpay.ts +3 -0
- package/src/lib/blockchainexplorers/v8.ts +42 -0
- package/src/lib/chain/eth/index.ts +3 -2
- package/src/lib/expressapp.ts +25 -0
- package/src/lib/model/txproposal.ts +8 -0
- package/src/lib/routes/aave.ts +48 -0
- package/src/lib/routes/tss.ts +2 -2
- package/src/lib/server.ts +183 -6
- package/src/lib/tss.ts +9 -4
- package/test/expressapp.test.ts +170 -0
- package/test/integration/server.test.ts +333 -0
- package/test/v8.test.ts +103 -0
- package/ts_build/package.json +21 -10
- package/ts_build/src/externalservices/changelly.d.ts +2 -2
- package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
- package/ts_build/src/externalservices/moonpay.js +6 -0
- package/ts_build/src/externalservices/moonpay.js.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts +12 -0
- package/ts_build/src/lib/blockchainexplorers/v8.d.ts.map +1 -1
- package/ts_build/src/lib/blockchainexplorers/v8.js +39 -0
- package/ts_build/src/lib/blockchainexplorers/v8.js.map +1 -1
- package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
- package/ts_build/src/lib/chain/eth/index.js +3 -2
- package/ts_build/src/lib/chain/eth/index.js.map +1 -1
- package/ts_build/src/lib/expressapp.d.ts.map +1 -1
- package/ts_build/src/lib/expressapp.js +23 -0
- package/ts_build/src/lib/expressapp.js.map +1 -1
- package/ts_build/src/lib/model/txproposal.d.ts +3 -0
- package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
- package/ts_build/src/lib/model/txproposal.js +6 -0
- package/ts_build/src/lib/model/txproposal.js.map +1 -1
- package/ts_build/src/lib/routes/aave.d.ts +12 -0
- package/ts_build/src/lib/routes/aave.d.ts.map +1 -0
- package/ts_build/src/lib/routes/aave.js +47 -0
- package/ts_build/src/lib/routes/aave.js.map +1 -0
- package/ts_build/src/lib/routes/tss.js +2 -2
- package/ts_build/src/lib/routes/tss.js.map +1 -1
- package/ts_build/src/lib/server.d.ts +5 -0
- package/ts_build/src/lib/server.d.ts.map +1 -1
- package/ts_build/src/lib/server.js +131 -6
- package/ts_build/src/lib/server.js.map +1 -1
- package/ts_build/src/lib/tss.d.ts +1 -0
- package/ts_build/src/lib/tss.d.ts.map +1 -1
- package/ts_build/src/lib/tss.js +6 -2
- package/ts_build/src/lib/tss.js.map +1 -1
- package/ts_build/test/expressapp.test.js +162 -0
- package/ts_build/test/expressapp.test.js.map +1 -1
- package/ts_build/test/integration/server.test.js +281 -0
- package/ts_build/test/integration/server.test.js.map +1 -1
- package/ts_build/test/v8.test.js +89 -0
- package/ts_build/test/v8.test.js.map +1 -1
package/test/expressapp.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import config from '../src/config';
|
|
|
10
10
|
import { Common } from '../src/lib/common';
|
|
11
11
|
import { WalletService } from '../src/lib/server';
|
|
12
12
|
import { ExpressApp } from '../src/lib/expressapp';
|
|
13
|
+
import { ClientError } from '../src/lib/errors/clienterror';
|
|
13
14
|
|
|
14
15
|
const Defaults = Common.Defaults;
|
|
15
16
|
const should = chai.should();
|
|
@@ -483,6 +484,175 @@ describe('ExpressApp', function() {
|
|
|
483
484
|
});
|
|
484
485
|
|
|
485
486
|
});
|
|
487
|
+
describe('Aave service routes', function() {
|
|
488
|
+
it('/v1/service/aave/userAccountData', function(done) {
|
|
489
|
+
const server = {
|
|
490
|
+
getAaveUserAccountData: sinon.stub().resolves({ totalCollateralBase: '1000', healthFactor: '2.0' }),
|
|
491
|
+
};
|
|
492
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
493
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
494
|
+
start(ExpressApp, function() {
|
|
495
|
+
const requestOptions = {
|
|
496
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/service/aave/userAccountData',
|
|
497
|
+
method: 'post',
|
|
498
|
+
json: { chain: 'eth', network: 'mainnet', address: '0x123', version: 'v3' }
|
|
499
|
+
};
|
|
500
|
+
request(requestOptions, function(err, res, body) {
|
|
501
|
+
should.not.exist(err);
|
|
502
|
+
res.statusCode.should.equal(200);
|
|
503
|
+
body.totalCollateralBase.should.equal('1000');
|
|
504
|
+
body.healthFactor.should.equal('2.0');
|
|
505
|
+
done();
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('/v1/service/aave/userAccountData should fail with missing arguments', function(done) {
|
|
511
|
+
const server = {
|
|
512
|
+
getAaveUserAccountData: sinon.stub().rejects(new ClientError('getAaveUserAccountData request missing arguments')),
|
|
513
|
+
};
|
|
514
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
515
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
516
|
+
start(ExpressApp, function() {
|
|
517
|
+
const requestOptions = {
|
|
518
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/service/aave/userAccountData',
|
|
519
|
+
method: 'post',
|
|
520
|
+
json: { version: 'v3' }
|
|
521
|
+
};
|
|
522
|
+
request(requestOptions, function(err, res) {
|
|
523
|
+
should.not.exist(err);
|
|
524
|
+
res.statusCode.should.equal(400);
|
|
525
|
+
done();
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('/v1/service/aave/reserveData', function(done) {
|
|
531
|
+
const server = {
|
|
532
|
+
getAaveReserveData: sinon.stub().resolves({ currentVariableBorrowRate: '35000000' }),
|
|
533
|
+
};
|
|
534
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
535
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
536
|
+
start(ExpressApp, function() {
|
|
537
|
+
const requestOptions = {
|
|
538
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/service/aave/reserveData',
|
|
539
|
+
method: 'post',
|
|
540
|
+
json: { chain: 'eth', network: 'mainnet', asset: '0xabc', version: 'v3' }
|
|
541
|
+
};
|
|
542
|
+
request(requestOptions, function(err, res, body) {
|
|
543
|
+
should.not.exist(err);
|
|
544
|
+
res.statusCode.should.equal(200);
|
|
545
|
+
body.currentVariableBorrowRate.should.equal('35000000');
|
|
546
|
+
done();
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('/v1/service/aave/reserveData should fail with missing arguments', function(done) {
|
|
552
|
+
const server = {
|
|
553
|
+
getAaveReserveData: sinon.stub().rejects(new ClientError('getAaveReserveData request missing arguments')),
|
|
554
|
+
};
|
|
555
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
556
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
557
|
+
start(ExpressApp, function() {
|
|
558
|
+
const requestOptions = {
|
|
559
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/service/aave/reserveData',
|
|
560
|
+
method: 'post',
|
|
561
|
+
json: { version: 'v3' }
|
|
562
|
+
};
|
|
563
|
+
request(requestOptions, function(err, res) {
|
|
564
|
+
should.not.exist(err);
|
|
565
|
+
res.statusCode.should.equal(400);
|
|
566
|
+
done();
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('/v1/service/aave/reserveTokensAddresses', function(done) {
|
|
572
|
+
const server = {
|
|
573
|
+
getAaveReserveTokensAddresses: sinon.stub().resolves({ variableDebtTokenAddress: '0xdef' }),
|
|
574
|
+
};
|
|
575
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
576
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
577
|
+
start(ExpressApp, function() {
|
|
578
|
+
const requestOptions = {
|
|
579
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/service/aave/reserveTokensAddresses',
|
|
580
|
+
method: 'post',
|
|
581
|
+
json: { chain: 'eth', network: 'mainnet', asset: '0xabc', version: 'v3' }
|
|
582
|
+
};
|
|
583
|
+
request(requestOptions, function(err, res, body) {
|
|
584
|
+
should.not.exist(err);
|
|
585
|
+
res.statusCode.should.equal(200);
|
|
586
|
+
body.variableDebtTokenAddress.should.equal('0xdef');
|
|
587
|
+
done();
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('/v1/service/aave/reserveTokensAddresses should fail with missing arguments', function(done) {
|
|
593
|
+
const server = {
|
|
594
|
+
getAaveReserveTokensAddresses: sinon.stub().rejects(new ClientError('getAaveReserveTokensAddresses request missing arguments')),
|
|
595
|
+
};
|
|
596
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
597
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
598
|
+
start(ExpressApp, function() {
|
|
599
|
+
const requestOptions = {
|
|
600
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/service/aave/reserveTokensAddresses',
|
|
601
|
+
method: 'post',
|
|
602
|
+
json: { version: 'v3' }
|
|
603
|
+
};
|
|
604
|
+
request(requestOptions, function(err, res) {
|
|
605
|
+
should.not.exist(err);
|
|
606
|
+
res.statusCode.should.equal(400);
|
|
607
|
+
done();
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
describe('Token allowance', function() {
|
|
614
|
+
it('/v1/token/allowance', function(done) {
|
|
615
|
+
const server = {
|
|
616
|
+
getTokenAllowance: sinon.stub().resolves(5000000),
|
|
617
|
+
};
|
|
618
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
619
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
620
|
+
start(ExpressApp, function() {
|
|
621
|
+
const requestOptions = {
|
|
622
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/token/allowance',
|
|
623
|
+
method: 'post',
|
|
624
|
+
json: { chain: 'eth', network: 'mainnet', tokenAddress: '0xtoken', ownerAddress: '0xowner', spenderAddress: '0xspender' }
|
|
625
|
+
};
|
|
626
|
+
request(requestOptions, function(err, res, body) {
|
|
627
|
+
should.not.exist(err);
|
|
628
|
+
res.statusCode.should.equal(200);
|
|
629
|
+
body.should.equal(5000000);
|
|
630
|
+
done();
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('/v1/token/allowance should fail with missing arguments', function(done) {
|
|
636
|
+
const server = {
|
|
637
|
+
getTokenAllowance: sinon.stub().rejects(new ClientError('getTokenAllowance request missing arguments')),
|
|
638
|
+
};
|
|
639
|
+
sandbox.stub(WalletService, 'initialize').callsArg(1);
|
|
640
|
+
sandbox.stub(WalletService, 'getInstance').returns(server);
|
|
641
|
+
start(ExpressApp, function() {
|
|
642
|
+
const requestOptions = {
|
|
643
|
+
url: testHost + ':' + testPort + config.basePath + '/v1/token/allowance',
|
|
644
|
+
method: 'post',
|
|
645
|
+
json: {}
|
|
646
|
+
};
|
|
647
|
+
request(requestOptions, function(err, res) {
|
|
648
|
+
should.not.exist(err);
|
|
649
|
+
res.statusCode.should.equal(400);
|
|
650
|
+
done();
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
|
|
486
656
|
describe('Clear cache', function() {
|
|
487
657
|
it('/v1/clearcache/', function(done) {
|
|
488
658
|
const resolveStub = sinon.stub().callsFake( () => { return Promise.resolve(true);});
|
|
@@ -7801,6 +7801,339 @@ describe('Wallet service', function() {
|
|
|
7801
7801
|
});
|
|
7802
7802
|
});
|
|
7803
7803
|
|
|
7804
|
+
describe('#prepareTx (deferred nonce)', function() {
|
|
7805
|
+
const ETH_ADDR = '0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A';
|
|
7806
|
+
let server, wallet, fromAddr;
|
|
7807
|
+
|
|
7808
|
+
beforeEach(async function() {
|
|
7809
|
+
({ server, wallet } = await helpers.createAndJoinWallet(1, 1, { coin: 'eth' }));
|
|
7810
|
+
const address = await util.promisify(server.createAddress).call(server, {});
|
|
7811
|
+
fromAddr = address.address;
|
|
7812
|
+
await helpers.stubUtxos(server, wallet, [1, 2], { coin: 'eth' });
|
|
7813
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '5');
|
|
7814
|
+
});
|
|
7815
|
+
|
|
7816
|
+
it('should create txp with nonce=null when deferNonce is true', async function() {
|
|
7817
|
+
const txOpts = {
|
|
7818
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
7819
|
+
feePerKb: 123e2,
|
|
7820
|
+
from: fromAddr,
|
|
7821
|
+
deferNonce: true
|
|
7822
|
+
};
|
|
7823
|
+
const txp = await util.promisify(server.createTx).call(server, txOpts);
|
|
7824
|
+
should.exist(txp);
|
|
7825
|
+
txp.deferNonce.should.be.true;
|
|
7826
|
+
should.not.exist(txp.nonce);
|
|
7827
|
+
});
|
|
7828
|
+
|
|
7829
|
+
it('should create txp with nonce when deferNonce is absent', async function() {
|
|
7830
|
+
const txOpts = {
|
|
7831
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
7832
|
+
feePerKb: 123e2,
|
|
7833
|
+
from: fromAddr
|
|
7834
|
+
};
|
|
7835
|
+
const txp = await util.promisify(server.createTx).call(server, txOpts);
|
|
7836
|
+
should.exist(txp);
|
|
7837
|
+
should.not.exist(txp.deferNonce);
|
|
7838
|
+
txp.nonce.should.equal('5');
|
|
7839
|
+
});
|
|
7840
|
+
|
|
7841
|
+
it('should publish a deferred-nonce txp and save prePublishRaw', async function() {
|
|
7842
|
+
const txOpts = {
|
|
7843
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
7844
|
+
feePerKb: 123e2,
|
|
7845
|
+
from: fromAddr,
|
|
7846
|
+
deferNonce: true
|
|
7847
|
+
};
|
|
7848
|
+
const txp = await util.promisify(server.createTx).call(server, txOpts);
|
|
7849
|
+
should.not.exist(txp.nonce);
|
|
7850
|
+
|
|
7851
|
+
const publishOpts = helpers.getProposalSignatureOpts(txp, TestData.copayers[0].privKey_1H_0);
|
|
7852
|
+
const published = await util.promisify(server.publishTx).call(server, publishOpts);
|
|
7853
|
+
should.exist(published);
|
|
7854
|
+
published.status.should.equal('pending');
|
|
7855
|
+
published.deferNonce.should.be.true;
|
|
7856
|
+
should.exist(published.prePublishRaw);
|
|
7857
|
+
});
|
|
7858
|
+
|
|
7859
|
+
it('should assign nonce to a deferred-nonce txp', async function() {
|
|
7860
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '10');
|
|
7861
|
+
|
|
7862
|
+
const txp = await helpers.createAndPublishTx(server, {
|
|
7863
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
7864
|
+
feePerKb: 123e2,
|
|
7865
|
+
from: fromAddr,
|
|
7866
|
+
deferNonce: true
|
|
7867
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7868
|
+
|
|
7869
|
+
should.not.exist(txp.nonce);
|
|
7870
|
+
|
|
7871
|
+
const result = await util.promisify(server.prepareTx).call(server, {
|
|
7872
|
+
txProposalId: txp.id
|
|
7873
|
+
});
|
|
7874
|
+
should.exist(result);
|
|
7875
|
+
result.nonce.should.equal(10);
|
|
7876
|
+
});
|
|
7877
|
+
|
|
7878
|
+
it('should return txp as-is if deferNonce is not set', async function() {
|
|
7879
|
+
const txp = await helpers.createAndPublishTx(server, {
|
|
7880
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
7881
|
+
feePerKb: 123e2,
|
|
7882
|
+
from: fromAddr
|
|
7883
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7884
|
+
|
|
7885
|
+
txp.nonce.should.equal('5');
|
|
7886
|
+
|
|
7887
|
+
const result = await util.promisify(server.prepareTx).call(server, {
|
|
7888
|
+
txProposalId: txp.id
|
|
7889
|
+
});
|
|
7890
|
+
result.nonce.should.equal('5');
|
|
7891
|
+
});
|
|
7892
|
+
|
|
7893
|
+
it('should fail for non-existent txp', function(done) {
|
|
7894
|
+
server.prepareTx({ txProposalId: 'nonexistent' }, function(err) {
|
|
7895
|
+
should.exist(err);
|
|
7896
|
+
err.message.should.contain('not found');
|
|
7897
|
+
done();
|
|
7898
|
+
});
|
|
7899
|
+
});
|
|
7900
|
+
|
|
7901
|
+
it('should calculate gap-free nonce skipping pending txp nonces', async function() {
|
|
7902
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '5');
|
|
7903
|
+
|
|
7904
|
+
const txp1 = await helpers.createAndPublishTx(server, {
|
|
7905
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 1000 }],
|
|
7906
|
+
feePerKb: 123e2,
|
|
7907
|
+
from: fromAddr
|
|
7908
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7909
|
+
txp1.nonce.should.equal('5');
|
|
7910
|
+
|
|
7911
|
+
const txp2 = await helpers.createAndPublishTx(server, {
|
|
7912
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 2000 }],
|
|
7913
|
+
feePerKb: 123e2,
|
|
7914
|
+
from: fromAddr,
|
|
7915
|
+
deferNonce: true
|
|
7916
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7917
|
+
should.not.exist(txp2.nonce);
|
|
7918
|
+
|
|
7919
|
+
const result = await util.promisify(server.prepareTx).call(server, {
|
|
7920
|
+
txProposalId: txp2.id
|
|
7921
|
+
});
|
|
7922
|
+
result.nonce.should.equal(6);
|
|
7923
|
+
});
|
|
7924
|
+
|
|
7925
|
+
it('should assign sequential nonces for multiple deferred txps', async function() {
|
|
7926
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '0');
|
|
7927
|
+
|
|
7928
|
+
const txps = [];
|
|
7929
|
+
for (let i = 0; i < 3; i++) {
|
|
7930
|
+
const txp = await helpers.createAndPublishTx(server, {
|
|
7931
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 1000 * (i + 1) }],
|
|
7932
|
+
feePerKb: 123e2,
|
|
7933
|
+
from: fromAddr,
|
|
7934
|
+
deferNonce: true
|
|
7935
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7936
|
+
txps.push(txp);
|
|
7937
|
+
}
|
|
7938
|
+
|
|
7939
|
+
const results = [];
|
|
7940
|
+
for (const txp of txps) {
|
|
7941
|
+
const result = await util.promisify(server.prepareTx).call(server, {
|
|
7942
|
+
txProposalId: txp.id
|
|
7943
|
+
});
|
|
7944
|
+
results.push(result);
|
|
7945
|
+
}
|
|
7946
|
+
|
|
7947
|
+
results[0].nonce.should.equal(0);
|
|
7948
|
+
results[1].nonce.should.equal(1);
|
|
7949
|
+
results[2].nonce.should.equal(2);
|
|
7950
|
+
});
|
|
7951
|
+
|
|
7952
|
+
it('should handle mix of normal and deferred-nonce txps', async function() {
|
|
7953
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '3');
|
|
7954
|
+
|
|
7955
|
+
const normalTxp = await helpers.createAndPublishTx(server, {
|
|
7956
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 1000 }],
|
|
7957
|
+
feePerKb: 123e2,
|
|
7958
|
+
from: fromAddr
|
|
7959
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7960
|
+
normalTxp.nonce.should.equal('3');
|
|
7961
|
+
|
|
7962
|
+
const deferred1 = await helpers.createAndPublishTx(server, {
|
|
7963
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 2000 }],
|
|
7964
|
+
feePerKb: 123e2,
|
|
7965
|
+
from: fromAddr,
|
|
7966
|
+
deferNonce: true
|
|
7967
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7968
|
+
|
|
7969
|
+
const deferred2 = await helpers.createAndPublishTx(server, {
|
|
7970
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 3000 }],
|
|
7971
|
+
feePerKb: 123e2,
|
|
7972
|
+
from: fromAddr,
|
|
7973
|
+
deferNonce: true
|
|
7974
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7975
|
+
|
|
7976
|
+
const result1 = await util.promisify(server.prepareTx).call(server, {
|
|
7977
|
+
txProposalId: deferred1.id
|
|
7978
|
+
});
|
|
7979
|
+
result1.nonce.should.equal(4);
|
|
7980
|
+
|
|
7981
|
+
const result2 = await util.promisify(server.prepareTx).call(server, {
|
|
7982
|
+
txProposalId: deferred2.id
|
|
7983
|
+
});
|
|
7984
|
+
result2.nonce.should.equal(5);
|
|
7985
|
+
});
|
|
7986
|
+
|
|
7987
|
+
it('should sign a deferred-nonce txp after assignNonce', async function() {
|
|
7988
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '7');
|
|
7989
|
+
helpers.stubBroadcast('txid123');
|
|
7990
|
+
|
|
7991
|
+
const txp = await helpers.createAndPublishTx(server, {
|
|
7992
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
7993
|
+
feePerKb: 123e2,
|
|
7994
|
+
from: fromAddr,
|
|
7995
|
+
deferNonce: true
|
|
7996
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
7997
|
+
|
|
7998
|
+
const withNonce = await util.promisify(server.prepareTx).call(server, {
|
|
7999
|
+
txProposalId: txp.id
|
|
8000
|
+
});
|
|
8001
|
+
withNonce.nonce.should.equal(7);
|
|
8002
|
+
|
|
8003
|
+
const fetched = await util.promisify(server.getTx).call(server, { txProposalId: txp.id });
|
|
8004
|
+
const signatures = helpers.clientSign(fetched, TestData.copayers[0].xPrivKey_44H_0H_0H);
|
|
8005
|
+
const signed = await util.promisify(server.signTx).call(server, {
|
|
8006
|
+
txProposalId: txp.id,
|
|
8007
|
+
signatures
|
|
8008
|
+
});
|
|
8009
|
+
signed.status.should.equal('accepted');
|
|
8010
|
+
});
|
|
8011
|
+
|
|
8012
|
+
it('should not trigger nonce conflict for deferred txps with null nonce', async function() {
|
|
8013
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '5');
|
|
8014
|
+
|
|
8015
|
+
const normalTxp = await helpers.createAndPublishTx(server, {
|
|
8016
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 1000 }],
|
|
8017
|
+
feePerKb: 123e2,
|
|
8018
|
+
from: fromAddr
|
|
8019
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
8020
|
+
normalTxp.nonce.should.equal('5');
|
|
8021
|
+
|
|
8022
|
+
const deferredTxp = await helpers.createAndPublishTx(server, {
|
|
8023
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 2000 }],
|
|
8024
|
+
feePerKb: 123e2,
|
|
8025
|
+
from: fromAddr,
|
|
8026
|
+
deferNonce: true
|
|
8027
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
8028
|
+
|
|
8029
|
+
const withNonce = await util.promisify(server.prepareTx).call(server, {
|
|
8030
|
+
txProposalId: deferredTxp.id
|
|
8031
|
+
});
|
|
8032
|
+
withNonce.nonce.should.equal(6);
|
|
8033
|
+
|
|
8034
|
+
const sigs1 = helpers.clientSign(normalTxp, TestData.copayers[0].xPrivKey_44H_0H_0H);
|
|
8035
|
+
const signed1 = await util.promisify(server.signTx).call(server, {
|
|
8036
|
+
txProposalId: normalTxp.id,
|
|
8037
|
+
signatures: sigs1
|
|
8038
|
+
});
|
|
8039
|
+
signed1.status.should.equal('accepted');
|
|
8040
|
+
helpers.stubBroadcast(signed1.txid);
|
|
8041
|
+
await util.promisify(server.broadcastTx).call(server, { txProposalId: normalTxp.id });
|
|
8042
|
+
|
|
8043
|
+
const fetchedDeferred = await util.promisify(server.getTx).call(server, { txProposalId: deferredTxp.id });
|
|
8044
|
+
const sigs2 = helpers.clientSign(fetchedDeferred, TestData.copayers[0].xPrivKey_44H_0H_0H);
|
|
8045
|
+
const signed2 = await util.promisify(server.signTx).call(server, {
|
|
8046
|
+
txProposalId: deferredTxp.id,
|
|
8047
|
+
signatures: sigs2
|
|
8048
|
+
});
|
|
8049
|
+
signed2.status.should.equal('accepted');
|
|
8050
|
+
});
|
|
8051
|
+
|
|
8052
|
+
it('should complete full lifecycle for a deferred-nonce txp', async function() {
|
|
8053
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '42');
|
|
8054
|
+
|
|
8055
|
+
const txOpts = {
|
|
8056
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
8057
|
+
feePerKb: 123e2,
|
|
8058
|
+
from: fromAddr,
|
|
8059
|
+
deferNonce: true
|
|
8060
|
+
};
|
|
8061
|
+
const created = await util.promisify(server.createTx).call(server, txOpts);
|
|
8062
|
+
created.isTemporary().should.be.true;
|
|
8063
|
+
created.deferNonce.should.be.true;
|
|
8064
|
+
should.not.exist(created.nonce);
|
|
8065
|
+
|
|
8066
|
+
const publishOpts = helpers.getProposalSignatureOpts(created, TestData.copayers[0].privKey_1H_0);
|
|
8067
|
+
const published = await util.promisify(server.publishTx).call(server, publishOpts);
|
|
8068
|
+
published.status.should.equal('pending');
|
|
8069
|
+
should.exist(published.prePublishRaw);
|
|
8070
|
+
|
|
8071
|
+
const withNonce = await util.promisify(server.prepareTx).call(server, {
|
|
8072
|
+
txProposalId: created.id
|
|
8073
|
+
});
|
|
8074
|
+
withNonce.nonce.should.equal(42);
|
|
8075
|
+
|
|
8076
|
+
const fetched = await util.promisify(server.getTx).call(server, { txProposalId: created.id });
|
|
8077
|
+
const signatures = helpers.clientSign(fetched, TestData.copayers[0].xPrivKey_44H_0H_0H);
|
|
8078
|
+
const signed = await util.promisify(server.signTx).call(server, {
|
|
8079
|
+
txProposalId: created.id,
|
|
8080
|
+
signatures
|
|
8081
|
+
});
|
|
8082
|
+
signed.status.should.equal('accepted');
|
|
8083
|
+
|
|
8084
|
+
helpers.stubBroadcast(signed.txid);
|
|
8085
|
+
const broadcasted = await util.promisify(server.broadcastTx).call(server, {
|
|
8086
|
+
txProposalId: created.id
|
|
8087
|
+
});
|
|
8088
|
+
broadcasted.status.should.equal('broadcasted');
|
|
8089
|
+
should.exist(broadcasted.txid);
|
|
8090
|
+
});
|
|
8091
|
+
|
|
8092
|
+
it('should handle bulk sign scenario (3 deferred txps signed sequentially)', async function() {
|
|
8093
|
+
blockchainExplorer.getTransactionCount = sinon.stub().callsArgWith(1, null, '10');
|
|
8094
|
+
|
|
8095
|
+
const txps = [];
|
|
8096
|
+
for (let i = 0; i < 3; i++) {
|
|
8097
|
+
const txp = await helpers.createAndPublishTx(server, {
|
|
8098
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 1000 * (i + 1) }],
|
|
8099
|
+
feePerKb: 123e2,
|
|
8100
|
+
from: fromAddr,
|
|
8101
|
+
deferNonce: true
|
|
8102
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
8103
|
+
txps.push(txp);
|
|
8104
|
+
}
|
|
8105
|
+
|
|
8106
|
+
const assignedNonces = [];
|
|
8107
|
+
for (let i = 0; i < txps.length; i++) {
|
|
8108
|
+
const withNonce = await util.promisify(server.prepareTx).call(server, {
|
|
8109
|
+
txProposalId: txps[i].id
|
|
8110
|
+
});
|
|
8111
|
+
withNonce.nonce.should.equal(10 + i);
|
|
8112
|
+
assignedNonces.push(withNonce.nonce);
|
|
8113
|
+
}
|
|
8114
|
+
|
|
8115
|
+
assignedNonces.should.deep.equal([10, 11, 12]);
|
|
8116
|
+
});
|
|
8117
|
+
|
|
8118
|
+
it('should return txp with nonce set (extensibility baseline)', async function() {
|
|
8119
|
+
const txp = await helpers.createAndPublishTx(server, {
|
|
8120
|
+
outputs: [{ toAddress: ETH_ADDR, amount: 8000 }],
|
|
8121
|
+
feePerKb: 123e2,
|
|
8122
|
+
from: fromAddr,
|
|
8123
|
+
deferNonce: true
|
|
8124
|
+
}, TestData.copayers[0].privKey_1H_0);
|
|
8125
|
+
|
|
8126
|
+
should.not.exist(txp.nonce);
|
|
8127
|
+
|
|
8128
|
+
const result = await util.promisify(server.prepareTx).call(server, {
|
|
8129
|
+
txProposalId: txp.id
|
|
8130
|
+
});
|
|
8131
|
+
result.nonce.should.be.a('number');
|
|
8132
|
+
result.nonce.should.equal(5);
|
|
8133
|
+
// future: result.gasPrice, result.maxFee would also be set here
|
|
8134
|
+
});
|
|
8135
|
+
});
|
|
8136
|
+
|
|
7804
8137
|
describe('#broadcastTx & #broadcastRawTx', function() {
|
|
7805
8138
|
let server: WalletService;
|
|
7806
8139
|
let wallet: Model.Wallet;
|
package/test/v8.test.ts
CHANGED
|
@@ -247,6 +247,109 @@ describe('V8', () => {
|
|
|
247
247
|
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
+
describe('#getAaveUserAccountData', () => {
|
|
251
|
+
it('should get aave user account data', (done) => {
|
|
252
|
+
const fakeRequest = {
|
|
253
|
+
get: sinon.stub().resolves('{"totalCollateralBase":"1000","totalDebtBase":"500","availableBorrowsBase":"200","currentLiquidationThreshold":"8000","ltv":"7500","healthFactor":"2.0"}'),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const be = new V8({
|
|
257
|
+
chain: 'eth',
|
|
258
|
+
network: 'livenet',
|
|
259
|
+
url: 'http://dummy/',
|
|
260
|
+
apiPrefix: 'dummyPath',
|
|
261
|
+
userAgent: 'testAgent',
|
|
262
|
+
request: fakeRequest,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
be.getAaveUserAccountData({ address: '0x123', version: 'v3' }, (err, accountData) => {
|
|
266
|
+
should.not.exist(err);
|
|
267
|
+
should.exist(accountData);
|
|
268
|
+
accountData.totalCollateralBase.should.equal('1000');
|
|
269
|
+
accountData.totalDebtBase.should.equal('500');
|
|
270
|
+
accountData.availableBorrowsBase.should.equal('200');
|
|
271
|
+
accountData.currentLiquidationThreshold.should.equal('8000');
|
|
272
|
+
accountData.ltv.should.equal('7500');
|
|
273
|
+
accountData.healthFactor.should.equal('2.0');
|
|
274
|
+
return done();
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('#getAaveReserveData', () => {
|
|
280
|
+
it('should get aave reserve data', (done) => {
|
|
281
|
+
const fakeRequest = {
|
|
282
|
+
get: sinon.stub().resolves('{"currentVariableBorrowRate":"35000000000000000000000000"}'),
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const be = new V8({
|
|
286
|
+
chain: 'eth',
|
|
287
|
+
network: 'livenet',
|
|
288
|
+
url: 'http://dummy/',
|
|
289
|
+
apiPrefix: 'dummyPath',
|
|
290
|
+
userAgent: 'testAgent',
|
|
291
|
+
request: fakeRequest,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
be.getAaveReserveData({ asset: '0xabc', version: 'v3' }, (err, reserveData) => {
|
|
295
|
+
should.not.exist(err);
|
|
296
|
+
should.exist(reserveData);
|
|
297
|
+
reserveData.currentVariableBorrowRate.should.equal('35000000000000000000000000');
|
|
298
|
+
return done();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe('#getAaveReserveTokensAddresses', () => {
|
|
304
|
+
it('should get aave reserve tokens addresses', (done) => {
|
|
305
|
+
const fakeRequest = {
|
|
306
|
+
get: sinon.stub().resolves('{"variableDebtTokenAddress":"0xdef456"}'),
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const be = new V8({
|
|
310
|
+
chain: 'eth',
|
|
311
|
+
network: 'livenet',
|
|
312
|
+
url: 'http://dummy/',
|
|
313
|
+
apiPrefix: 'dummyPath',
|
|
314
|
+
userAgent: 'testAgent',
|
|
315
|
+
request: fakeRequest,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
be.getAaveReserveTokensAddresses({ asset: '0xabc', version: 'v3' }, (err, tokensAddresses) => {
|
|
319
|
+
should.not.exist(err);
|
|
320
|
+
should.exist(tokensAddresses);
|
|
321
|
+
tokensAddresses.variableDebtTokenAddress.should.equal('0xdef456');
|
|
322
|
+
return done();
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('#getTokenAllowance', () => {
|
|
328
|
+
it('should get token allowance', (done) => {
|
|
329
|
+
const fakeRequest = {
|
|
330
|
+
get: sinon.stub().resolves('5000000'),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const be = new V8({
|
|
334
|
+
chain: 'eth',
|
|
335
|
+
network: 'livenet',
|
|
336
|
+
url: 'http://dummy/',
|
|
337
|
+
apiPrefix: 'dummyPath',
|
|
338
|
+
userAgent: 'testAgent',
|
|
339
|
+
request: fakeRequest,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
be.getTokenAllowance(
|
|
343
|
+
{ tokenAddress: '0xtoken', ownerAddress: '0xowner', spenderAddress: '0xspender' },
|
|
344
|
+
(err, allowance) => {
|
|
345
|
+
should.not.exist(err);
|
|
346
|
+
should.exist(allowance);
|
|
347
|
+
allowance.should.equal(5000000);
|
|
348
|
+
return done();
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
250
353
|
|
|
251
354
|
describe('#broadcast', () => {
|
|
252
355
|
it('should broadcast a TX', (done) => {
|