diff --git a/packages/api/src/pubsub/eth/eth.js b/packages/api/src/pubsub/eth/eth.js index d538716f..322b19f8 100644 --- a/packages/api/src/pubsub/eth/eth.js +++ b/packages/api/src/pubsub/eth/eth.js @@ -15,65 +15,84 @@ // along with Parity. If not, see . const PubsubBase = require('../pubsubBase'); -const { inAddress, inBlockNumber, inHex, inNumber16, inOptions, inFilter } = require('../../format/input'); -const { outAddress, outBlock, outNumber, outTransaction, outSyncing, outReceipt, outLog } = require('../../format/output'); +const { + inAddress, + inBlockNumber, + inHex, + inNumber16, + inOptions, + inFilter +} = require('../../format/input'); +const { + outAddress, + outBlock, + outNumber, + outTransaction, + outSyncing, + outReceipt, + outLog +} = require('../../format/output'); class Eth extends PubsubBase { - constructor (provider) { + constructor(provider) { super(provider); this._api = 'parity'; } - newHeads (callback) { - return this.addListener('eth', 'newHeads', callback, null); + newHeads(callback) { + return this.addListener( + 'eth', + 'newHeads', + (error, data) => { + error ? callback(error) : callback(null, outBlock(data)); + }, + null + ); } - logs (callback) { + syncing(callback) { + return this.addListener( + 'eth', + 'syncing', + (error, data) => { + error ? callback(error) : callback(null, outSyncing(data)); + }, + null + ); + } + + logs(callback) { throw Error('not supported yet'); } // eth API - protocolVersion (callback) { + protocolVersion(callback) { return this.addListener(this._api, 'eth_protocolVersion', callback); } - syncing (callback) { - return this.addListener(this._api, 'eth_syncing', (error, data) => { - error - ? callback(error) - : callback(null, outSyncing(data)); - }); - } - - hashrate (callback) { + hashrate(callback) { return this.addListener(this._api, 'eth_hashrate', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); + error ? callback(error) : callback(null, outNumber(data)); }); } - coinbase (callback) { + coinbase(callback) { return this.addListener(this._api, 'eth_coinbase', (error, data) => { - error - ? callback(error) - : callback(null, outAddress(data)); + error ? callback(error) : callback(null, outAddress(data)); }); } - mining (callback) { + mining(callback) { return this.addListener(this._api, 'eth_mining', callback); } - gasPrice (callback) { + gasPrice(callback) { return this.addListener(this._api, 'eth_gasPrice', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); + error ? callback(error) : callback(null, outNumber(data)); }); } - accounts (callback) { + accounts(callback) { return this.addListener(this._api, 'eth_accounts', (error, accounts) => { error ? callback(error) @@ -81,147 +100,211 @@ class Eth extends PubsubBase { }); } - blockNumber (callback) { + blockNumber(callback) { return this.addListener(this._api, 'eth_blockNumber', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); + error ? callback(error) : callback(null, outNumber(data)); }); } - getBalance (callback, address, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_getBalance', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inAddress(address), inBlockNumber(blockNumber)]); + getBalance(callback, address, blockNumber = 'latest') { + return this.addListener( + this._api, + 'eth_getBalance', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inAddress(address), inBlockNumber(blockNumber)] + ); } - getStorageAt (callback, address, index = 0, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_getStorageAt', callback, [inAddress(address), inNumber16(index), inBlockNumber(blockNumber)]); + getStorageAt(callback, address, index = 0, blockNumber = 'latest') { + return this.addListener(this._api, 'eth_getStorageAt', callback, [ + inAddress(address), + inNumber16(index), + inBlockNumber(blockNumber) + ]); } - getBlockByHash (callback, hash, full = false) { - return this.addListener(this._api, 'eth_getBlockByHash', (error, data) => { - error - ? callback(error) - : callback(null, outBlock(data)); - }, [inHex(hash), full]); + getBlockByHash(callback, hash, full = false) { + return this.addListener( + this._api, + 'eth_getBlockByHash', + (error, data) => { + error ? callback(error) : callback(null, outBlock(data)); + }, + [inHex(hash), full] + ); } - getBlockByNumber (callback, blockNumber = 'latest', full = false) { - return this.addListener(this._api, 'eth_getBlockByNumber', (error, data) => { - error - ? callback(error) - : callback(null, outBlock(data)); - }, [inBlockNumber(blockNumber), full]); + getBlockByNumber(callback, blockNumber = 'latest', full = false) { + return this.addListener( + this._api, + 'eth_getBlockByNumber', + (error, data) => { + error ? callback(error) : callback(null, outBlock(data)); + }, + [inBlockNumber(blockNumber), full] + ); } - getTransactionCount (callback, address, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_getTransactionCount', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inAddress(address), inBlockNumber(blockNumber)]); + getTransactionCount(callback, address, blockNumber = 'latest') { + return this.addListener( + this._api, + 'eth_getTransactionCount', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inAddress(address), inBlockNumber(blockNumber)] + ); } - getBlockTransactionCountByHash (callback, hash) { - return this.addListener(this._api, 'eth_getBlockTransactionCountByHash', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inHex(hash)]); + getBlockTransactionCountByHash(callback, hash) { + return this.addListener( + this._api, + 'eth_getBlockTransactionCountByHash', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inHex(hash)] + ); } - getBlockTransactionCountByNumber (callback, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_getBlockTransactionCountByNumber', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inBlockNumber(blockNumber)]); + getBlockTransactionCountByNumber(callback, blockNumber = 'latest') { + return this.addListener( + this._api, + 'eth_getBlockTransactionCountByNumber', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inBlockNumber(blockNumber)] + ); } - getUncleCountByBlockHash (callback, hash) { - return this.addListener(this._api, 'eth_getUncleCountByBlockHash', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inHex(hash)]); + getUncleCountByBlockHash(callback, hash) { + return this.addListener( + this._api, + 'eth_getUncleCountByBlockHash', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inHex(hash)] + ); } - getUncleCountByBlockNumber (callback, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_getUncleCountByBlockNumber', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inBlockNumber(blockNumber)]); + getUncleCountByBlockNumber(callback, blockNumber = 'latest') { + return this.addListener( + this._api, + 'eth_getUncleCountByBlockNumber', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inBlockNumber(blockNumber)] + ); } - getCode (callback, address, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_getCode', callback, [inAddress(address), inBlockNumber(blockNumber)]); + getCode(callback, address, blockNumber = 'latest') { + return this.addListener(this._api, 'eth_getCode', callback, [ + inAddress(address), + inBlockNumber(blockNumber) + ]); } - call (callback, options, blockNumber = 'latest') { - return this.addListener(this._api, 'eth_call', callback, [inOptions(options), inBlockNumber(blockNumber)]); + call(callback, options, blockNumber = 'latest') { + return this.addListener(this._api, 'eth_call', callback, [ + inOptions(options), + inBlockNumber(blockNumber) + ]); } - estimateGas (callback, options) { - return this.addListener(this._api, 'eth_estimateGas', (error, data) => { - error - ? callback(error) - : callback(null, outNumber(data)); - }, [inOptions(options)]); + estimateGas(callback, options) { + return this.addListener( + this._api, + 'eth_estimateGas', + (error, data) => { + error ? callback(error) : callback(null, outNumber(data)); + }, + [inOptions(options)] + ); } - getTransactionByHash (callback, hash) { - return this.addListener(this._api, 'eth_getTransactionByHash', (error, data) => { - error - ? callback(error) - : callback(null, outTransaction(data)); - }, [inHex(hash)]); + getTransactionByHash(callback, hash) { + return this.addListener( + this._api, + 'eth_getTransactionByHash', + (error, data) => { + error ? callback(error) : callback(null, outTransaction(data)); + }, + [inHex(hash)] + ); } - getTransactionByBlockHashAndIndex (callback, hash, index = 0) { - return this.addListener(this._api, 'eth_getTransactionByBlockHashAndIndex', (error, data) => { - error - ? callback(error) - : callback(null, outTransaction(data)); - }, [inHex(hash), inNumber16(index)]); + getTransactionByBlockHashAndIndex(callback, hash, index = 0) { + return this.addListener( + this._api, + 'eth_getTransactionByBlockHashAndIndex', + (error, data) => { + error ? callback(error) : callback(null, outTransaction(data)); + }, + [inHex(hash), inNumber16(index)] + ); } - getTransactionByBlockNumberAndIndex (callback, blockNumber = 'latest', index = 0) { - return this.addListener(this._api, 'eth_getTransactionByBlockNumberAndIndex', (error, data) => { - error - ? callback(error) - : callback(null, outTransaction(data)); - }, [inBlockNumber(blockNumber), inNumber16(index)]); + getTransactionByBlockNumberAndIndex( + callback, + blockNumber = 'latest', + index = 0 + ) { + return this.addListener( + this._api, + 'eth_getTransactionByBlockNumberAndIndex', + (error, data) => { + error ? callback(error) : callback(null, outTransaction(data)); + }, + [inBlockNumber(blockNumber), inNumber16(index)] + ); } - getTransactionReceipt (callback, txhash) { - return this.addListener(this._api, 'eth_getTransactionReceipt', (error, data) => { - error - ? callback(error) - : callback(null, outReceipt(data)); - }, [inHex(txhash)]); + getTransactionReceipt(callback, txhash) { + return this.addListener( + this._api, + 'eth_getTransactionReceipt', + (error, data) => { + error ? callback(error) : callback(null, outReceipt(data)); + }, + [inHex(txhash)] + ); } - getUncleByBlockHashAndIndex (callback, hash, index = 0) { - return this.addListener(this._api, 'eth_getUncleByBlockHashAndIndex', callback, [inHex(hash), inNumber16(index)]); + getUncleByBlockHashAndIndex(callback, hash, index = 0) { + return this.addListener( + this._api, + 'eth_getUncleByBlockHashAndIndex', + callback, + [inHex(hash), inNumber16(index)] + ); } - getUncleByBlockNumberAndIndex (callback, blockNumber = 'latest', index = 0) { - return this.addListener(this._api, 'eth_getUncleByBlockNumberAndIndex', callback, [inBlockNumber(blockNumber), inNumber16(index)]); + getUncleByBlockNumberAndIndex(callback, blockNumber = 'latest', index = 0) { + return this.addListener( + this._api, + 'eth_getUncleByBlockNumberAndIndex', + callback, + [inBlockNumber(blockNumber), inNumber16(index)] + ); } - getLogs (callback, options) { - return this.addListener(this._api, 'eth_getLogs', (error, logs) => { - error - ? callback(error) - : callback(null, (logs) => logs.map(outLog)); - }, [inFilter(options)]); + getLogs(callback, options) { + return this.addListener( + this._api, + 'eth_getLogs', + (error, logs) => { + error ? callback(error) : callback(null, logs => logs.map(outLog)); + }, + [inFilter(options)] + ); } - getWork (callback) { + getWork(callback) { return this.addListener(this._api, 'eth_getWork', callback); } } diff --git a/packages/api/src/pubsub/pubsub.spec.js b/packages/api/src/pubsub/pubsub.spec.js index 0d0d0d6d..ddd544a5 100644 --- a/packages/api/src/pubsub/pubsub.spec.js +++ b/packages/api/src/pubsub/pubsub.spec.js @@ -30,17 +30,25 @@ describe('pubsub/Pubsub', () => { describe('accountsInfo', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: { - '0x63cf90d3f0410092fc0fca41846f596223979195': { - name: 'name', uuid: 'uuid', meta: '{"data":"data"}' + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: { + '0x63cf90d3f0410092fc0fca41846f596223979195': { + name: 'name', + uuid: 'uuid', + meta: '{"data":"data"}' + } + }, + subscription: 2 } - }, - subscription: 2 + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -48,12 +56,14 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('retrieves the available account info', (done) => { + it('retrieves the available account info', done => { instance.parity.accountsInfo((error, result) => { expect(error).to.be.null; expect(result).to.deep.equal({ '0x63Cf90D3f0410092FC0fca41846f596223979195': { - name: 'name', uuid: 'uuid', meta: { + name: 'name', + uuid: 'uuid', + meta: { data: 'data' } } @@ -76,7 +86,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('Promise gets resolved on success.', (done) => { + it('Promise gets resolved on success.', done => { instance.parity.accountsInfo().then(s => { instance.parity.unsubscribe(s).then(b => { expect(b).to.be.true; @@ -88,15 +98,21 @@ describe('pubsub/Pubsub', () => { describe('chainStatus', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: { - 'blockGap': [0x123, 0x456] - }, - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: { + blockGap: [0x123, 0x456] + }, + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -104,11 +120,11 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('retrieves the chain status', (done) => { + it('retrieves the chain status', done => { instance.parity.chainStatus((error, result) => { expect(error).to.be.null; expect(result).to.deep.equal({ - 'blockGap': [new BigNumber(0x123), new BigNumber(0x456)] + blockGap: [new BigNumber(0x123), new BigNumber(0x456)] }); done(); }); @@ -117,13 +133,19 @@ describe('pubsub/Pubsub', () => { describe('gasFloorTarget', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123456', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123456', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -131,7 +153,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the gasfloor, formatted', (done) => { + it('returns the gasfloor, formatted', done => { instance.parity.gasFloorTarget((error, result) => { expect(error).to.be.null; expect(isBigNumber(result)).to.be.true; @@ -143,13 +165,19 @@ describe('pubsub/Pubsub', () => { describe('transactionsLimit', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: 1024, - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: 1024, + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -157,7 +185,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the tx limit, formatted', (done) => { + it('returns the tx limit, formatted', done => { instance.parity.transactionsLimit((error, result) => { expect(error).to.be.null; expect(isBigNumber(result)).to.be.true; @@ -169,13 +197,19 @@ describe('pubsub/Pubsub', () => { describe('minGasPrice', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123456', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123456', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -183,7 +217,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the min gasprice, formatted', (done) => { + it('returns the min gasprice, formatted', done => { instance.parity.minGasPrice((error, result) => { expect(error).to.be.null; expect(isBigNumber(result)).to.be.true; @@ -195,13 +229,19 @@ describe('pubsub/Pubsub', () => { describe('netPeers', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: { active: 123, connected: 456, max: 789, peers: [] }, - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: { active: 123, connected: 456, max: 789, peers: [] }, + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -209,7 +249,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the peer structure, formatted', (done) => { + it('returns the peer structure, formatted', done => { instance.parity.netPeers((error, peers) => { expect(error).to.be.null; expect(peers.active.eq(123)).to.be.true; @@ -222,13 +262,19 @@ describe('pubsub/Pubsub', () => { describe('netPort', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: 33030, - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: 33030, + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -236,7 +282,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the connected port, formatted', (done) => { + it('returns the connected port, formatted', done => { instance.parity.netPort((error, count) => { expect(error).to.be.null; expect(isBigNumber(count)).to.be.true; @@ -248,13 +294,19 @@ describe('pubsub/Pubsub', () => { describe('accounts', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: [address.toLowerCase()], - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: [address.toLowerCase()], + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -262,7 +314,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns a list of accounts, formatted', (done) => { + it('returns a list of accounts, formatted', done => { instance.eth.accounts((error, accounts) => { expect(error).to.be.null; expect(accounts).to.deep.equal([address]); @@ -273,13 +325,19 @@ describe('pubsub/Pubsub', () => { describe('newHeads', () => { beforeEach(() => { - scope = mockWs([{ method: 'eth_subscribe', reply: 2, subscription: { - method: 'eth_subscription', - params: { - result: '0x123456', - subscription: 2 + scope = mockWs([ + { + method: 'eth_subscribe', + reply: 2, + subscription: { + method: 'eth_subscription', + params: { + result: '0x123456', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -287,10 +345,10 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns newHeads for eth_subscribe', (done) => { - instance.eth.newHeads((error, blockNumber) => { + it('returns newHeads for eth_subscribe', done => { + instance.eth.newHeads((error, block) => { expect(error).to.be.null; - expect(blockNumber).to.equal('0x123456'); + expect(block).not.to.be.null; done(); }); }); @@ -298,13 +356,19 @@ describe('pubsub/Pubsub', () => { describe('blockNumber', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123456', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123456', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -312,7 +376,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the current blockNumber, formatted', (done) => { + it('returns the current blockNumber, formatted', done => { instance.eth.blockNumber((error, blockNumber) => { expect(error).to.be.null; expect(isBigNumber(blockNumber)).to.be.true; @@ -324,13 +388,19 @@ describe('pubsub/Pubsub', () => { describe('call', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: [], - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: [], + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -338,32 +408,51 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('formats the input options & blockNumber', (done) => { - instance.eth.call((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_call', [{ data: '0x12345678' }, 'earliest']]); - done(); - }, { data: '12345678' }, 'earliest'); - }); - - it('provides a latest blockNumber when not specified', (done) => { - instance.eth.call((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_call', [{ data: '0x12345678' }, 'latest']]); - done(); - }, { data: '12345678' }); + it('formats the input options & blockNumber', done => { + instance.eth.call( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_call', + [{ data: '0x12345678' }, 'earliest'] + ]); + done(); + }, + { data: '12345678' }, + 'earliest' + ); + }); + + it('provides a latest blockNumber when not specified', done => { + instance.eth.call( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_call', + [{ data: '0x12345678' }, 'latest'] + ]); + done(); + }, + { data: '12345678' } + ); }); }); describe('coinbase', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: address.toLowerCase(), - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: address.toLowerCase(), + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -371,7 +460,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the coinbase, formatted', (done) => { + it('returns the coinbase, formatted', done => { instance.eth.coinbase((error, account) => { expect(error).to.be.null; expect(account).to.deep.equal(address); @@ -382,13 +471,19 @@ describe('pubsub/Pubsub', () => { describe('estimateGas', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -396,15 +491,21 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('converts the options correctly', (done) => { - instance.eth.estimateGas((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_estimateGas', [{ gas: '0x5208' }]]); - done(); - }, { gas: 21000 }); + it('converts the options correctly', done => { + instance.eth.estimateGas( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_estimateGas', + [{ gas: '0x5208' }] + ]); + done(); + }, + { gas: 21000 } + ); }); - it('returns the gas used, formatted', (done) => { + it('returns the gas used, formatted', done => { instance.eth.estimateGas((error, gas) => { expect(error).to.be.null; expect(isBigNumber(gas)).to.be.true; @@ -416,13 +517,19 @@ describe('pubsub/Pubsub', () => { describe('gasPrice', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -430,7 +537,7 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('returns the gas price, formatted', (done) => { + it('returns the gas price, formatted', done => { instance.eth.gasPrice((error, price) => { expect(error).to.be.null; expect(isBigNumber(price)).to.be.true; @@ -442,13 +549,19 @@ describe('pubsub/Pubsub', () => { describe('getBalance', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -456,23 +569,33 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('passes in the address (default blockNumber)', (done) => { - instance.eth.getBalance((error) => { + it('passes in the address (default blockNumber)', done => { + instance.eth.getBalance(error => { expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBalance', [address.toLowerCase(), 'latest']]); + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBalance', + [address.toLowerCase(), 'latest'] + ]); done(); }, address); }); - it('passes in the address & blockNumber', (done) => { - instance.eth.getBalance((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBalance', [address.toLowerCase(), '0x456']]); - done(); - }, address, 0x456); + it('passes in the address & blockNumber', done => { + instance.eth.getBalance( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBalance', + [address.toLowerCase(), '0x456'] + ]); + done(); + }, + address, + 0x456 + ); }); - it('returns the balance', (done) => { + it('returns the balance', done => { instance.eth.getBalance((error, balance) => { expect(error).to.be.null; expect(isBigNumber(balance)).to.be.true; @@ -484,13 +607,19 @@ describe('pubsub/Pubsub', () => { describe('getBlockByHash', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: { miner: address.toLowerCase() }, - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: { miner: address.toLowerCase() }, + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -498,23 +627,33 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('formats the input hash as a hash, default full', (done) => { - instance.eth.getBlockByHash((error) => { + it('formats the input hash as a hash, default full', done => { + instance.eth.getBlockByHash(error => { expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByHash', ['0x1234', false]]); + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBlockByHash', + ['0x1234', false] + ]); done(); }, '1234'); }); - it('formats the input hash as a hash, full true', (done) => { - instance.eth.getBlockByHash((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByHash', ['0x1234', true]]); - done(); - }, '1234', true); + it('formats the input hash as a hash, full true', done => { + instance.eth.getBlockByHash( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBlockByHash', + ['0x1234', true] + ]); + done(); + }, + '1234', + true + ); }); - it('formats the output into block', (done) => { + it('formats the output into block', done => { instance.eth.getBlockByHash((error, block) => { expect(error).to.be.null; expect(block.miner).to.equal(address); @@ -525,13 +664,19 @@ describe('pubsub/Pubsub', () => { describe('getBlockByNumber', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: { miner: address.toLowerCase() }, - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: { miner: address.toLowerCase() }, + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -539,31 +684,44 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('assumes blockNumber latest & full false', (done) => { - instance.eth.getBlockByNumber((error) => { + it('assumes blockNumber latest & full false', done => { + instance.eth.getBlockByNumber(error => { expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByNumber', ['latest', false]]); + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBlockByNumber', + ['latest', false] + ]); done(); }); }); - it('uses input blockNumber & full false', (done) => { - instance.eth.getBlockByNumber((error) => { + it('uses input blockNumber & full false', done => { + instance.eth.getBlockByNumber(error => { expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByNumber', ['0x1234', false]]); + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBlockByNumber', + ['0x1234', false] + ]); done(); }, '0x1234'); }); - it('formats the input blockNumber, full true', (done) => { - instance.eth.getBlockByNumber((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByNumber', ['0x1234', true]]); - done(); - }, 0x1234, true); + it('formats the input blockNumber, full true', done => { + instance.eth.getBlockByNumber( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getBlockByNumber', + ['0x1234', true] + ]); + done(); + }, + 0x1234, + true + ); }); - it('formats the output into block', (done) => { + it('formats the output into block', done => { instance.eth.getBlockByNumber((error, block) => { expect(error).to.be.null; expect(block.miner).to.equal(address); @@ -574,13 +732,19 @@ describe('pubsub/Pubsub', () => { describe('getTransactionCount', () => { beforeEach(() => { - scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: { - method: 'parity_subscription', - params: { - result: '0x123', - subscription: 2 + scope = mockWs([ + { + method: 'parity_subscribe', + reply: 2, + subscription: { + method: 'parity_subscription', + params: { + result: '0x123', + subscription: 2 + } + } } - } }]); + ]); instance = new Pubsub(new WsSecure(TEST_WS_URL)); }); @@ -588,29 +752,43 @@ describe('pubsub/Pubsub', () => { scope.stop(); }); - it('passes in the address (default blockNumber)', (done) => { - instance.eth.getTransactionCount((error) => { + it('passes in the address (default blockNumber)', done => { + instance.eth.getTransactionCount(error => { expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getTransactionCount', [address.toLowerCase(), 'latest']]); + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getTransactionCount', + [address.toLowerCase(), 'latest'] + ]); done(); }, address); }); - it('passes in the address & blockNumber', (done) => { - instance.eth.getTransactionCount((error) => { - expect(error).to.be.null; - expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getTransactionCount', [address.toLowerCase(), '0x456']]); - done(); - }, address, 0x456); - }); - - it('returns the count, formatted', (done) => { - instance.eth.getTransactionCount((error, count) => { - expect(error).to.be.null; - expect(isBigNumber(count)).to.be.true; - expect(count.toString(16)).to.equal('123'); - done(); - }, address, 0x456); + it('passes in the address & blockNumber', done => { + instance.eth.getTransactionCount( + error => { + expect(error).to.be.null; + expect(scope.body.parity_subscribe.params).to.deep.equal([ + 'eth_getTransactionCount', + [address.toLowerCase(), '0x456'] + ]); + done(); + }, + address, + 0x456 + ); + }); + + it('returns the count, formatted', done => { + instance.eth.getTransactionCount( + (error, count) => { + expect(error).to.be.null; + expect(isBigNumber(count)).to.be.true; + expect(count.toString(16)).to.equal('123'); + done(); + }, + address, + 0x456 + ); }); }); }); diff --git a/packages/api/src/transport/ws/ws.js b/packages/api/src/transport/ws/ws.js index 26091f08..08cde330 100644 --- a/packages/api/src/transport/ws/ws.js +++ b/packages/api/src/transport/ws/ws.js @@ -212,11 +212,11 @@ class Ws extends JsonRpcBase { } _extract (result) { - const { result: res, id, method, params } = result; + const { result: res, error, id, method, params } = result; const msg = this._messages[id]; // initial pubsub ACK - if (id && msg.subscription) { + if (id && msg.subscription && !error) { // save subscription to map subId -> messageId this._subscriptions[msg.subscription] = this._subscriptions[msg.subscription] || {}; this._subscriptions[msg.subscription][res] = id; diff --git a/packages/light.js/example/src/provider.js b/packages/light.js/example/src/provider.js index 58cc4564..3e5ff4d3 100644 --- a/packages/light.js/example/src/provider.js +++ b/packages/light.js/example/src/provider.js @@ -7,6 +7,9 @@ import Api from '@parity/api'; export const currentProvider = window.web3 && window.web3.currentProvider; export const localProvider = new Api.Provider.Ws('ws://127.0.0.1:8546'); +export const infuraProvider = new Api.Provider.Ws( + 'wss://mainnet.infura.io/_ws' +); const provider = currentProvider || localProvider; diff --git a/packages/light.js/src/api.ts b/packages/light.js/src/api.ts index 429a2d3d..f0d7b7c6 100644 --- a/packages/light.js/src/api.ts +++ b/packages/light.js/src/api.ts @@ -25,11 +25,6 @@ export const createApiFromProvider = memoizee( */ export const setApi = (newApi: any) => { api = newApi; - if (!api.isPubSub) { - console.warn( - `Current provider does not support pubsub. @parity/light.js will poll every second to listen to changes.` - ); - } }; /** diff --git a/packages/light.js/src/frequency/accounts.ts b/packages/light.js/src/frequency/accounts.ts index e92e15a2..7a0ef515 100644 --- a/packages/light.js/src/frequency/accounts.ts +++ b/packages/light.js/src/frequency/accounts.ts @@ -12,7 +12,11 @@ import createPubsubObservable from './utils/createPubsubObservable'; * @param options - Options to pass to {@link FrequencyObservable}. */ export function onAccountsChanged$ (options?: FrequencyObservableOptions) { - return createPubsubObservable('eth_accounts', options); + return createPubsubObservable( + 'eth_accounts', + 'eth_accounts', + options + ); } /** @@ -21,5 +25,9 @@ export function onAccountsChanged$ (options?: FrequencyObservableOptions) { * @param options - Options to pass to {@link FrequencyObservable}. */ export function onAccountsInfoChanged$ (options?: FrequencyObservableOptions) { - return createPubsubObservable('parity_accountsInfo', options); + return createPubsubObservable( + 'parity_accountsInfo', + 'parity_accountsInfo', + options + ); } diff --git a/packages/light.js/src/frequency/blocks.ts b/packages/light.js/src/frequency/blocks.ts index 11b2fe53..8481f0c7 100644 --- a/packages/light.js/src/frequency/blocks.ts +++ b/packages/light.js/src/frequency/blocks.ts @@ -3,14 +3,14 @@ // // SPDX-License-Identifier: MIT -import BigNumber from 'bignumber.js'; -import { filter, map, withLatestFrom } from 'rxjs/operators'; +import { filter, map, startWith, withLatestFrom } from 'rxjs/operators'; import * as memoizee from 'memoizee'; import { Observable } from 'rxjs'; +import { Block, FrequencyObservableOptions } from '../types'; import { createApiFromProvider, getApi } from '../api'; import createPubsubObservable from './utils/createPubsubObservable'; -import { FrequencyObservableOptions } from '../types'; +import { distinctValues } from '../utils/operators'; import { onSyncingChanged$ } from './health'; /** @@ -20,11 +20,16 @@ import { onSyncingChanged$ } from './health'; */ const onEveryBlockWithApi$ = memoizee( (api: any, options?: FrequencyObservableOptions) => - createPubsubObservable('eth_blockNumber', options).pipe( - withLatestFrom(onSyncingChanged$(options)), + createPubsubObservable( + 'eth_newHeads', + 'eth_getBlockByNumber', + options + ).pipe( + withLatestFrom(onSyncingChanged$(options).pipe(startWith(false))), filter(([_, isSyncing]) => isSyncing === false), - map(([blockNumber]) => blockNumber) - ) as Observable, + map(([blockNumber]) => blockNumber), + distinctValues() + ) as Observable, { length: 1 } // Only memoize by api ); diff --git a/packages/light.js/src/frequency/health.ts b/packages/light.js/src/frequency/health.ts index 4c6d424e..1e2b0613 100644 --- a/packages/light.js/src/frequency/health.ts +++ b/packages/light.js/src/frequency/health.ts @@ -17,5 +17,9 @@ import { FrequencyObservableOptions } from '../types'; * @param options - Options to pass to {@link FrequencyObservable}. */ export function onSyncingChanged$ (options?: FrequencyObservableOptions) { - return createPubsubObservable('eth_syncing', options); + return createPubsubObservable( + 'eth_syncing', + 'eth_syncing', + options + ); } diff --git a/packages/light.js/src/frequency/utils/createPubsubObservable.spec.ts b/packages/light.js/src/frequency/utils/createPubsubObservable.spec.ts index 394fcefd..e3f7059a 100644 --- a/packages/light.js/src/frequency/utils/createPubsubObservable.spec.ts +++ b/packages/light.js/src/frequency/utils/createPubsubObservable.spec.ts @@ -10,37 +10,49 @@ import { setApi } from '../../api'; it('should return an Observable', () => { setApi(resolveApi()); - expect(isObservable(createPubsubObservable('fake_method'))).toBe(true); + expect( + isObservable(createPubsubObservable('eth_blockNumber', 'eth_blockNumber')) + ).toBe(true); }); it('should fire an event when pubsub publishes', done => { setApi(resolveApi()); - createPubsubObservable('fake_method').subscribe(data => { - expect(data).toBe('foo'); - done(); - }); + createPubsubObservable('eth_blockNumber', 'eth_blockNumber').subscribe( + data => { + expect(data).toBe('foo'); + done(); + } + ); }); it('should fire an error when pubsub errors', done => { setApi(rejectApi()); - createPubsubObservable('fake_method').subscribe(undefined, err => { - expect(err).toEqual(new Error('bar')); - done(); - }); + createPubsubObservable('eth_blockNumber', 'eth_blockNumber').subscribe( + undefined, + err => { + expect(err).toEqual(new Error('bar')); + done(); + } + ); }); it('should fire an event when polling pubsub publishes', done => { setApi(resolveApi('foo', false)); - createPubsubObservable('fake_method').subscribe(data => { - expect(data).toBe('foo'); - done(); - }); + createPubsubObservable('eth_blockNumber', 'eth_blockNumber').subscribe( + data => { + expect(data).toBe('foo'); + done(); + } + ); }); it('should fire an error when polling pubsub errors', done => { setApi(rejectApi(new Error('bar'), false)); - createPubsubObservable('fake_method').subscribe(undefined, err => { - expect(err).toEqual(new Error('bar')); - done(); - }); + createPubsubObservable('eth_blockNumber', 'eth_blockNumber').subscribe( + undefined, + err => { + expect(err).toEqual(new Error('bar')); + done(); + } + ); }); diff --git a/packages/light.js/src/frequency/utils/createPubsubObservable.ts b/packages/light.js/src/frequency/utils/createPubsubObservable.ts index 9534ace3..be454ba4 100644 --- a/packages/light.js/src/frequency/utils/createPubsubObservable.ts +++ b/packages/light.js/src/frequency/utils/createPubsubObservable.ts @@ -4,22 +4,45 @@ // SPDX-License-Identifier: MIT import * as debug from 'debug'; +import { exhaustMap } from 'rxjs/operators'; import { FrequencyObservableOptions } from '../../types'; import * as memoizee from 'memoizee'; import { Observable, Observer, timer } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; import { createApiFromProvider, getApi } from '../../api'; import { distinctReplayRefCount } from '../../utils/operators/distinctReplayRefCount'; +const POLL_INTERVAL = 1000; + +/** + * Create a polling function, calls the `fallback` JSONRPC on each second, or + * on previous call's result, whichever comes last. + * + * @ignore + */ +function createPoll ( + fallback: string, + api: any, + pollInterval = POLL_INTERVAL +) { + const [fallbackNamespace, fallbackMethod] = fallback.split('_'); + + return timer(0, pollInterval).pipe( + exhaustMap(() => api[fallbackNamespace][fallbackMethod]()) + ) as Observable; +} + /** * Given an api, returns an Observable that emits on each pubsub event. * Pure function version of {@link createPubsubObservable}. * * @ignore + * @param pubsub - The pubsub method to subscribe to. + * @param fallback - If pubsub doesn't work, poll this method every + * POLL_INTERVAL ms. */ const createPubsubObservableWithApi = memoizee( - (pubsub: string, api: any) => { + (pubsub: string, fallback: string, api: any) => { const [namespace, method] = pubsub.split('_'); // There's a chance the provider doesn't support pubsub, for example @@ -29,12 +52,10 @@ const createPubsubObservableWithApi = memoizee( debug('@parity/light.js:api')( `Pubsub not available for ${ api.provider ? api.provider.constructor.name : 'current Api' - } provider, polling "${pubsub}" every second.` + } provider, polling "${fallback}" every ${POLL_INTERVAL}ms.` ); - return timer(0, 1000).pipe( - switchMap(() => api[namespace][method]()) - ) as Observable; + return createPoll(fallback, api); } return Observable.create((observer: Observer) => { @@ -47,10 +68,25 @@ const createPubsubObservableWithApi = memoizee( observer.next(result); } } - ); + ).catch(() => { + // If we get an error during subscription, then default to fallback. + // TODO Should this be done on @parity/api? + debug('@parity/light.js:api')( + `Pubsub not available for method "${pubsub}", polling "${fallback}" every ${POLL_INTERVAL}ms` + ); + + createPoll(fallback, api).subscribe( + e => observer.next(e), + e => observer.error(e), + () => observer.complete() + ); + }); + return () => subscription.then((subscriptionId: string) => - api.pubsub.unsubscribe(subscriptionId) + subscriptionId + ? api.pubsub.unsubscribe(subscriptionId) + : Promise.resolve() ); }).pipe(distinctReplayRefCount()) as Observable; } @@ -64,11 +100,12 @@ const createPubsubObservableWithApi = memoizee( */ const createPubsubObservable = ( pubsub: string, + fallback: string, { provider }: FrequencyObservableOptions = {} ) => { const api = provider ? createApiFromProvider(provider) : getApi(); - return createPubsubObservableWithApi(pubsub, api); + return createPubsubObservableWithApi(pubsub, fallback, api); }; export default createPubsubObservable; diff --git a/packages/light.js/src/rpc/eth.ts b/packages/light.js/src/rpc/eth.ts index 6e6205ae..9e982f4f 100644 --- a/packages/light.js/src/rpc/eth.ts +++ b/packages/light.js/src/rpc/eth.ts @@ -7,7 +7,7 @@ import BigNumber from 'bignumber.js'; import { of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { Address, RpcObservableOptions } from '../types'; +import { Address, Block, RpcObservableOptions } from '../types'; import createRpc$ from './utils/createRpc'; import frequency from '../frequency'; import { isNullOrLoading, RPC_LOADING } from '../utils/isLoading'; @@ -66,9 +66,10 @@ export function defaultAccount$ (options?: RpcObservableOptions) { * @return {Observable} - An Observable containing the block height. */ export function blockNumber$ (options?: RpcObservableOptions) { - return createRpc$({ + return createRpc$({ frequency: [frequency.onEveryBlock$], - name: 'blockNumber$' + name: 'blockNumber$', + pipes: () => [map(block => block.number)] })(options)(); } @@ -81,11 +82,10 @@ export function myBalance$ (options?: RpcObservableOptions) { dependsOn: defaultAccount$, name: 'myBalance$', pipes: () => [ - switchMap( - defaultAccount => - isNullOrLoading(defaultAccount) - ? of(RPC_LOADING) - : balanceOf$(defaultAccount) + switchMap(defaultAccount => + isNullOrLoading(defaultAccount) + ? of(RPC_LOADING) + : balanceOf$(defaultAccount) ) ] })(options)(); diff --git a/packages/light.js/src/types.ts b/packages/light.js/src/types.ts index 9ed40090..b790a581 100644 --- a/packages/light.js/src/types.ts +++ b/packages/light.js/src/types.ts @@ -26,6 +26,11 @@ export type Address = string; // TODO This should be on @parity/api export type ApiValue = any; +// TODO This should be on @parity/api +export type Block = { + number: BigNumber; +}; + export interface Metadata { calledWithArgs?: { [key: string]: ReplaySubject; diff --git a/packages/light.js/src/utils/operators/distinctValues.spec.ts b/packages/light.js/src/utils/operators/distinctValues.spec.ts index cf1c6416..6ac63171 100644 --- a/packages/light.js/src/utils/operators/distinctValues.spec.ts +++ b/packages/light.js/src/utils/operators/distinctValues.spec.ts @@ -52,3 +52,4 @@ testValue(2, 'number'); testValue('foo', 'string'); testValue({ foo: 'bar' }, 'object'); testValue(new BigNumber(2), 'BigNumber'); +testValue({ number: new BigNumber(2) }, 'Block'); diff --git a/packages/light.js/src/utils/operators/distinctValues.ts b/packages/light.js/src/utils/operators/distinctValues.ts index f666293b..c909c2da 100644 --- a/packages/light.js/src/utils/operators/distinctValues.ts +++ b/packages/light.js/src/utils/operators/distinctValues.ts @@ -7,6 +7,8 @@ import BigNumber from 'bignumber.js'; import { distinctUntilChanged } from 'rxjs/operators'; import { isObject } from '@parity/api/lib/util/types'; +import { Block } from '../../types'; + /** * An intelligent distinctUntilChanged(). * @@ -14,11 +16,28 @@ import { isObject } from '@parity/api/lib/util/types'; */ export const distinctValues = () => distinctUntilChanged((x, y) => { + // If T == Block + if ( + x && + y && + ((x as unknown) as Block).number && + ((y as unknown) as Block).number + ) { + return ((x as unknown) as Block).number.eq( + ((y as unknown) as Block).number + ); + } + + // If T == BigNumber if (BigNumber.isBigNumber(x) && BigNumber.isBigNumber(y)) { - return ((x as any) as BigNumber).eq((y as any) as BigNumber); + return ((x as unknown) as BigNumber).eq((y as unknown) as BigNumber); } + + // If T == object if (isObject(x) && isObject(y)) { return JSON.stringify(x) === JSON.stringify(y); // TODO Do a deep equal instead } + + // Other cases return x === y; }); diff --git a/packages/light.js/src/utils/testHelpers/mockApi.ts b/packages/light.js/src/utils/testHelpers/mockApi.ts index 430c788b..79583d36 100644 --- a/packages/light.js/src/utils/testHelpers/mockApi.ts +++ b/packages/light.js/src/utils/testHelpers/mockApi.ts @@ -22,7 +22,7 @@ export class MockProvider extends EventEmitter { // List of JSONRPCs we want to mock const listOfMockRps: { [index: string]: string[] } = { - eth: ['accounts', 'blockNumber', 'getBalance', 'syncing'], + eth: ['accounts', 'blockNumber', 'getBalance', 'newHeads', 'syncing'], fake: ['method'], net: ['peerCount'], parity: ['accountsInfo', 'chain', 'postTransaction']