diff --git a/.prettierrc b/.prettierrc index bbaef00..8904e0d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,8 @@ { "parser": "typescript", - "printWidth": 80, + "printWidth": 100, "singleQuote": true, + "bracketSpacing": true, "trailingComma": "all", "arrowParens": "always", "overrides": [ @@ -10,6 +11,12 @@ "options": { "parser": "json" } + }, + { + "files": ".prettierrc", + "options": { + "parser": "json" + } } ] } diff --git a/packages/harmony-core/src/harmony.ts b/packages/harmony-core/src/harmony.ts index cd21fe3..a338ee0 100644 --- a/packages/harmony-core/src/harmony.ts +++ b/packages/harmony-core/src/harmony.ts @@ -1,12 +1,12 @@ import * as crypto from '@harmony-js/crypto'; import * as utils from '@harmony-js/utils'; -import {HttpProvider, Messenger, WSProvider} from '@harmony-js/network'; -import {TransactionFactory, Transaction} from '@harmony-js/transaction'; -import {ContractFactory, Contract} from '@harmony-js/contract'; -import {Wallet, Account} from '@harmony-js/account'; -import {Blockchain} from './blockchain'; -import {HarmonyConfig} from './util'; +import { Provider, HttpProvider, Messenger, WSProvider } from '@harmony-js/network'; +import { TransactionFactory, Transaction } from '@harmony-js/transaction'; +import { ContractFactory, Contract } from '@harmony-js/contract'; +import { Wallet, Account } from '@harmony-js/account'; +import { Blockchain } from './blockchain'; +import { HarmonyConfig } from './util'; export class Harmony extends utils.HarmonyCore { Modules = { @@ -40,7 +40,7 @@ export class Harmony extends utils.HarmonyCore { const providerUrl = config.chainUrl || url; - this.provider = this.onInitSetProvider(providerUrl); + this.provider = new Provider(providerUrl).provider; this.messenger = new Messenger(this.provider, this.chainType, this.chainId); this.blockchain = new Blockchain(this.messenger); this.transactions = new TransactionFactory(this.messenger); @@ -50,15 +50,7 @@ export class Harmony extends utils.HarmonyCore { this.utils = utils; } public 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.provider = new Provider(provider).provider; this.messenger.setProvider(this.provider); this.blockchain.setMessenger(this.messenger); this.wallet.setMessenger(this.messenger); @@ -79,11 +71,4 @@ export class Harmony extends utils.HarmonyCore { this.wallet.setMessenger(this.messenger); this.transactions.setMessenger(this.messenger); } - private onInitSetProvider(providerUrl: string): HttpProvider | WSProvider { - return utils.isHttp(providerUrl) - ? new HttpProvider(providerUrl) - : utils.isWs(providerUrl) - ? new WSProvider(providerUrl) - : new HttpProvider(utils.defaultConfig.Default.Chain_URL); - } } diff --git a/packages/harmony-crypto/src/keyTool.ts b/packages/harmony-crypto/src/keyTool.ts index 6624f44..8f68bea 100644 --- a/packages/harmony-crypto/src/keyTool.ts +++ b/packages/harmony-crypto/src/keyTool.ts @@ -50,10 +50,11 @@ export const getAddressFromPrivateKey = (privateKey: string): string => { }; export const getPublic = (privateKey: string, compress?: boolean): string => { - if (!isPrivateKey(privateKey)) { + if (!isPrivateKey(privateKey) || !validatePrivateKey(privateKey)) { throw new Error(`${privateKey} is not PrivateKey`); } const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex'); + return ecKey.getPublic(compress || false, 'hex'); }; @@ -104,10 +105,7 @@ export const toChecksumAddress = (address: string): string => { return '0x' + chars.join(''); }; -export const sign = ( - digest: bytes.Arrayish | string, - privateKey: string, -): bytes.Signature => { +export const sign = (digest: bytes.Arrayish | string, privateKey: string): bytes.Signature => { if (!isPrivateKey(privateKey)) { throw new Error(`${privateKey} is not PrivateKey`); } @@ -134,9 +132,7 @@ export function getContractAddress(from: string, nonce: number): string { throw new Error('missing from address'); } - const addr = keccak256( - encode([from, bytes.stripZeros(bytes.hexlify(nonce))]), - ); + const addr = keccak256(encode([from, bytes.stripZeros(bytes.hexlify(nonce))])); return '0x' + addr.substring(26); } @@ -156,11 +152,7 @@ export function recoverPublicKey( const rs = { r: bytes.arrayify(sig.r), s: bytes.arrayify(sig.s) }; //// - const recovered = secp256k1.recoverPubKey( - bytes.arrayify(digest), - rs, - sig.recoveryParam, - ); + const recovered = secp256k1.recoverPubKey(bytes.arrayify(digest), rs, sig.recoveryParam); const key = recovered.encode('hex', false); const ecKey = secp256k1.keyFromPublic(key, 'hex'); @@ -189,8 +181,11 @@ export function recoverAddress( * @returns {boolean} */ export const isValidChecksumAddress = (address: string): boolean => { - return ( - isAddress(address.replace('0x', '')) && - toChecksumAddress(address) === address - ); + return isAddress(address.replace('0x', '')) && toChecksumAddress(address) === address; +}; + +export const validatePrivateKey = (privateKey: string): boolean => { + const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex'); + const { result } = ecKey.validate(); + return result; }; diff --git a/packages/harmony-network/src/index.ts b/packages/harmony-network/src/index.ts index 6dc3545..9346109 100644 --- a/packages/harmony-network/src/index.ts +++ b/packages/harmony-network/src/index.ts @@ -1,5 +1,5 @@ import mitt from 'mitt'; -export {mitt}; +export { mitt }; // provider related export * from './providers/baseProvider'; export * from './providers/baseSocket'; @@ -7,6 +7,7 @@ export * from './providers/defaultFetcher'; export * from './providers/http'; export * from './providers/ws'; export * from './providers/emitter'; +export * from './providers/provider'; // messenger and middlewares export * from './messenger/messenger'; export * from './messenger/responseMiddleware'; diff --git a/packages/harmony-network/src/providers/provider.ts b/packages/harmony-network/src/providers/provider.ts new file mode 100644 index 0000000..87d572c --- /dev/null +++ b/packages/harmony-network/src/providers/provider.ts @@ -0,0 +1,55 @@ +import { HttpProvider } from './http'; +import { WSProvider } from './ws'; +import { defaultConfig, isHttp, isWs } from '@harmony-js/utils'; + +export enum ProviderType { + http = 'http', + ws = 'ws', +} + +export class Provider { + static getProvider(provider: string | HttpProvider | WSProvider) { + try { + this.getProvider(provider); + return new Provider(provider); + } catch (error) { + throw error; + } + } + public provider: WSProvider | HttpProvider; + public providerType: ProviderType; + constructor(url: string | WSProvider | HttpProvider) { + this.provider = this.onInitSetProvider(url); + this.providerType = this.getType(this.provider); + } + private onInitSetProvider( + providerUrl: string | HttpProvider | WSProvider, + ): HttpProvider | WSProvider { + if (typeof providerUrl === 'string') { + return isHttp(providerUrl) + ? new HttpProvider(providerUrl) + : isWs(providerUrl) + ? new WSProvider(providerUrl) + : new HttpProvider(defaultConfig.Default.Chain_URL); + } + try { + const providerType = this.getType(providerUrl); + if (providerType === ProviderType.http || providerType === ProviderType.ws) { + return providerUrl; + } else { + throw new Error('cannot get provider type'); + } + } catch (error) { + throw error; + } + } + private getType(provider: HttpProvider | WSProvider) { + if (provider instanceof HttpProvider) { + return ProviderType.http; + } + if (provider instanceof WSProvider) { + return ProviderType.ws; + } + throw new Error('provider is not correct'); + } +} diff --git a/packages/harmony-transaction/src/transaction.ts b/packages/harmony-transaction/src/transaction.ts index 4b04058..bf185c0 100644 --- a/packages/harmony-transaction/src/transaction.ts +++ b/packages/harmony-transaction/src/transaction.ts @@ -9,7 +9,7 @@ import { getAddress, HarmonyAddress, } from '@harmony-js/crypto'; -import {add0xToString, numberToHex, ChainType, Unit} from '@harmony-js/utils'; +import { add0xToString, numberToHex, ChainType, Unit } from '@harmony-js/utils'; import { Messenger, RPCMethod, @@ -19,7 +19,7 @@ import { // SubscribeReturns, NewHeaders, } from '@harmony-js/network'; -import {TxParams, TxStatus, TransasctionReceipt} from './types'; +import { TxParams, TxStatus, TransasctionReceipt } from './types'; import { recover, transactionFields, @@ -80,19 +80,13 @@ class Transaction { this.to = params && params.to ? this.normalizeAddress(params.to) : '0x'; this.value = - params && params.value - ? new Unit(params.value).asWei().toWei() - : new Unit(0).asWei().toWei(); + params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei(); this.data = params && params.data ? params.data : '0x'; // chainid should change with different network settings - this.chainId = - params && params.chainId ? params.chainId : this.messenger.chainId; - this.rawTransaction = - params && params.rawTransaction ? params.rawTransaction : '0x'; + this.chainId = params && params.chainId ? params.chainId : this.messenger.chainId; + this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x'; this.unsignedRawTransaction = - params && params.unsignedRawTransaction - ? params.unsignedRawTransaction - : '0x'; + params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x'; this.signature = params && params.signature ? params.signature @@ -114,24 +108,15 @@ class Transaction { // temp setting to be compatible with eth const fields = - this.messenger.chainType === ChainType.Harmony - ? transactionFields - : transactionFieldsETH; + this.messenger.chainType === ChainType.Harmony ? transactionFields : transactionFieldsETH; fields.forEach((field: any) => { let value = (this.txParams)[field.name] || []; value = arrayify( - hexlify( - field.transform === 'hex' ? add0xToString(value.toString(16)) : value, - ), + hexlify(field.transform === 'hex' ? add0xToString(value.toString(16)) : value), ); // Fixed-width field - if ( - field.fix === true && - field.length && - value.length !== field.length && - value.length > 0 - ) { + if (field.fix === true && field.length && value.length !== field.length && value.length > 0) { throw new Error(`invalid length for ${field.name}`); } @@ -196,9 +181,7 @@ class Transaction { shardID: this.txParams.shardID ? numberToHex(this.shardID) : '0x', toShardID: this.txParams.toShardID ? numberToHex(this.toShardID) : '0x', gas: this.txParams.gasLimit ? numberToHex(this.txParams.gasLimit) : '0x', - gasPrice: this.txParams.gasPrice - ? numberToHex(this.txParams.gasPrice) - : '0x', + gasPrice: this.txParams.gasPrice ? numberToHex(this.txParams.gasPrice) : '0x', value: this.txParams.value ? numberToHex(this.txParams.value) : '0x', data: this.txParams.data || '0x', nonce: this.txParams.nonce ? numberToHex(this.nonce) : '0x', @@ -239,17 +222,12 @@ class Transaction { this.toShardID = params && params.toShardID ? params.toShardID : 0; this.to = params && params.to ? this.normalizeAddress(params.to) : '0x'; this.value = - params && params.value - ? new Unit(params.value).asWei().toWei() - : new Unit(0).asWei().toWei(); + params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei(); this.data = params && params.data ? params.data : '0x'; this.chainId = params && params.chainId ? params.chainId : 0; - this.rawTransaction = - params && params.rawTransaction ? params.rawTransaction : '0x'; + this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x'; this.unsignedRawTransaction = - params && params.unsignedRawTransaction - ? params.unsignedRawTransaction - : '0x'; + params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x'; this.signature = params && params.signature ? params.signature @@ -310,10 +288,7 @@ class Transaction { if (!this.messenger) { throw new Error('Messenger not found'); } - const res = await this.messenger.send( - RPCMethod.SendRawTransaction, - this.rawTransaction, - ); + const res = await this.messenger.send(RPCMethod.SendRawTransaction, this.rawTransaction); // temporarilly hard coded if (res.isResult()) { @@ -337,10 +312,7 @@ class Transaction { throw new Error('Messenger not found'); } // TODO: regex validation for txHash so we don't get garbage - const res = await this.messenger.send( - RPCMethod.GetTransactionReceipt, - txHash, - ); + const res = await this.messenger.send(RPCMethod.GetTransactionReceipt, txHash); if (res.isResult() && res.result !== null) { this.receipt = res.result; @@ -363,20 +335,22 @@ class Transaction { } else { this.txStatus = TxStatus.PENDING; const currentBlock = await this.getBlockNumber(); + this.blockNumbers.push('0x' + currentBlock.toString('hex')); + this.confirmationCheck += 1; return false; } } else { + this.txStatus = TxStatus.PENDING; + const currentBlock = await this.getBlockNumber(); + this.blockNumbers.push('0x' + currentBlock.toString('hex')); + this.confirmationCheck += 1; return false; } } - async confirm( - txHash: string, - maxAttempts: number = 20, - interval: number = 1000, - ) { + async confirm(txHash: string, maxAttempts: number = 20, interval: number = 1000) { if (this.messenger.provider instanceof HttpProvider) { this.txStatus = TxStatus.PENDING; const oldBlock = await this.getBlockNumber(); @@ -390,6 +364,7 @@ class Transaction { if (newBlock.gte(nextBlock)) { checkBlock = newBlock; + if (await this.trackTx(txHash)) { this.emitConfirm(this.txStatus); return this; @@ -410,9 +385,7 @@ class Transaction { } this.txStatus = TxStatus.REJECTED; this.emitConfirm(this.txStatus); - throw new Error( - `The transaction is still not confirmed after ${maxAttempts} attempts.`, - ); + throw new Error(`The transaction is still not confirmed after ${maxAttempts} attempts.`); } else { try { if (await this.trackTx(txHash)) { @@ -426,22 +399,18 @@ class Transaction { this.txStatus = TxStatus.REJECTED; this.emitConfirm(this.txStatus); throw new Error( - `The transaction is still not confirmed after ${maxAttempts * - interval} mil seconds.`, + `The transaction is still not confirmed after ${maxAttempts * interval} mil seconds.`, ); } } } - socketConfirm( - txHash: string, - maxAttempts: number = 20, - ): Promise { + socketConfirm(txHash: string, maxAttempts: number = 20): Promise { return new Promise((resolve, reject) => { const newHeads = Promise.resolve(new NewHeaders(this.messenger)); newHeads.then((p) => { p.onData(async (data: any) => { - if (!this.blockNumbers.includes(data.number)) { + if (!this.blockNumbers.includes(data.params.result.number)) { if (await this.trackTx(txHash)) { this.emitConfirm(this.txStatus); await p.unsubscribe(); @@ -492,10 +461,7 @@ class Transaction { } async getBlockByNumber(blockNumber: string) { try { - const block = await this.messenger.send(RPCMethod.GetBlockByNumber, [ - blockNumber, - true, - ]); + const block = await this.messenger.send(RPCMethod.GetBlockByNumber, [blockNumber, true]); if (block.isError()) { throw block.message; } @@ -519,4 +485,4 @@ class Transaction { } } } -export {Transaction}; +export { Transaction };