diff --git a/.gitignore b/.gitignore index a427781..f6f608c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # .DS_Store *.lock +*.tsbuldinfo .vscode/ # package.json diff --git a/README.md b/README.md index fb893ad..f2df9e6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ It's a mono-repo library, not yet published to npm. 3. cleanup and build ```bash - yarn install && yarn bootstrap && yarn dist + yarn global add lerna && yarn install && yarn bootstrap && yarn dist ``` diff --git a/examples/testGanache.js b/examples/testGanache.js index d3184ed..cbb9fbc 100644 --- a/examples/testGanache.js +++ b/examples/testGanache.js @@ -1,10 +1,13 @@ const { Harmony } = require('@harmony/core'); const ganache = require('ganache-cli'); +const WebSocket = require('ws'); -var port = 18545; +const port = 18545; const url = `http://localhost:${port}`; +const wsUrl = `ws://localhost:${port}`; + const mne = 'food response winner warfare indicate visual hundred toilet jealous okay relief tornado'; @@ -19,6 +22,8 @@ console.log('-------------------------------------'); const harmony = new Harmony(url, 1); +const wsHarmony = new Harmony(wsUrl, 1); + async function createAndEncrypt(words, index, password) { for (let i = 0; i < index; i++) { const newAcc = harmony.wallet.addByMnemonic(words, i); @@ -51,7 +56,7 @@ async function main() { }); console.log('--- testing: hmy_getBalance'); console.log('-------------------------------------'); - console.log({ balance: harmony.utils.hexToNumber(latestBalance) }); + console.log({ balance: harmony.utils.hexToNumber(latestBalance.result) }); console.log('-------------------------------------'); const nonce = await harmony.blockchain.getTransactionCount({ @@ -60,10 +65,13 @@ async function main() { }); console.log('--- testing: hmy_getTransactionCount'); console.log('-------------------------------------'); - console.log({ nonce: Number.parseInt(harmony.utils.hexToNumber(nonce), 10) }); + console.log({ + nonce: Number.parseInt(harmony.utils.hexToNumber(nonce.result), 10), + }); console.log('-------------------------------------'); const balanceOfAccount = await harmony.wallet.signer.getBalance(); + console.log('--- testing: Account.getBalance'); console.log('-------------------------------------'); console.log(balanceOfAccount); @@ -109,63 +117,63 @@ async function main() { }); console.log('--- testing: hmy_getBlockByNumber'); console.log('-------------------------------------'); - console.log({ latestBlockHash: latestBlock.hash }); + console.log({ latestBlockHash: latestBlock.result.hash }); console.log('-------------------------------------'); const sameLatestBlock = await harmony.blockchain.getBlockByHash({ - blockHash: latestBlock.hash, + blockHash: latestBlock.result.hash, }); console.log('--- testing: hmy_getBlockByHash'); console.log('-------------------------------------'); - console.log({ sameLatestBlockNumber: sameLatestBlock.number }); + console.log({ sameLatestBlockNumber: sameLatestBlock.result.number }); console.log('-------------------------------------'); const blockTransactionCount = await harmony.blockchain.getBlockTransactionCountByHash( { - blockHash: latestBlock.hash, + blockHash: latestBlock.result.hash, }, ); console.log('--- testing: hmy_getBlockTransactionCountByHash'); console.log('-------------------------------------'); - console.log(blockTransactionCount); + console.log(blockTransactionCount.result); console.log('-------------------------------------'); const sameBlockTransactionCount = await harmony.blockchain.getBlockTransactionCountByNumber( { - blockNumber: latestBlock.number, + blockNumber: latestBlock.result.number, }, ); console.log('--- testing: hmy_getBlockTransactionCountByNumber'); console.log('-------------------------------------'); - console.log(sameBlockTransactionCount); + console.log(sameBlockTransactionCount.result); console.log('-------------------------------------'); const transaction = await harmony.blockchain.getTransactionByBlockHashAndIndex( { - blockHash: latestBlock.hash, + blockHash: latestBlock.result.hash, index: '0x0', }, ); console.log('--- testing: hmy_getTransactionByBlockHashAndIndex'); console.log('-------------------------------------'); - console.log(transaction); + console.log(transaction.result); console.log('-------------------------------------'); const sameTransaction = await harmony.blockchain.getTransactionByBlockNumberAndIndex( { - blockNumber: latestBlock.number, + blockNumber: latestBlock.result.number, index: '0x0', }, ); console.log('--- testing: hmy_getTransactionByBlockNumberAndIndex'); console.log('-------------------------------------'); - console.log({ gas: sameTransaction.gas }); + console.log({ gas: sameTransaction.result.gas }); console.log('-------------------------------------'); const sameTransaction2 = await harmony.blockchain.getTransactionByHash({ - txnHash: transaction.hash, + txnHash: transaction.result.hash, }); - const { gas, gasPrice, value } = sameTransaction2; + const { gas, gasPrice, value } = sameTransaction2.result; const valueBN = harmony.utils.hexToBN(value); const gasBN = harmony.utils.hexToBN(gas); const gasPriceBN = harmony.utils.hexToBN(gasPrice); @@ -191,5 +199,7 @@ async function main() { } server.listen(port, function(err, blockchain) { + // console.log(blockchain); main(); + // console.log(wsHarmony.provider.connected); }); diff --git a/examples/testNode.js b/examples/testNode.js index 080c838..eee8de4 100644 --- a/examples/testNode.js +++ b/examples/testNode.js @@ -33,83 +33,85 @@ async function main() { }); console.log('--- testing: hmy_getBlockNumber'); console.log('-------------------------------------'); - console.log(latestBlock); + console.log(latestBlock.result); console.log('-------------------------------------'); const sameLatestBlock = await harmony.blockchain.getBlockByHash({ - blockHash: latestBlock.hash, + blockHash: latestBlock.result.hash, }); console.log('--- testing: hmy_getBlockByHash'); console.log('-------------------------------------'); - console.log(sameLatestBlock); + console.log(sameLatestBlock.result); console.log('-------------------------------------'); const blockTransactionCount = await harmony.blockchain.getBlockTransactionCountByHash( { - blockHash: latestBlock.hash, + blockHash: latestBlock.result.hash, }, ); console.log('--- testing: hmy_getBlockTransactionCountByHash'); console.log('-------------------------------------'); - console.log(blockTransactionCount); + console.log(blockTransactionCount.result); console.log('-------------------------------------'); const sameBlockTransactionCount = await harmony.blockchain.getBlockTransactionCountByNumber( { - blockNumber: latestBlock.number, + blockNumber: latestBlock.result.number, }, ); console.log('--- testing: hmy_getBlockTransactionCountByNumber'); console.log('-------------------------------------'); - console.log(sameBlockTransactionCount); + console.log(sameBlockTransactionCount.result); console.log('-------------------------------------'); const transaction = await harmony.blockchain.getTransactionByBlockHashAndIndex( { - blockHash: latestBlock.hash, + blockHash: latestBlock.result.hash, index: '0x0', }, ); console.log('--- testing: hmy_getTransactionByBlockHashAndIndex'); console.log('-------------------------------------'); - console.log(transaction); + console.log(transaction.result); console.log('-------------------------------------'); const sameTransaction = await harmony.blockchain.getTransactionByBlockNumberAndIndex( { - blockNumber: latestBlock.number, + blockNumber: latestBlock.result.number, index: '0x0', }, ); console.log('--- testing: hmy_getTransactionByBlockNumberAndIndex'); console.log('-------------------------------------'); - console.log(sameTransaction); + console.log(sameTransaction.result); console.log('-------------------------------------'); const sameTransaction2 = await harmony.blockchain.getTransactionByHash({ - txnHash: transaction.hash, + txnHash: transaction.result.hash, }); console.log('--- testing: hmy_getTransactionByHash'); console.log('-------------------------------------'); - console.log(sameTransaction2); + console.log(sameTransaction2.result); console.log('-------------------------------------'); const latestBalance = await harmony.blockchain.getBalance({ address: acc.address, - blockNumber: latestBlock.number, + blockNumber: latestBlock.result.number, }); console.log('--- testing: hmy_getBalance'); console.log('-------------------------------------'); - console.log({ balance: harmony.utils.hexToNumber(latestBalance) }); + console.log({ balance: harmony.utils.hexToNumber(latestBalance.result) }); console.log('-------------------------------------'); const nonce = await harmony.blockchain.getTransactionCount({ address: acc.address, - blockNumber: latestBlock.number, + blockNumber: latestBlock.result.number, }); console.log('--- testing: hmy_getTransactionCount'); console.log('-------------------------------------'); - console.log({ nonce: Number.parseInt(harmony.utils.hexToNumber(nonce), 10) }); + console.log({ + nonce: Number.parseInt(harmony.utils.hexToNumber(nonce.result), 10), + }); console.log('-------------------------------------'); const balanceOfAccount = await acc.getBalance(); diff --git a/package.json b/package.json index 7e90f09..8e4e52b 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@types/uuid": "^3.4.4", "@types/valid-url": "^1.0.2", "@types/webpack": "^4.4.17", + "@types/websocket": "^0.0.40", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^23.4.2", "babel-loader": "^8.0.0-beta.0", @@ -81,6 +82,7 @@ "jest-json-schema": "^2.0.1", "jest-watch-typeahead": "^0.2.0", "lerna": "^3.4.0", + "mitt": "^1.1.3", "mkdirp": "^0.5.1", "prettier": "^1.14.3", "pretty-quick": "^1.8.0", @@ -105,7 +107,8 @@ "uglifyjs-webpack-plugin": "^2.1.2", "webpack": "^4.20.2", "webpack-command": "^0.4.1", - "webpack-node-externals": "^1.7.2" + "webpack-node-externals": "^1.7.2", + "websocket": "^1.0.28" }, "dependencies": {} } diff --git a/packages/harmony-account/src/account.ts b/packages/harmony-account/src/account.ts index d251119..1ba0bae 100644 --- a/packages/harmony-account/src/account.ts +++ b/packages/harmony-account/src/account.ts @@ -12,7 +12,7 @@ import { import { isPrivateKey, add0xToString, hexToNumber } from '@harmony/utils'; import { Transaction } from '@harmony/transaction'; -import { Messenger, RPCMethod } from '@harmony/network'; +import { Messenger, RPCMethod, getResultForData } from '@harmony/network'; import { Shards } from './types'; import { RLPSign } from './utils'; @@ -103,14 +103,20 @@ class Account { */ async getBalance(): Promise { if (this.messenger) { - const balance = await this.messenger.send(RPCMethod.GetBalance, [ - this.address, - 'latest', - ]); - const nonce = await this.messenger.send(RPCMethod.GetTransactionCount, [ - this.address, - 'latest', - ]); + const balance = getResultForData( + await this.messenger.send(RPCMethod.GetBalance, [ + this.address, + 'latest', + ]), + ); + console.log({ balance }); + const nonce = getResultForData( + await this.messenger.send(RPCMethod.GetTransactionCount, [ + this.address, + 'latest', + ]), + ); + console.log({ nonce }); this.balance = hexToNumber(balance); this.nonce = Number.parseInt(hexToNumber(nonce), 10); diff --git a/packages/harmony-account/src/wallet.ts b/packages/harmony-account/src/wallet.ts index 4ac658d..22e3799 100644 --- a/packages/harmony-account/src/wallet.ts +++ b/packages/harmony-account/src/wallet.ts @@ -56,7 +56,8 @@ class Wallet { const seed = bip39.mnemonicToSeed(phrase); const hdKey = hdkey.fromMasterSeed(seed); // TODO:hdkey should apply to Harmony's settings - const childKey = hdKey.derive(`m/44'/313'/0'/0/${index}`); + const path = '60'; + const childKey = hdKey.derive(`m/44'/${path}'/0'/0/${index}`); const privateKey = childKey.privateKey.toString('hex'); return this.addByPrivateKey(privateKey); } diff --git a/packages/harmony-core/src/blockchain.ts b/packages/harmony-core/src/blockchain.ts index fb01286..b22d936 100644 --- a/packages/harmony-core/src/blockchain.ts +++ b/packages/harmony-core/src/blockchain.ts @@ -1,4 +1,4 @@ -import { RPCMethod, Messenger } from '@harmony/network'; +import { RPCMethod, Messenger, ResponseMiddleware } from '@harmony/network'; import { assertObject, @@ -22,6 +22,13 @@ class Blockchain extends HarmonyCore { setMessenger(messenger: Messenger) { this.messenger = messenger; } + getRpcResult(result: any) { + if (result instanceof ResponseMiddleware) { + return result.getRaw; + } else { + return result; + } + } /** * @@ -42,7 +49,7 @@ class Blockchain extends HarmonyCore { [address, blockNumber], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } /** @@ -64,7 +71,7 @@ class Blockchain extends HarmonyCore { [blockHash, returnObject], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } /** @@ -86,7 +93,7 @@ class Blockchain extends HarmonyCore { [blockNumber, returnObject], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } @assertObject({ @@ -98,7 +105,7 @@ class Blockchain extends HarmonyCore { [blockHash], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } @assertObject({ @@ -114,7 +121,7 @@ class Blockchain extends HarmonyCore { [blockNumber], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } /** @@ -136,7 +143,7 @@ class Blockchain extends HarmonyCore { [blockHash, index], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } @assertObject({ @@ -155,7 +162,7 @@ class Blockchain extends HarmonyCore { [blockNumber, index], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } @assertObject({ @@ -167,7 +174,7 @@ class Blockchain extends HarmonyCore { [txnHash], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } /** @@ -182,7 +189,7 @@ class Blockchain extends HarmonyCore { [txnHash], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } /** * @@ -203,7 +210,7 @@ class Blockchain extends HarmonyCore { [address, blockNumber], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } /** @@ -215,15 +222,14 @@ class Blockchain extends HarmonyCore { [], this.chainPrefix, ); - if (result.responseType === 'raw') { - return result.result; - } - return result; + + return this.getRpcResult(result); } async net_peerCount() { const result = await this.messenger.send(RPCMethod.PeerCount, [], 'net'); - return result; + + return this.getRpcResult(result); } @assertObject({ @@ -245,7 +251,7 @@ class Blockchain extends HarmonyCore { [address, position, blockNumber], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } @assertObject({ @@ -264,7 +270,7 @@ class Blockchain extends HarmonyCore { [address, blockNumber], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } async sendTransaction(transaction: Transaction) { @@ -276,7 +282,7 @@ class Blockchain extends HarmonyCore { [transaction.txPayload], this.chainPrefix, ); - return result; + return this.getRpcResult(result); } async sendRawTransaction(transaction: Transaction) { diff --git a/packages/harmony-core/src/harmony.ts b/packages/harmony-core/src/harmony.ts index cc18c55..4e05021 100644 --- a/packages/harmony-core/src/harmony.ts +++ b/packages/harmony-core/src/harmony.ts @@ -1,7 +1,7 @@ import * as crypto from '@harmony/crypto'; import * as utils from '@harmony/utils'; -import { HttpProvider, Messenger } from '@harmony/network'; +import { HttpProvider, Messenger, WSProvider } from '@harmony/network'; import { TransactionFactory, Transaction } from '@harmony/transaction'; import { Wallet, Account } from '@harmony/account'; import { Blockchain } from './blockchain'; @@ -9,6 +9,7 @@ import { Blockchain } from './blockchain'; class Harmony extends utils.HarmonyCore { Modules = { HttpProvider, + WSProvider, Messenger, Blockchain, TransactionFactory, @@ -22,14 +23,18 @@ class Harmony extends utils.HarmonyCore { blockchain: Blockchain; crypto: any; utils: any; - private provider: HttpProvider; - + private provider: HttpProvider | WSProvider; constructor( url: string, chainType: utils.ChainType = utils.ChainType.Harmony, ) { super(chainType); - this.provider = new HttpProvider(url); + + this.provider = utils.isHttp(url) + ? new HttpProvider(url) + : utils.isWs(url) + ? new WSProvider(url) + : new HttpProvider('http://localhost:9128'); this.messenger = new Messenger(this.provider, this.chainType); this.blockchain = new Blockchain(this.messenger, this.chainType); this.transactions = new TransactionFactory(this.messenger); @@ -37,12 +42,15 @@ class Harmony extends utils.HarmonyCore { this.crypto = crypto; this.utils = utils; } - - setProvider(provider: string | HttpProvider): void { + setProvider(provider: string | HttpProvider | WSProvider): void { if (utils.isHttp(provider) && typeof provider === 'string') { this.provider = new HttpProvider(provider); } else if (provider instanceof HttpProvider) { this.provider = provider; + } else if (utils.isWs(provider) && typeof provider === 'string') { + this.provider = new WSProvider(provider); + } else if (provider instanceof WSProvider) { + this.provider = provider; } this.messenger.setProvider(this.provider); this.blockchain.setMessenger(this.messenger); diff --git a/packages/harmony-network/package.json b/packages/harmony-network/package.json index 7f8b60f..cba847a 100644 --- a/packages/harmony-network/package.json +++ b/packages/harmony-network/package.json @@ -14,8 +14,10 @@ "author": "neeboo@firestack.one", "license": "ISC", "dependencies": { - "cross-fetch": "^3.0.2", "@harmony/core": "^0.0.1", - "@harmony/utils": "^0.0.1" + "@harmony/utils": "^0.0.1", + "cross-fetch": "^3.0.2", + "mitt": "^1.1.3", + "websocket": "^1.0.28" } } diff --git a/packages/harmony-network/src/index.ts b/packages/harmony-network/src/index.ts index c640b46..132565e 100644 --- a/packages/harmony-network/src/index.ts +++ b/packages/harmony-network/src/index.ts @@ -1,7 +1,9 @@ // provider related export * from './providers/baseProvider'; +export * from './providers/baseSocket'; export * from './providers/defaultFetcher'; export * from './providers/http'; +export * from './providers/ws'; // messenger and middlewares export * from './messenger/messenger'; export * from './messenger/responseMiddleware'; diff --git a/packages/harmony-network/src/messenger/messenger.ts b/packages/harmony-network/src/messenger/messenger.ts index 30c09d3..afe8d4c 100644 --- a/packages/harmony-network/src/messenger/messenger.ts +++ b/packages/harmony-network/src/messenger/messenger.ts @@ -2,14 +2,15 @@ import { HarmonyCore, ChainType, isString } from '@harmony/utils'; import { JsonRpc } from '../rpcMethod/rpcbuilder'; import { ResponseMiddleware } from './responseMiddleware'; import { HttpProvider } from '../providers/http'; -import { getResultForData } from '../util'; +import { WSProvider } from '../providers/ws'; +// import { getResultForData } from '../util'; import { RPCMethod } from '../rpcMethod/rpc'; const defaultConfig = { Default: { CHAIN_ID: 0, Network_ID: 'Default', - nodeProviderUrl: 'http://localhost:4200', + nodeProviderUrl: 'http://localhost:9128', }, DevNet: { CHAIN_ID: 333, @@ -36,14 +37,14 @@ const defaultConfig = { * @return {Messenger} Messenger instance */ class Messenger extends HarmonyCore { - provider: HttpProvider; + provider: HttpProvider | WSProvider; config?: object; // tslint:disable-next-line: variable-name Network_ID: string = 'Default'; JsonRpc: JsonRpc; constructor( - provider: HttpProvider, + provider: HttpProvider | WSProvider, chainType: ChainType = ChainType.Harmony, config?: object, ) { @@ -99,9 +100,16 @@ class Messenger extends HarmonyCore { } try { const payload = this.JsonRpc.toPayload(rpcMethod, params); - this.setResMiddleware((data: any) => new ResponseMiddleware(data)); + this.setResMiddleware((data: any) => { + if (!(data instanceof ResponseMiddleware)) { + return new ResponseMiddleware(data); + } else { + return data; + } + }); const result = await this.provider.send(payload); - return getResultForData(result); // getResultForData(result) + return result; + // return getResultForData(result); // getResultForData(result) } catch (e) { throw new Error(e); } @@ -113,7 +121,7 @@ class Messenger extends HarmonyCore { * @description provider setter * @param {Provider} provider - provider instance */ - setProvider(provider: HttpProvider) { + setProvider(provider: HttpProvider | WSProvider) { this.provider = provider; } @@ -137,7 +145,7 @@ class Messenger extends HarmonyCore { * @param {String} method - method name */ setReqMiddleware(middleware: any, method = '*') { - return this.provider.middlewares.request.use(middleware, method); + this.provider.middlewares.request.use(middleware, method); } /** diff --git a/packages/harmony-network/src/providers/baseSocket.ts b/packages/harmony-network/src/providers/baseSocket.ts new file mode 100644 index 0000000..99170ee --- /dev/null +++ b/packages/harmony-network/src/providers/baseSocket.ts @@ -0,0 +1,88 @@ +import { isWs } from '@harmony/utils'; +import mitt from 'mitt'; +import { BaseProvider } from './baseProvider'; + +export const enum SocketConnection { + READY = 'ready', + CONNECT = 'connect', + ERROR = 'error', + CLOSE = 'close', +} + +export const enum SocketState { + SOCKET_MESSAGE = 'socket_message', + SOCKET_READY = 'socket_ready', + SOCKET_CLOSE = 'socket_close', + SOCKET_ERROR = 'socket_error', + SOCKET_CONNECT = 'socket_connect', + SOCKET_NETWORK_CHANGED = 'socket_networkChanged', + SOCKET_ACCOUNTS_CHANGED = 'socket_accountsChanged', +} + +class BaseSocket extends BaseProvider { + url: string; + emitter: mitt.Emitter; + subscriptions: any = {}; + handlers: any = {}; + constructor(url: string) { + super(url); + if (!isWs(url)) { + throw new Error(`${url} is not websocket`); + } + this.url = url; + this.emitter = new mitt(this.handlers); + } + resetHandlers() { + // tslint:disable-next-line: forin + for (const i in this.handlers) { + delete this.handlers[i]; + } + } + + once(type: string, handler: mitt.Handler) { + this.emitter.on(type, handler); + this.removeListener(type); + } + + removeListener(type?: string, handler?: mitt.Handler) { + if (!type) { + this.handlers = {}; + return; + } + if (!handler) { + delete this.handlers[type]; + } else { + return this.emitter.off(type, handler); + } + } + reset() { + this.removeListener('*'); + // this.registerEventListeners(); + } + removeAllSocketListeners() { + this.removeListener(SocketState.SOCKET_MESSAGE); + this.removeListener(SocketState.SOCKET_READY); + this.removeListener(SocketState.SOCKET_CLOSE); + this.removeListener(SocketState.SOCKET_ERROR); + this.removeListener(SocketState.SOCKET_CONNECT); + } + + onReady(event: any) { + this.emitter.on(SocketConnection.READY, () => event); + this.emitter.on(SocketState.SOCKET_READY, () => event); + } + onError(error: any) { + this.emitter.on(SocketConnection.ERROR, () => error); + this.emitter.on(SocketState.SOCKET_ERROR, () => error); + this.removeAllSocketListeners(); + this.removeListener('*'); + } + onClose(error = null) { + this.emitter.on(SocketConnection.CLOSE, () => error); + this.emitter.on(SocketState.SOCKET_CLOSE, () => error); + this.removeAllSocketListeners(); + this.removeListener('*'); + } +} + +export { BaseSocket }; diff --git a/packages/harmony-network/src/providers/http.ts b/packages/harmony-network/src/providers/http.ts index 15dd7d5..c77823b 100644 --- a/packages/harmony-network/src/providers/http.ts +++ b/packages/harmony-network/src/providers/http.ts @@ -23,7 +23,7 @@ class HttpProvider extends BaseProvider { options?: any; constructor(url: string, options?: any, fetcher?: any) { super(url); - this.url = url || 'http://localhost:4201'; + this.url = url || 'http://localhost:9128'; this.fetcher = fetcher || fetchRPC; if (options) { this.options = { diff --git a/packages/harmony-network/src/providers/ws.ts b/packages/harmony-network/src/providers/ws.ts new file mode 100644 index 0000000..ab272ea --- /dev/null +++ b/packages/harmony-network/src/providers/ws.ts @@ -0,0 +1,86 @@ +// TODO: implement Websocket Provider +import { w3cwebsocket as W3CWebsocket } from 'websocket'; +import { BaseSocket } from './baseSocket'; +import { isWs } from '@harmony/utils'; +import { JsonRpc } from '../rpcMethod/rpcbuilder'; +import { composeMiddleware } from '../rpcMethod/net'; +import { RPCRequestPayload } from '../types'; + +class WSProvider extends BaseSocket { + url: string; + subscriptions: any = {}; + options: any = {}; + connection: W3CWebsocket | WebSocket; + jsonRpc: JsonRpc; + // ws: w3cwebsocket; + constructor(url: string, options: any = {}) { + super(url); + if (!isWs(url)) { + throw new Error(`${url} is not websocket`); + } + this.url = url; + this.options = options; + this.connection = this.createWebsocketProvider(this.url, this.options); + this.jsonRpc = new JsonRpc(); + } + + createWebsocketProvider(url: string, options: any = {}) { + // tslint:disable-next-line: no-string-literal + if (typeof window !== 'undefined' && (window).WebSocket) { + // tslint:disable-next-line: no-string-literal + return new WebSocket(url, options.protocol); + } else { + const headers = options.headers || {}; + const urlObject = new URL(url); + + if (!headers.authorization && urlObject.username && urlObject.password) { + const authToken = Buffer.from( + `${urlObject.username}:${urlObject.password}`, + ).toString('base64'); + headers.authorization = `Basic ${authToken}`; + } + + return new W3CWebsocket( + url, + options.protocol, + undefined, + headers, + undefined, + options.clientConfig, + ); + } + } + + get connected() { + return this.connection.readyState === this.connection.OPEN; + } + + isConnecting() { + return this.connection.readyState === this.connection.CONNECTING; + } + + send(payload: RPCRequestPayload): Promise { + const [tReq, tRes] = this.getMiddleware(payload.method); + const reqMiddleware = composeMiddleware(...tReq); + const resMiddleware = composeMiddleware(...tRes); + + return new Promise((resolve, reject) => { + this.connection.send(reqMiddleware(JSON.stringify(payload))); + this.connection.onmessage = (msg: MessageEvent) => { + if (msg && msg.data) { + let result; + try { + result = JSON.parse(msg.data); + resolve(resMiddleware(result)); + } catch (error) { + reject(error); + } + } else { + reject('provider error'); + } + }; + }); + } +} + +export { WSProvider }; diff --git a/packages/harmony-network/src/util.ts b/packages/harmony-network/src/util.ts index 41591e6..ceb69b4 100644 --- a/packages/harmony-network/src/util.ts +++ b/packages/harmony-network/src/util.ts @@ -13,3 +13,7 @@ export function getResultForData(data: any): any { } return data.getRaw; } + +export function getRawForData(data: any): any { + return data.getRaw; +} diff --git a/packages/harmony-transaction/src/transaction.ts b/packages/harmony-transaction/src/transaction.ts index 3bc04e1..a38c28a 100644 --- a/packages/harmony-transaction/src/transaction.ts +++ b/packages/harmony-transaction/src/transaction.ts @@ -8,7 +8,7 @@ import { splitSignature, } from '@harmony/crypto'; import { add0xToString, numberToHex } from '@harmony/utils'; -import { Messenger, RPCMethod } from '@harmony/network'; +import { Messenger, RPCMethod, getResultForData } from '@harmony/network'; import { TxParams, TxStatus, TransasctionReceipt } from './types'; import { recover, transactionFields, sleep } from './utils'; @@ -220,9 +220,8 @@ class Transaction { if (!this.messenger) { throw new Error('Messenger not found'); } - const result = await this.messenger.send( - RPCMethod.SendRawTransaction, - this.txnHash, + const result = getResultForData( + await this.messenger.send(RPCMethod.SendRawTransaction, this.txnHash), ); // temporarilly hard coded @@ -243,9 +242,8 @@ class Transaction { throw new Error('Messenger not found'); } // TODO: regex validation for txHash so we don't get garbage - const res: TransasctionReceipt = await this.messenger.send( - RPCMethod.GetTransactionReceipt, - txHash, + const res: TransasctionReceipt = getResultForData( + await this.messenger.send(RPCMethod.GetTransactionReceipt, txHash), ); if (res.responseType === 'error') { return false; diff --git a/packages/harmony-utils/src/validators.ts b/packages/harmony-utils/src/validators.ts index 79396b3..fbc463e 100644 --- a/packages/harmony-utils/src/validators.ts +++ b/packages/harmony-utils/src/validators.ts @@ -134,7 +134,7 @@ export const isWs = (obj: any): boolean => { if (!isString(obj)) { throw new Error(`${obj} is not valid url`); } else { - return obj.startsWith('ws://') || obj.startsWith('websocket://'); + return obj.startsWith('ws://') || obj.startsWith('wss://'); } }; isWs.validator = 'isWs'; diff --git a/typings/window.d.ts b/typings/window.d.ts new file mode 100644 index 0000000..e374cc5 --- /dev/null +++ b/typings/window.d.ts @@ -0,0 +1,4 @@ +export interface Window { + WebSocket: any; + MozWebSocket: any; +}