@bitpay-labs/bitcore-wallet-service 11.6.5 → 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.
Files changed (55) hide show
  1. package/package.json +22 -11
  2. package/src/externalservices/moonpay.ts +3 -0
  3. package/src/lib/blockchainexplorers/v8.ts +42 -0
  4. package/src/lib/chain/eth/index.ts +3 -2
  5. package/src/lib/expressapp.ts +25 -0
  6. package/src/lib/model/txproposal.ts +8 -0
  7. package/src/lib/routes/aave.ts +48 -0
  8. package/src/lib/routes/tss.ts +2 -2
  9. package/src/lib/server.ts +183 -6
  10. package/src/lib/tss.ts +9 -4
  11. package/test/expressapp.test.ts +170 -0
  12. package/test/integration/server.test.ts +333 -0
  13. package/test/v8.test.ts +103 -0
  14. package/ts_build/package.json +22 -11
  15. package/ts_build/src/externalservices/banxa.d.ts.map +1 -1
  16. package/ts_build/src/externalservices/banxa.js +0 -1
  17. package/ts_build/src/externalservices/banxa.js.map +1 -1
  18. package/ts_build/src/externalservices/changelly.d.ts +2 -2
  19. package/ts_build/src/externalservices/moonpay.d.ts.map +1 -1
  20. package/ts_build/src/externalservices/moonpay.js +6 -0
  21. package/ts_build/src/externalservices/moonpay.js.map +1 -1
  22. package/ts_build/src/lib/blockchainexplorers/v8.d.ts +12 -0
  23. package/ts_build/src/lib/blockchainexplorers/v8.d.ts.map +1 -1
  24. package/ts_build/src/lib/blockchainexplorers/v8.js +39 -0
  25. package/ts_build/src/lib/blockchainexplorers/v8.js.map +1 -1
  26. package/ts_build/src/lib/chain/eth/index.d.ts.map +1 -1
  27. package/ts_build/src/lib/chain/eth/index.js +3 -2
  28. package/ts_build/src/lib/chain/eth/index.js.map +1 -1
  29. package/ts_build/src/lib/expressapp.d.ts.map +1 -1
  30. package/ts_build/src/lib/expressapp.js +23 -0
  31. package/ts_build/src/lib/expressapp.js.map +1 -1
  32. package/ts_build/src/lib/model/txproposal.d.ts +3 -0
  33. package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
  34. package/ts_build/src/lib/model/txproposal.js +6 -0
  35. package/ts_build/src/lib/model/txproposal.js.map +1 -1
  36. package/ts_build/src/lib/routes/aave.d.ts +12 -0
  37. package/ts_build/src/lib/routes/aave.d.ts.map +1 -0
  38. package/ts_build/src/lib/routes/aave.js +47 -0
  39. package/ts_build/src/lib/routes/aave.js.map +1 -0
  40. package/ts_build/src/lib/routes/tss.js +2 -2
  41. package/ts_build/src/lib/routes/tss.js.map +1 -1
  42. package/ts_build/src/lib/server.d.ts +5 -0
  43. package/ts_build/src/lib/server.d.ts.map +1 -1
  44. package/ts_build/src/lib/server.js +131 -6
  45. package/ts_build/src/lib/server.js.map +1 -1
  46. package/ts_build/src/lib/tss.d.ts +1 -0
  47. package/ts_build/src/lib/tss.d.ts.map +1 -1
  48. package/ts_build/src/lib/tss.js +6 -2
  49. package/ts_build/src/lib/tss.js.map +1 -1
  50. package/ts_build/test/expressapp.test.js +162 -0
  51. package/ts_build/test/expressapp.test.js.map +1 -1
  52. package/ts_build/test/integration/server.test.js +281 -0
  53. package/ts_build/test/integration/server.test.js.map +1 -1
  54. package/ts_build/test/v8.test.js +89 -0
  55. package/ts_build/test/v8.test.js.map +1 -1
@@ -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) => {