diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..e46f63e --- /dev/null +++ b/api/README.md @@ -0,0 +1,31 @@ +# API + +> API for Interacting with $EDI Token + +## Setup + +- valid Etherscan.io API Key +- Infura API Key + + +## Overview + +* Exchanges + - Buying + - Selling +* Reporting + - CMC + - Coingecko +* Staking + - Current Status + - Epoch + +## Examples + +```bash +$ npm install -g @fr8/token +$ edi-ctl --help +``` + +## License +Apache-2.0 diff --git a/api/package.json b/api/package.json new file mode 100644 index 0000000..54ef64f --- /dev/null +++ b/api/package.json @@ -0,0 +1,56 @@ +{ + "name": "@fr8/token-api", + "version": "0.1.1", + "description": "", + "scripts": { + "postinstall": "rm -rf ./bundle && npm run bundle && npm run bundlejs", + "bundle": "npm run tsc -- --project tsconfig.bundle.json", + "bundlejs": "./node_modules/browserify/bin/cmd.js -p esmify ./bundle/src/Client.js -o ./bundle/etherscan-api.js -s EtherscanClient", + "exportpkgversion": "node ./scripts/export-version.js", + "pretest": "npm run exportpkgversion", + "preecoverage": "npm run exportpkgversion", + "test": "ava-ts test/ -v", + "coverage": "nyc ava-ts test/ -v", + "posttest": "npm run lint", + "tsc": "./node_modules/typescript/bin/tsc", + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "docs": "typedoc --module etherscan-api --out docs ./src --tsBuildInfoFile ./tsconfig.json", + "prepublish": "tsc" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/freight-trust/editoken-api.git" + }, + "keywords": [ + "ethereum", + "api", + "eth", + "edi", + "erc20", + "staking" + ], + "main": "dist/src/Client.js", + "types": "dist/index.d.ts", + "author": "SEE LICENSE", + "license": "MIT", + "bugs": { + "url": "https://github.com/freight-trust/editoken-api/issues" + }, + "homepage": "https://github.com/freight-trust/editoken-api#readme", + "dependencies": { + "isomorphic-fetch": "^2.2.1" + }, + "devDependencies": { + "etherscan-api-test-fixtures": "1.0.0", + "ava": "^2.2.0", + "ava-ts": "^0.25.2", + "browserify": "^16.5.0", + "esmify": "^2.1.1", + "nock": "^11.3.2", + "nyc": "^14.1.1", + "ts-node": "^8.3.0", + "tslint": "^5.18.0", + "typedoc": "^0.15.0", + "typescript": "^3.5.3" + } +} diff --git a/api/scripts/bundler.js b/api/scripts/bundler.js new file mode 100644 index 0000000..dae9605 --- /dev/null +++ b/api/scripts/bundler.js @@ -0,0 +1,10 @@ +const { FuseBox } = require('fuse-box'); + +const fuse = FuseBox.init({ + homeDir: '../src', + output: '../bundle/$name.js', +}); + +fuse.bundle('etherscan-api').instructions(`> Client.ts`); + +fuse.run(); \ No newline at end of file diff --git a/api/scripts/export-version.js b/api/scripts/export-version.js new file mode 100644 index 0000000..38c85e8 --- /dev/null +++ b/api/scripts/export-version.js @@ -0,0 +1,6 @@ +const pkg = require('../package.json') +const fs = require('fs') +const text = `export const VERSION: string = '${pkg.version}' +` +fs.writeFileSync('./src/version.ts', text, 'utf-8') +console.log(`Version written to version.ts ${pkg.version}`) \ No newline at end of file diff --git a/api/src/Client.ts b/api/src/Client.ts new file mode 100644 index 0000000..18d08a5 --- /dev/null +++ b/api/src/Client.ts @@ -0,0 +1,539 @@ +import { account } from './actions/account' +import { block } from './actions/block' +import { contract } from './actions/contract' +import { logs } from './actions/logs' +import { proxy } from './actions/proxy' +import { stats } from './actions/stats' +import { transaction } from './actions/transaction' +import { ClientAccountBalance } from './client/account/Balance' +import { ClientAccountBalancemulti } from './client/account/Balancemulti' +import { ClientAccountGetminedblocks } from './client/account/Getminedblocks' +import { ClientAccountTokenbalance } from './client/account/Tokenbalance' +import { ClientAccountTokentx } from './client/account/Tokentx' +import { ClientAccountTxlist } from './client/account/Txlist' +import { ClientAccountTxlistinternal } from './client/account/Txlistinternal' +import { ClientAccountTxlistinternalTxhash } from './client/account/TxlistinternalTxhash' +import { ClientStakingGetstakingreward } from './client/block/Getblockreward' +import { ClientContractGetabi } from './client/contract/Getabi' +import { ClientContractGetsource } from './client/contract/Getsource' +import { ClientProxyEthBlocknumber } from './client/proxy/EthBlocknumber' +import { ClientProxyEthCall } from './client/proxy/EthCall' +import { ClientProxyEthEstimateGas } from './client/proxy/EthEstimateGas' +import { ClientProxyEthGasPrice } from './client/proxy/EthGasPrice' +import { ClientProxyEthGetblockByNumber } from './client/proxy/EthGetblockByNumber' +import { ClientProxyEthGetBlockTransactionCountByNumber} from './client/proxy/EthGetBlockTransactionCountByNumber' +import { ClientProxyEthGetCode } from './client/proxy/EthGetCode' +import { ClientProxyEthGetStorageAt } from './client/proxy/EthGetStorageAt' +import { ClientProxyEthGetTransactionByBlockNumberAndIndex } from './client/proxy/EthGetTransactionByBlockNumberAndIndex' +import { ClientProxyEthGetTransactionCount } from './client/proxy/EthGetTransactionCount' +import { ClientProxyEthGetTransactionByHash } from './client/proxy/EthGetTxByHash' +import { ClientProxyEthGetUncleByBlockNumberAndIndex } from './client/proxy/EthGetUncleByBlockNumberAndIndex' +import { ClientStatsChainsize } from './client/stats/Chainsize' +import { ClientStatsEthprice } from './client/stats/Ethprice' +import { ClientStatsEthsupply } from './client/stats/Ethsupply' +import { ClientStatsTokenupply } from './client/stats/Tokensupply' +import { ClientTransactionGetstatus } from './client/transaction/Getstatus' +import { ClientTransactionGettxreceiptstatus } from './client/transaction/Gettxreceiptstatus' +import { Address } from './entities/Address' +import { ApiKey } from './entities/Apikey' +import { Clienttype } from './entities/Clienttype' +import { Network } from './entities/Network' +import { Paging } from './entities/Paging' +import { PositiveNumber } from './entities/PositiveNumber' +import { Sort } from './entities/Sort' +import { Syncmode } from './entities/Syncmode' +import { UsDate } from './entities/UsDate' +import { performRequest } from './util/performRequest' +import { VERSION } from './version' + +/** + * Client for the api at etherscan.io + */ +export class Client { + /** + * Version number of the client + */ + static version: string = VERSION + /** + * Network + */ + protected network: Network + /** + * Api key to access the etherscan api + */ + private apiKey: ApiKey + constructor(apiKey: string, network?: string) { + this.apiKey = new ApiKey(apiKey) + this.apiKey.validate() + + this.network = network ? new Network(network) : new Network() + this.network.validate() + } + /** + * Proxy to web3 + * @param action + */ + proxy(action: string) { + const apiKey = this.apiKey + const network = this.network + if (!proxy.get(action)) { + throw new Error('unknown action' + action) + } + const actions: { [key: string]: any } = { + eth_call(to: string, data: string, tag: string) { + const client = new ClientProxyEthCall(to, data, tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthCall.module, + ClientProxyEthCall.action, + json, + ).then((response) => response.json()) + }, + eth_Blocknumber() { + const client = new ClientProxyEthBlocknumber() + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthBlocknumber.module, + ClientProxyEthBlocknumber.action, + json, + ).then((response) => response.json()) + }, + eth_estimateGas(value: string, to: string, gasPrice: string) { + const client = new ClientProxyEthEstimateGas(to, value, gasPrice) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthEstimateGas.module, + ClientProxyEthEstimateGas.action, + json, + ).then((response) => response.json()) + }, + eth_gasPrice() { + const client = new ClientProxyEthGasPrice() + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGasPrice.module, + ClientProxyEthGasPrice.action, + json, + ).then((response) => response.json()) + }, + eth_getBlockTransactionCountByNumber(tag: string) { + const client = new ClientProxyEthGetBlockTransactionCountByNumber(tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetBlockTransactionCountByNumber.module, + ClientProxyEthGetBlockTransactionCountByNumber.action, + json, + ).then((response) => response.json()) + }, + eth_getCode(address: string, tag: string) { + const client = new ClientProxyEthGetCode(new Address(address), tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetCode.module, + ClientProxyEthGetCode.action, + json, + ).then((response) => response.json()) + }, + eth_getStorageAt(address: string, position: string, tag: string) { + const client = new ClientProxyEthGetStorageAt(new Address(address), position, tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetStorageAt.module, + ClientProxyEthGetStorageAt.action, + json, + ).then((response) => response.json()) + }, + eth_getTransactionByBlockNumberAndIndex(index: string, tag: string) { + const client = new ClientProxyEthGetTransactionByBlockNumberAndIndex(index, tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetTransactionByBlockNumberAndIndex.module, + ClientProxyEthGetTransactionByBlockNumberAndIndex.action, + json, + ).then((response) => response.json()) + }, + eth_getTransactionCount(address: string, tag: string) { + const client = new ClientProxyEthGetTransactionCount(new Address(address), tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetTransactionCount.module, + ClientProxyEthGetTransactionCount.action, + json, + ).then((response) => response.json()) + }, + eth_getTransactionByHash(txhash: string) { + const client = new ClientProxyEthGetTransactionByHash(txhash) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetTransactionByHash.module, + ClientProxyEthGetTransactionByHash.action, + json, + ).then((response) => response.json()) + }, + eth_getUncleByBlockNumberAndIndex(index: string, tag: string) { + const client = new ClientProxyEthGetUncleByBlockNumberAndIndex(index, tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetUncleByBlockNumberAndIndex.module, + ClientProxyEthGetUncleByBlockNumberAndIndex.action, + json, + ).then((response) => response.json()) + }, + eth_getBlockByNumber(tag: string, bool: boolean) { + const client = new ClientProxyEthGetblockByNumber(tag, bool) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientProxyEthGetblockByNumber.module, + ClientProxyEthGetblockByNumber.action, + json, + ).then((response) => response.json()) + }, + } + return actions[action] + } + /** + * Access log Information + * @param action + */ + log(action: string): any { + const apiKey = this.apiKey + const network = this.network + if (!logs.get(action)) { + throw new Error('unknown action' + action) + } + const actions: { [key: string]: any } = { + getLogs( + fromBlock: string, + toBlock: string, + address: string, + topics: string[], + topic01opr?: string, + topic12opr?: string, + topic23opr?: string, + topic02opr?: string, + topic03opr?: string, + topic13opr?: string, + ) { + const oFromBlock = new PositiveNumber(fromBlock) + const oToBlock = new PositiveNumber(toBlock) + // const oToBlock = !!fromBlock ? new PositiveNumber(fromBlock) : undefined + }, + } + } + /** + * Statistics methods + * @param action + */ + stats(action: string): any { + const apiKey = this.apiKey + const network = this.network + if (!stats.get(action)) { + throw new Error('unknown action' + action) + } + const actions: { [key: string]: any } = { + ethprice() { + const client = new ClientStatsEthprice() + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientStatsTokenupply.module, + ClientStatsTokenupply.action, + json, + ).then((response) => response.json()) + }, + ethsupply() { + const client = new ClientStatsEthsupply() + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientStatsTokenupply.module, + ClientStatsTokenupply.action, + json, + ).then((response) => response.json()) + }, + chainsize(startdate: string, enddate: string, clienttype: string, syncmode: string) { + const oStartdate = new UsDate(startdate) + const oEnddate = new UsDate(enddate) + const oClienttype = new Clienttype(clienttype) + const oSyncmode = new Syncmode(syncmode) + const client = new ClientStatsChainsize(oStartdate, oEnddate, oClienttype, oSyncmode) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientStatsTokenupply.module, + ClientStatsTokenupply.action, + json, + ).then((response) => response.json()) + }, + tokensupply(contractaddress: string) { + const client = new ClientStatsTokenupply(contractaddress) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientStatsTokenupply.module, + ClientStatsTokenupply.action, + json, + ).then((response) => response.json()) + }, + } + return actions[action] + } + + /** + * Block api + * @param action + */ + block(action: string): any { + const apiKey = this.apiKey + const network = this.network + if (!block.get(action)) { + throw new Error('unknown action' + action) + } + const actions: { [key: string]: any } = { + getblockreward(blockno: string) { + const blocknoNumber = new PositiveNumber(blockno) + const client = new ClientStakingGetstakingreward(blocknoNumber) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientStakingGetstakingreward.module, + ClientStakingGetstakingreward.action, + json, + ).then((response) => response.json()) + }, + } + return actions[action] + } + /** + * Transaction api + * @param action + */ + transaction(action: string): any { + const apiKey = this.apiKey + const network = this.network + if (!transaction.get(action)) { + throw new Error('unknown action' + action) + } + const actions: { [key: string]: any } = { + getstatus(txhash: string) { + const client = new ClientTransactionGetstatus(txhash) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientContractGetabi.module, + ClientContractGetabi.action, + json, + ).then((response) => response.json()) + }, + gettxreceiptstatus(txhash: string) { + const client = new ClientTransactionGettxreceiptstatus(txhash) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientContractGetabi.module, + ClientContractGetabi.action, + json, + ).then((response) => response.json()) + }, + } + return actions[action] + } + + /** + * methods to access ethereum contract data + * @param action + */ + contract(action: string): any { + const apiKey = this.apiKey + const network = this.network + + if (!contract.get(action)) { + throw new Error('unknown action' + action) + } + const actions: { [key: string]: any } = { + getabi(address: string) { + const oAddress = new Address(address) + const client = new ClientContractGetabi(oAddress) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientContractGetabi.module, + ClientContractGetabi.action, + json, + ).then((response) => response.json()) + }, + getsourcecode(address: string) { + const oAddress = new Address(address) + const client = new ClientContractGetsource(oAddress) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientContractGetsource.module, + ClientContractGetsource.action, + json, + ).then((response) => response.json()) + }, + } + return actions[action] + } + + /** + * methods to access ethereum accounts + * @param action + */ + account(action: string): any { + + const validatingAction = action === 'txlistinternaltxhash' ? 'txlistinternal' : action + const apiKey = this.apiKey + const network = this.network + + if (!account.get(validatingAction)) { + throw new Error('unknown action' + validatingAction) + } + + const actions: { [key: string]: any } = { + balance: (address: string, tag: string): Promise => { + const oAddress = new Address(address) + const client = new ClientAccountBalance(oAddress, tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountBalance.module, + ClientAccountBalance.action, + json, + ).then((response) => response.json()) + }, + balancemulti(address: string[], tag: string): Promise { + const oAddress = address.map((addresString) => new Address(addresString)) + const client = new ClientAccountBalancemulti(oAddress, tag) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountBalancemulti.module, + ClientAccountBalancemulti.action, + json, + ).then((response) => response.json()) + }, + getstakingblocks(address: string, type: string): Promise { + const oAddress = new Address(address) + const client = new ClientAccountGetminedblocks(oAddress, type) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountGetminedblocks.module, + ClientAccountGetminedblocks.action, + json, + ).then((response) => response.json()) + }, + tokenbalance(address: string, contractaddress: string): Promise { + const client = new ClientAccountTokenbalance( + new Address(address), + new Address(contractaddress), + ) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountTokenbalance.module, + ClientAccountTokenbalance.action, + json, + ).then((response) => response.json()) + }, + tokentx(address: string, startblock: string, endblock: string, sort?: string): Promise { + const oAddress = new Address(address) + const oSort = !!sort ? new Sort(sort) : undefined + const client = new ClientAccountTokentx(oAddress, startblock, endblock, oSort) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountTokentx.module, + ClientAccountTokentx.action, + json, + ).then((response) => response.json()) + }, + txlist( + address: string, + startblock: string, + endblock: string, + sort?: string, + page?: string, + offset?: string): Promise { + const oAddress = new Address(address) + const oSort = !!sort ? new Sort(sort) : undefined + const paging = !!page && !!offset ? new Paging(new PositiveNumber(page), new PositiveNumber(offset)) : undefined + const client = new ClientAccountTxlist(oAddress, startblock, endblock, oSort, paging) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountTokentx.module, + ClientAccountTokentx.action, + json, + ).then((response) => response.json()) + }, + txlistinternal(address: string, startblock: string, endblock: string, sort?: string): Promise { + const oAddress = new Address(address) + const oSort = !!sort ? new Sort(sort) : undefined + const client = new ClientAccountTxlistinternal(oAddress, startblock, endblock, oSort) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountTokentx.module, + ClientAccountTokentx.action, + json, + ).then((response) => response.json()) + }, + txlistinternaltxhash(txhash: string, startblock: string, endblock: string, sort?: string): Promise { + const oSort = !!sort ? new Sort(sort) : undefined + const client = new ClientAccountTxlistinternalTxhash(txhash, startblock, endblock, oSort) + const json = client.toJson() + json.apiKey = apiKey.toString() + return performRequest( + network, + ClientAccountTokentx.module, + ClientAccountTokentx.action, + json, + ).then((response) => response.json()) + }, + } + return actions[action] + } +} diff --git a/api/src/actions/account.ts b/api/src/actions/account.ts new file mode 100644 index 0000000..036080d --- /dev/null +++ b/api/src/actions/account.ts @@ -0,0 +1,11 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'balance', + 'balancemulti', + 'txlist', + 'txlistinternal', + 'tokentx', + 'tokenbalance', + 'getstakingblocks', +] +export const account = mapFromArray(actionNames) diff --git a/api/src/actions/block.ts b/api/src/actions/block.ts new file mode 100644 index 0000000..9d7dceb --- /dev/null +++ b/api/src/actions/block.ts @@ -0,0 +1,5 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'getblockreward', +] +export const block = mapFromArray(actionNames) diff --git a/api/src/actions/contract.ts b/api/src/actions/contract.ts new file mode 100644 index 0000000..0e69eaf --- /dev/null +++ b/api/src/actions/contract.ts @@ -0,0 +1,6 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'getabi', + 'getsourcecode', +] +export const contract = mapFromArray(actionNames) diff --git a/api/src/actions/index.ts b/api/src/actions/index.ts new file mode 100644 index 0000000..3a46513 --- /dev/null +++ b/api/src/actions/index.ts @@ -0,0 +1,8 @@ +export { account } from './account' +export { block } from './block' +export { contract } from './contract' +export { logs } from './logs' +export { proxy } from './proxy' +export { stats } from './stats' +export { tokens } from './tokens' +export { transaction } from './transaction' diff --git a/api/src/actions/logs.ts b/api/src/actions/logs.ts new file mode 100644 index 0000000..2acbf9a --- /dev/null +++ b/api/src/actions/logs.ts @@ -0,0 +1,5 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'getLogs', +] +export const logs = mapFromArray(actionNames) diff --git a/api/src/actions/proxy.ts b/api/src/actions/proxy.ts new file mode 100644 index 0000000..ff129cb --- /dev/null +++ b/api/src/actions/proxy.ts @@ -0,0 +1,19 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'eth_Blocknumber', + 'eth_getBlockByNumber', + 'eth_getBlockByNumberAndIndex', + 'eth_getBlockTransactionCountByNumber', + 'eth_getTransactionByHash', + 'eth_getTransactionByBlockNumberAndIndex', + 'eth_getUncleByBlockNumberAndIndex', + 'eth_getTransactionCount', + 'eth_sendRawTransaction', + 'eth_getTransactionReceipt', + 'eth_call', + 'eth_getCode', + 'eth_getStorageAt', + 'eth_gasPrice', + 'eth_estimateGas', +] +export const proxy = mapFromArray(actionNames) diff --git a/api/src/actions/stats.ts b/api/src/actions/stats.ts new file mode 100644 index 0000000..7aa654b --- /dev/null +++ b/api/src/actions/stats.ts @@ -0,0 +1,8 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'ethsupply', + 'ethprice', + 'chainsize', + 'tokensupply', +] +export const stats = mapFromArray(actionNames) diff --git a/api/src/actions/tokens.ts b/api/src/actions/tokens.ts new file mode 100644 index 0000000..55b0197 --- /dev/null +++ b/api/src/actions/tokens.ts @@ -0,0 +1,6 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'tokenbalance', + 'tokensupply', +] +export const tokens = mapFromArray(actionNames) diff --git a/api/src/actions/transaction.ts b/api/src/actions/transaction.ts new file mode 100644 index 0000000..840f2f7 --- /dev/null +++ b/api/src/actions/transaction.ts @@ -0,0 +1,6 @@ +import { mapFromArray } from '../util/mapFromArray' +const actionNames = [ + 'getstatus', + 'gettxreceiptstatus', +] +export const transaction = mapFromArray(actionNames) diff --git a/api/src/client/ClientBase.ts b/api/src/client/ClientBase.ts new file mode 100644 index 0000000..61890df --- /dev/null +++ b/api/src/client/ClientBase.ts @@ -0,0 +1,39 @@ +import { Network } from '../entities/Network' + +/** + * Basic functions shared by all clients + */ +export class ClientBase { + + /** + * Network to use for requests + */ + protected network: Network = new Network() + /** + * Creates a URL for the API + */ + toUrl(): string { + return this.network.toUrl() + } + /** + * Sets the correct network + * @param chain + */ + setNetwork(network: Network) { + this.network = network + } + + /** + * Dies the actual request to the server + */ + async request(): Promise { + return fetch(this.toUrl()).then((response: any) => response.json()) + } + /** + * Processes the result and return it + * @param result String result of a request + */ + protected processResult(result: string): object { + return JSON.parse(result) + } +} diff --git a/api/src/client/ClientPagingBase.ts b/api/src/client/ClientPagingBase.ts new file mode 100644 index 0000000..f0b92d0 --- /dev/null +++ b/api/src/client/ClientPagingBase.ts @@ -0,0 +1,35 @@ +import { Paging } from '../entities/Paging' +import { PositiveNumber } from '../entities/PositiveNumber' +import { ClientBase } from './ClientBase' + +/** + * Basic paging base class + */ +export class ClientPagingBase extends ClientBase { + /** + * Paging with the correct defaults + */ + protected paging?: Paging = new Paging( + new PositiveNumber('1'), + new PositiveNumber('10'), + ) + /** + * Sets new values for paging + * @param page Page to show + * @param offset number of entries + */ + setPaging(page: string, offset: string) { + this.paging = new Paging( + new PositiveNumber(page), + new PositiveNumber(offset), + ) + } + /** + * Adds the paging information to the json + * @param json + */ + addPagingToJson(json: any) { + const pagingJson = this.paging ? this.paging.toJson() : {} + return Object.assign(json, pagingJson) + } +} diff --git a/api/src/client/account/Balance.ts b/api/src/client/account/Balance.ts new file mode 100644 index 0000000..60191e1 --- /dev/null +++ b/api/src/client/account/Balance.ts @@ -0,0 +1,42 @@ +import { Address } from '../../entities/Address' +import { IClientAccountBalanceRequest } from '../../interfaces/Account' +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientAccountBalance extends ClientBase implements IClientAccountBalanceRequest { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'balance' + /** + * Address to lookup the account balance + */ + address: Address + /** + * tag to limit the results + */ + tag: string + + constructor(address: Address, tag: string) { + super() + this.address = address + this.tag = tag + } + /** + * generates a json represenatation of the + */ + toJson(): any { + return { + action: ClientAccountBalance.action, + address: this.address.toString(), + module: ClientAccountBalance.module, + tag: this.tag.toString(), + } + } +} diff --git a/api/src/client/account/Balancemulti.ts b/api/src/client/account/Balancemulti.ts new file mode 100644 index 0000000..b496338 --- /dev/null +++ b/api/src/client/account/Balancemulti.ts @@ -0,0 +1,51 @@ +import { Address } from '../../entities/Address' +import { IClientAccountBalanceMultiRequest } from '../../interfaces/Account' +import { ClientBase } from '../ClientBase' +/** + * Client for the account balance + */ +export class ClientAccountBalancemulti extends ClientBase implements IClientAccountBalanceMultiRequest { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'balancemulti' + /** + * Address to lookup the account balance + */ + address: Address[] + /** + * tag to limit the results + */ + tag: string + + constructor(address: Address[], tag: string) { + super() + + if (!address.length || address.length === 0) { + throw new Error('At least one address needs to be provided') + } + + if (address.length >= 20) { + throw new Error('A maximum of 20 adresses is allowed') + } + + this.address = address + this.tag = tag + } + + /** + * generates a json represenatation of the + */ + toJson(): any { + return { + action: ClientAccountBalancemulti.action, + address: this.address.map((address) => address.toString()).join(', '), + module: ClientAccountBalancemulti.module, + tag: this.tag.toString(), + } + } +} diff --git a/api/src/client/account/Getminedblocks.ts b/api/src/client/account/Getminedblocks.ts new file mode 100644 index 0000000..5611da3 --- /dev/null +++ b/api/src/client/account/Getminedblocks.ts @@ -0,0 +1,48 @@ +import { Address } from '../../entities/Address' +import { IClientAccountGetminedblocks } from '../../interfaces/Account' +import { ClientPagingBase } from '../ClientPagingBase' + +/** + * Client for the account balance + */ +export class ClientAccountGetminedblocks extends ClientPagingBase implements IClientAccountGetminedblocks { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + + /** + * action of the etherscan api to request + */ + static action: string = 'getstakingblocks' + + /** + * Address to lookup the account balance + */ + address: Address + + /** + * tag to limit the results + */ + type: string + + constructor( + address: Address, + type: string) { + super() + this.address = address + this.type = type + } + + /** + * generates a json represenatation of the + */ + toJson(): any { + return { + action: ClientAccountGetminedblocks.action, + address: this.address.toString(), + module: ClientAccountGetminedblocks.module, + type: this.type.toString(), + } + } +} diff --git a/api/src/client/account/Tokenbalance.ts b/api/src/client/account/Tokenbalance.ts new file mode 100644 index 0000000..1e98837 --- /dev/null +++ b/api/src/client/account/Tokenbalance.ts @@ -0,0 +1,43 @@ +import { Address } from '../../entities/Address' +import { IClientAccountTokenbalanceRequest } from '../../interfaces/Account' +import { ClientPagingBase } from '../ClientPagingBase' + +/** + * Client for the token balance + */ +export class ClientAccountTokenbalance extends ClientPagingBase implements IClientAccountTokenbalanceRequest { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'tokenbalance' + /** + * Address to lookup the account balance + */ + address: Address + /** + * tag to limit the results + */ + contractaddress: Address + + constructor(address: Address, contractaddress: Address) { + super() + this.address = address + this.contractaddress = address + } + + /** + * generates a json represenatation of the + */ + toJson(): any { + return { + action: ClientAccountTokenbalance.action, + address: this.address.toString(), + contractaddress: this.contractaddress.toString(), + module: ClientAccountTokenbalance.module, + } + } +} diff --git a/api/src/client/account/Tokentx.ts b/api/src/client/account/Tokentx.ts new file mode 100644 index 0000000..3998700 --- /dev/null +++ b/api/src/client/account/Tokentx.ts @@ -0,0 +1,71 @@ +import { Address } from '../../entities/Address' +import { Contractaddress } from '../../entities/Contractaddress' +import { Paging } from '../../entities/Paging' +import { Sort } from '../../entities/Sort' +import { IClientAccountTokentx } from '../../interfaces/Account' +import { ClientPagingBase } from '../ClientPagingBase' + +/** + * Client for the account balance + */ +export class ClientAccountTokentx extends ClientPagingBase implements IClientAccountTokentx { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'tokentx' + /** + * Address to lookup the account balance + */ + address: Address + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * Sort + */ + sort: Sort + /** + * Page + */ + paging?: Paging + + constructor( + address: Address | Contractaddress, + startblock: string, + endblock: string, + sort: Sort = new Sort(), + paging?: Paging, + ) { + super() + this.address = address + this.startblock = startblock + this.endblock = endblock + this.sort = sort + this.paging = paging + } + + /** + * generates a json represenatation of the + */ + toJson(): any { + const json = { + action: ClientAccountTokentx.action, + address: this.address.toString(), + endblock: this.endblock.toString(), + module: ClientAccountTokentx.module, + sort: this.sort.toString(), + startblock: this.startblock.toString(), + } + + return this.addPagingToJson(json) + } +} diff --git a/api/src/client/account/Txlist.ts b/api/src/client/account/Txlist.ts new file mode 100644 index 0000000..84776ce --- /dev/null +++ b/api/src/client/account/Txlist.ts @@ -0,0 +1,69 @@ +import { Address } from '../../entities/Address' +import { Paging } from '../../entities/Paging' +import { Sort } from '../../entities/Sort' +import { IClientAccountTxlistRequest } from '../../interfaces/Account' +import { ClientPagingBase } from '../ClientPagingBase' +/** + * Client for the account balance + */ +export class ClientAccountTxlist extends ClientPagingBase implements IClientAccountTxlistRequest { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'txlist' + /** + * Address to lookup the account balance + */ + address: Address + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * Sort + */ + sort: Sort + + /** + * Paging + */ + paging?: Paging + + constructor( + address: Address, + startblock: string, + endblock: string, + sort: Sort = new Sort('desc'), + paging?: Paging, + ) { + super() + this.address = address + this.startblock = startblock + this.endblock = endblock + this.paging = paging + this.sort = sort + } + + /** + * generates a json represenatation of the + */ + toJson(): any { + const json = { + action: ClientAccountTxlist.action, + address: this.address.toString(), + endblock: this.endblock.toString(), + module: ClientAccountTxlist.module, + sort: this.sort.toString(), + startblock: this.startblock.toString(), + } + return this.addPagingToJson(json) + } +} diff --git a/api/src/client/account/Txlistinternal.ts b/api/src/client/account/Txlistinternal.ts new file mode 100644 index 0000000..6918f0d --- /dev/null +++ b/api/src/client/account/Txlistinternal.ts @@ -0,0 +1,69 @@ +import { Address } from '../../entities/Address' +import { ApiKey } from '../../entities/Apikey' +import { Paging } from '../../entities/Paging' +import { Sort } from '../../entities/Sort' +import { IClientAccountTxlistRequest } from '../../interfaces/Account' +import { ClientPagingBase } from '../ClientPagingBase' + +/** + * Client for the account balance + */ +export class ClientAccountTxlistinternal extends ClientPagingBase implements IClientAccountTxlistRequest { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'txlistinternal' + /** + * Address to lookup the account balance + */ + address: Address + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * Sort + */ + sort: Sort + /** + * Page + */ + paging?: Paging + + constructor( + address: Address, + startblock: string, + endblock: string, + sort: Sort = new Sort(), + paging?: Paging, + ) { + super() + this.address = address + this.startblock = startblock + this.endblock = endblock + this.paging = paging + this.sort = sort + } + /** + * Generates a JSON representation + */ + toJson(): any { + const json = { + action: ClientAccountTxlistinternal.action, + address: this.address.toString(), + endblock: this.endblock.toString(), + module: ClientAccountTxlistinternal.module, + sort: this.sort.toString(), + startblock: this.startblock.toString(), + } + return this.addPagingToJson(json) + } +} diff --git a/api/src/client/account/TxlistinternalTxhash.ts b/api/src/client/account/TxlistinternalTxhash.ts new file mode 100644 index 0000000..60b9b13 --- /dev/null +++ b/api/src/client/account/TxlistinternalTxhash.ts @@ -0,0 +1,68 @@ +import { Paging } from '../../entities/Paging' +import { Sort } from '../../entities/Sort' +import { IClientAccountTxlistInternalTxhash } from '../../interfaces/Account' +import { ClientPagingBase } from '../ClientPagingBase' + +/** + * Client for the account balance + */ +export class ClientAccountTxlistinternalTxhash extends ClientPagingBase implements IClientAccountTxlistInternalTxhash { + /** + * module of the etherscan api to request + */ + static module: string = 'account' + /** + * action of the etherscan api to request + */ + static action: string = 'txlistinternal' + /** + * Hash of the transaction + */ + txhash: string + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * Sort + */ + sort: Sort + /** + * Page + */ + paging?: Paging + + constructor( + txhash: string, + startblock: string, + endblock: string, + sort: Sort = new Sort(), + paging?: Paging, + ) { + super() + this.txhash = txhash + this.startblock = startblock + this.endblock = endblock + this.sort = sort + this.paging = paging + } + /** + * Generates a JSON representation + */ + toJson(): any { + const json = { + action: ClientAccountTxlistinternalTxhash.action, + endblock: this.endblock.toString(), + module: ClientAccountTxlistinternalTxhash.module, + sort: this.sort.toString(), + startblock: this.startblock.toString(), + txhash: this.txhash.toString(), + } + + return this.addPagingToJson(json) + } +} diff --git a/api/src/client/block/Getstakingreward.ts b/api/src/client/block/Getstakingreward.ts new file mode 100644 index 0000000..0dd3421 --- /dev/null +++ b/api/src/client/block/Getstakingreward.ts @@ -0,0 +1,38 @@ +import { PositiveNumber } from '../../entities/PositiveNumber' +import { IClientStakingGetstakingrewardRequest } from '../../interfaces/Block' +import { ClientBase } from '../ClientBase' +/** + * Client for the transaction getstatus + */ +export class ClientStakingGetstakingreward extends ClientBase implements IClientStakingGetstakingrewardRequest { + + /** + * module to call + */ + static module: string = 'block' + /** + * Action + */ + static action: string = 'getblockreward' + + /** + * Hash of the transaction on the ethereum network + */ + blockno: PositiveNumber + + constructor(blockno: PositiveNumber) { + super() + this.blockno = blockno + } + + /** + * Generates JSON for url generation + */ + toJson(): any { + return { + action: ClientStakingGetstakingreward.action, + blockno: this.blockno.toString(), + module: ClientStakingGetstakingreward.module, + } + } +} diff --git a/api/src/client/contract/Getabi.ts b/api/src/client/contract/Getabi.ts new file mode 100644 index 0000000..6b9374f --- /dev/null +++ b/api/src/client/contract/Getabi.ts @@ -0,0 +1,39 @@ +import { Address } from '../../entities/Address' +import { IClientContractGetabiRequest } from '../../interfaces/Contract' +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientContractGetabi extends ClientBase implements IClientContractGetabiRequest { + + /** + * module to call + */ + static module: string = 'contract' + /** + * Action + */ + static action: string = 'getabi' + + /** + * Address on the ethereum network + */ + address: Address + + constructor(address: Address) { + super() + this.address = address + } + + /** + * Generates JSON for url generation + */ + toJson(): any { + return { + action: ClientContractGetabi.action, + address: this.address.toString(), + module: ClientContractGetabi.module, + } + } +} diff --git a/api/src/client/contract/Getsource.ts b/api/src/client/contract/Getsource.ts new file mode 100644 index 0000000..fb3a442 --- /dev/null +++ b/api/src/client/contract/Getsource.ts @@ -0,0 +1,39 @@ +import { Address } from '../../entities/Address' +import { IClientContractGetabiRequest } from '../../interfaces/Contract' +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientContractGetsource extends ClientBase implements IClientContractGetabiRequest { + + /** + * module to call + */ + static module: string = 'contract' + /** + * Action + */ + static action: string = 'getsourcecode' + + /** + * Address on the ethereum network + */ + address: Address + + constructor(address: Address) { + super() + this.address = address + } + + /** + * Generates JSON for url generation + */ + toJson(): any { + return { + action: ClientContractGetsource.action, + address: this.address.toString(), + module: ClientContractGetsource.module, + } + } +} diff --git a/api/src/client/log/GetLogs.ts b/api/src/client/log/GetLogs.ts new file mode 100644 index 0000000..a694b5e --- /dev/null +++ b/api/src/client/log/GetLogs.ts @@ -0,0 +1,112 @@ +import { Address } from '../../entities/Address' +import { Operator } from '../../entities/Operator' +import { PositiveNumber } from '../../entities/PositiveNumber' +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientLogGetLogs extends ClientBase { + /** + * Module to call + */ + static module: string = 'log' + /** + * Action to call + */ + static action: string = 'getLogs' + /** + * Block to start reading + */ + fromBlock: PositiveNumber + + /** + * End reading here + */ + toBlock: PositiveNumber + /** + * Address to read from + */ + address: Address + /** + * List of Topics with max 32 bytes + */ + topics: string[] + /** + * Topic 0 to 1 Operator + */ + topic01opr?: Operator + /** + * Topic 0 to 2 Operator + */ + topic02opr?: Operator + /** + * Topic 1 to 2 Operator + */ + topic12opr?: Operator + /** + * Topic 2 to 3 Operator + */ + topic23opr?: Operator + /** + * Topic 0 to 3 Operator + */ + topic03opr?: Operator + /** + * Topic 1 to 3 Operator + */ + topic13opr?: Operator + + constructor( + fromBlock: PositiveNumber, + toBlock: PositiveNumber, + address: Address, + topics: string[], + topic01opr?: Operator, + topic12opr?: Operator, + topic23opr?: Operator, + topic02opr?: Operator, + topic03opr?: Operator, + topic13opr?: Operator) { + super() + this.fromBlock = fromBlock + this.toBlock = toBlock + this.address = address + this.topics = topics + this.topic01opr = topic01opr + this.topic12opr = topic12opr + this.topic23opr = topic23opr + this.topic02opr = topic02opr + this.topic03opr = topic03opr + this.topic13opr = topic13opr + } + /** + * Generate Json for url generation + */ + toJson(): any { + + const returnValue: any = { + action: ClientLogGetLogs.action, + address: this.address.toString(), + fromBlock: this.fromBlock.toString(), + module: ClientLogGetLogs.module, + toBlock: this.toBlock.toString(), + } + + this.topics.map((topic, index) => { + const indexName = 'topic' + index + returnValue[indexName] = topic.toString() + }) + /* tslint:disable:no-string-literal */ + if (!!this.topic01opr) { + returnValue['topic0_1_opr'] = this.topic01opr.toString() + } + if (!!this.topic12opr) { + returnValue['topic1_2_opr'] = this.topic12opr.toString() + } + if (!!this.topic02opr) { + returnValue['topic0_2_opr'] = this.topic02opr.toString() + } + /* tslint:enable:no-string-literal */ + return returnValue + } +} diff --git a/api/src/client/proxy/EthBlocknumber.ts b/api/src/client/proxy/EthBlocknumber.ts new file mode 100644 index 0000000..ce1d08f --- /dev/null +++ b/api/src/client/proxy/EthBlocknumber.ts @@ -0,0 +1,23 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthBlocknumber extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_Blocknumber' + /** + * Generates json output + */ + toJson(): any { + return { + action: ClientProxyEthBlocknumber.action, + module: ClientProxyEthBlocknumber.module, + } + } +} diff --git a/api/src/client/proxy/EthCall.ts b/api/src/client/proxy/EthCall.ts new file mode 100644 index 0000000..f63b283 --- /dev/null +++ b/api/src/client/proxy/EthCall.ts @@ -0,0 +1,47 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthCall extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_call' + + /** + * data of the call + */ + data: string + + /** + * Tag of the call address + */ + tag: string + /** + * Call destination and code + */ + to: string + + constructor(to: string, data: string, tag: string) { + super() + this.to = to + this.data = data + this.tag = tag + } + /** + * Generates Json output + */ + toJson(): any { + return { + action: ClientProxyEthCall.action, + data: this.data, + module: ClientProxyEthCall.module, + tag: this.tag, + to: this.to, + } + } +} diff --git a/api/src/client/proxy/EthEstimateGas.ts b/api/src/client/proxy/EthEstimateGas.ts new file mode 100644 index 0000000..fcecb40 --- /dev/null +++ b/api/src/client/proxy/EthEstimateGas.ts @@ -0,0 +1,48 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthEstimateGas extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_estimateGas' + + /** + * Address of contract + */ + to: string + + /** + * Call to make and estimate + */ + value: string + + /** + * gasPrice + */ + gasPrice: string + + constructor(to: string, value: string, gasPrice: string) { + super() + this.value = value + this.to = to + this.gasPrice = gasPrice + } + /** + * Generates json + */ + toJson(): any { + return { + action: ClientProxyEthEstimateGas.action, + gasPrice: this.gasPrice, + module: ClientProxyEthEstimateGas.module, + to: this.to, + value: this.value, + } + } +} diff --git a/api/src/client/proxy/EthGasPrice.ts b/api/src/client/proxy/EthGasPrice.ts new file mode 100644 index 0000000..7205c40 --- /dev/null +++ b/api/src/client/proxy/EthGasPrice.ts @@ -0,0 +1,23 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGasPrice extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_gasPrice' + /** + * Generates Json output + */ + toJson(): any { + return { + action: ClientProxyEthGasPrice.action, + module: ClientProxyEthGasPrice.module, + } + } +} diff --git a/api/src/client/proxy/EthGetBlockTransactionCountByNumber.ts b/api/src/client/proxy/EthGetBlockTransactionCountByNumber.ts new file mode 100644 index 0000000..9ee5586 --- /dev/null +++ b/api/src/client/proxy/EthGetBlockTransactionCountByNumber.ts @@ -0,0 +1,33 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetBlockTransactionCountByNumber extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getBlockTransactionCountByNumber' + /** + * Tag to lookup + */ + tag: string + + constructor(tag: string) { + super() + this.tag = tag + } + /** + * Generates json representation + */ + toJson(): any { + return { + action: ClientProxyEthGetBlockTransactionCountByNumber.action, + module: ClientProxyEthGetBlockTransactionCountByNumber.module, + tag: this.tag, + } + } +} diff --git a/api/src/client/proxy/EthGetCode.ts b/api/src/client/proxy/EthGetCode.ts new file mode 100644 index 0000000..f0e016b --- /dev/null +++ b/api/src/client/proxy/EthGetCode.ts @@ -0,0 +1,45 @@ +import { Address } from '../../entities/Address' +import { ClientBase } from '../ClientBase' + +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetCode extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + + /** + * Action to call + */ + static action: string = 'eth_getCode' + + /** + * Address on the blockchain + */ + address: Address + + /** + * tag + */ + tag: string + + constructor(address: Address, tag: string) { + super() + this.address = address + this.tag = tag + } + + /** + * Generates a json represenation + */ + toJson(): any { + return { + action: ClientProxyEthGetCode.action, + address: this.address.toString(), + module: ClientProxyEthGetCode.module, + tag: this.tag, + } + } +} diff --git a/api/src/client/proxy/EthGetStorageAt.ts b/api/src/client/proxy/EthGetStorageAt.ts new file mode 100644 index 0000000..3a32776 --- /dev/null +++ b/api/src/client/proxy/EthGetStorageAt.ts @@ -0,0 +1,48 @@ +import { Address } from '../../entities/Address' +import { ClientBase } from '../ClientBase' + +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetStorageAt extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getStorageAt' + /** + * Address to request + */ + address: Address + /** + * Tag to show + */ + tag: string + + /** + * position + */ + position: string + + constructor(address: Address, position: string, tag: string) { + super() + this.address = address + this.position = position + this.tag = tag + } + /** + * Generates a json representation + */ + toJson(): any { + return { + action: ClientProxyEthGetStorageAt.action, + address: this.address.toString(), + module: ClientProxyEthGetStorageAt.module, + postion: this.position, + tag: this.tag, + } + } +} diff --git a/api/src/client/proxy/EthGetTransactionByBlockNumberAndIndex.ts b/api/src/client/proxy/EthGetTransactionByBlockNumberAndIndex.ts new file mode 100644 index 0000000..b786dce --- /dev/null +++ b/api/src/client/proxy/EthGetTransactionByBlockNumberAndIndex.ts @@ -0,0 +1,41 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetTransactionByBlockNumberAndIndex extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getTransactionByBlockNumberAndIndex' + + /** + * Index + */ + index: string + + /** + * Tag + */ + tag: string + + constructor(index: string, tag: string) { + super() + this.index = index + this.tag = tag + } + /** + * Generates json representation + */ + toJson(): any { + return { + action: ClientProxyEthGetTransactionByBlockNumberAndIndex.action, + index: this.index, + module: ClientProxyEthGetTransactionByBlockNumberAndIndex.module, + tag: this.tag, + } + } +} diff --git a/api/src/client/proxy/EthGetTransactionCount.ts b/api/src/client/proxy/EthGetTransactionCount.ts new file mode 100644 index 0000000..13509e3 --- /dev/null +++ b/api/src/client/proxy/EthGetTransactionCount.ts @@ -0,0 +1,40 @@ +import { Address } from '../../entities/Address' +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetTransactionCount extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getTransactionCount' + /** + * Address of the transaction + */ + address: Address + /** + * tag + */ + tag: string + + constructor(address: Address, tag: string) { + super() + this.address = address + this.tag = tag + } + /** + * Generates json representation + */ + toJson(): any { + return { + action: ClientProxyEthGetTransactionCount.action, + address: this.address.toString(), + module: ClientProxyEthGetTransactionCount.module, + tag: this.tag, + } + } +} diff --git a/api/src/client/proxy/EthGetTxByHash.ts b/api/src/client/proxy/EthGetTxByHash.ts new file mode 100644 index 0000000..aa13cd2 --- /dev/null +++ b/api/src/client/proxy/EthGetTxByHash.ts @@ -0,0 +1,34 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetTransactionByHash extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getTransactionByHash' + + /** + * Txhash of the transaction + */ + txhash: string + + constructor(txhash: string) { + super() + this.txhash = txhash + } + /** + * Generates a json representation + */ + toJson(): any { + return { + action: ClientProxyEthGetTransactionByHash.action, + module: ClientProxyEthGetTransactionByHash.module, + txhash: this.txhash, + } + } +} diff --git a/api/src/client/proxy/EthGetUncleByBlockNumberAndIndex.ts b/api/src/client/proxy/EthGetUncleByBlockNumberAndIndex.ts new file mode 100644 index 0000000..70b2868 --- /dev/null +++ b/api/src/client/proxy/EthGetUncleByBlockNumberAndIndex.ts @@ -0,0 +1,42 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetUncleByBlockNumberAndIndex extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getUncleByBlockNumberAndIndex' + + /** + * Tag to lookup + */ + tag: string + + /** + * Index + */ + index: string + + constructor(index: string, tag: string) { + super() + this.index = index + this.tag = tag + } + + /** + * Generates Json representation + */ + toJson(): any { + return { + action: ClientProxyEthGetUncleByBlockNumberAndIndex.action, + index: this.index, + module: ClientProxyEthGetUncleByBlockNumberAndIndex.module, + tag: this.tag, + } + } +} diff --git a/api/src/client/proxy/EthGetblockByNumber.ts b/api/src/client/proxy/EthGetblockByNumber.ts new file mode 100644 index 0000000..19362c8 --- /dev/null +++ b/api/src/client/proxy/EthGetblockByNumber.ts @@ -0,0 +1,41 @@ +import { ClientBase } from '../ClientBase' +/** + * Client for the stats chainsize + */ +export class ClientProxyEthGetblockByNumber extends ClientBase { + /** + * Module to call + */ + static module: string = 'proxy' + /** + * Action to call + */ + static action: string = 'eth_getBlockByNumber' + + /** + * Tag to look up + */ + tag: string + + /** + * Boolean value + */ + boolean: boolean + + constructor(tag: string, bool: boolean) { + super() + this.tag = tag + this.boolean = bool + } + /** + * Generates JSON representation + */ + toJson(): any { + return { + action: ClientProxyEthGetblockByNumber.action, + boolean: this.boolean.toString(), + module: ClientProxyEthGetblockByNumber.module, + tag: this.tag, + } + } +} diff --git a/api/src/client/stats/Chainsize.ts b/api/src/client/stats/Chainsize.ts new file mode 100644 index 0000000..cb12c72 --- /dev/null +++ b/api/src/client/stats/Chainsize.ts @@ -0,0 +1,62 @@ +import { Clienttype } from '../../entities/Clienttype' +import { Syncmode } from '../../entities/Syncmode' +import { UsDate } from '../../entities/UsDate' +import { IClientStatsChainsizeRequest } from '../../interfaces/Stats' +import { ClientBase } from '../ClientBase' + +/** + * Client for the stats chainsize + */ +export class ClientStatsChainsize extends ClientBase implements IClientStatsChainsizeRequest { + /** + * Module to call + */ + static module: string = 'stats' + /** + * Action to call + */ + static action: string = 'chainsize' + /** + * Starting date + */ + startdate: UsDate + /** + * End date + */ + enddate: UsDate + /** + * Syncmode + */ + syncmode: Syncmode + /** + * Cloienttype + */ + clienttype: Clienttype + + constructor( + startdate: UsDate, + enddate: UsDate, + clienttype: Clienttype = new Clienttype(), + syncmode: Syncmode = new Syncmode(), + ) { + super() + this.startdate = startdate + this.enddate = enddate + this.syncmode = syncmode + this.clienttype = clienttype + } + + /** + * Generates a json representation + */ + toJson(): any { + return { + action: ClientStatsChainsize.action, + clienttype: this.clienttype.toString(), + enddate: this.enddate.toString(), + module: ClientStatsChainsize.module, + startdate: this.startdate.toString(), + syncmode: this.syncmode.toString(), + } + } +} diff --git a/api/src/client/stats/Ediprice.ts b/api/src/client/stats/Ediprice.ts new file mode 100644 index 0000000..a4cc1d0 --- /dev/null +++ b/api/src/client/stats/Ediprice.ts @@ -0,0 +1,31 @@ +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + * TODO: You could modularize this as a generic 'ERC20' + */ +export class ClientStatsEdiprice extends ClientBase { + /** + * module of the exchange api to request + */ + static module: string = 'stats' + /** + * action of the exchange api to request + * TODO: this is a generic implementation, exchanges typically don't have it set as 'ediprice' + * but rather a specific pairing price, e.g. 'EDI-USDT' etc. + */ + static action: string = 'ediprice' + + constructor() { + super() + } + /** + * generates a josn representation + */ + toJson(): any { + return { + action: ClientStatsEdiprice.action, + module: ClientStatsEdiprice.module, + } + } +} diff --git a/api/src/client/stats/Ethprice.ts b/api/src/client/stats/Ethprice.ts new file mode 100644 index 0000000..a455f1c --- /dev/null +++ b/api/src/client/stats/Ethprice.ts @@ -0,0 +1,28 @@ +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientStatsEthprice extends ClientBase { + /** + * module of the etherscan api to request + */ + static module: string = 'stats' + /** + * action of the etherscan api to request + */ + static action: string = 'ethprice' + + constructor() { + super() + } + /** + * generates a josn representation + */ + toJson(): any { + return { + action: ClientStatsEthprice.action, + module: ClientStatsEthprice.module, + } + } +} diff --git a/api/src/client/stats/Ethsupply.ts b/api/src/client/stats/Ethsupply.ts new file mode 100644 index 0000000..9cb9f5e --- /dev/null +++ b/api/src/client/stats/Ethsupply.ts @@ -0,0 +1,28 @@ +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientStatsEthsupply extends ClientBase { + /** + * module of the etherscan api to request + */ + static module: string = 'stats' + /** + * action of the etherscan api to request + */ + static action: string = 'ethsupply' + + constructor() { + super() + } + /** + * generates a josn representation + */ + toJson(): any { + return { + action: ClientStatsEthsupply.action, + module: ClientStatsEthsupply.module, + } + } +} diff --git a/api/src/client/stats/Tokencirculatingsupply.ts b/api/src/client/stats/Tokencirculatingsupply.ts new file mode 100644 index 0000000..4d7711a --- /dev/null +++ b/api/src/client/stats/Tokencirculatingsupply.ts @@ -0,0 +1,36 @@ +import { Address } from '../../entities/Address' +import { IClientStatsTokensupplyRequest } from '../../interfaces/Stats' +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientStatsTokencirculatingsupply extends ClientBase implements IClientStatsTokensupplyRequest { + /** + * module of the api to request + */ + static module: string = 'stats' + /** + * action of the api to request + */ + static action: string = 'tokencirculatingsupply' + /** + * Address of the contract + */ + contractaddress: string + + constructor(contractaddress: string) { + super() + this.contractaddress = contractaddress + } + /** + * generates a josn representation + */ + toJson(): any { + return { + action: ClientStatsTokencirculatingsupply.action, + contractaddress: this.contractaddress, + module: ClientStatsTokencirculatingsupply.module, + } + } +} diff --git a/api/src/client/stats/Tokensupply.ts b/api/src/client/stats/Tokensupply.ts new file mode 100644 index 0000000..335e495 --- /dev/null +++ b/api/src/client/stats/Tokensupply.ts @@ -0,0 +1,36 @@ +import { Address } from '../../entities/Address' +import { IClientStatsTokensupplyRequest } from '../../interfaces/Stats' +import { ClientBase } from '../ClientBase' + +/** + * Client for the account balance + */ +export class ClientStatsTokenupply extends ClientBase implements IClientStatsTokensupplyRequest { + /** + * module of the etherscan api to request + */ + static module: string = 'stats' + /** + * action of the etherscan api to request + */ + static action: string = 'tokensupply' + /** + * Address of the contract + */ + contractaddress: string + + constructor(contractaddress: string) { + super() + this.contractaddress = contractaddress + } + /** + * generates a josn representation + */ + toJson(): any { + return { + action: ClientStatsTokenupply.action, + contractaddress: this.contractaddress, + module: ClientStatsTokenupply.module, + } + } +} diff --git a/api/src/client/transaction/Getstatus.ts b/api/src/client/transaction/Getstatus.ts new file mode 100644 index 0000000..8611c93 --- /dev/null +++ b/api/src/client/transaction/Getstatus.ts @@ -0,0 +1,38 @@ +import { Address } from '../../entities/Address' +import { IClientTransactionGetstatusRequest } from '../../interfaces/Transaction' +import { ClientBase } from '../ClientBase' +/** + * Client for the transaction getstatus + */ +export class ClientTransactionGetstatus extends ClientBase implements IClientTransactionGetstatusRequest { + + /** + * module to call + */ + static module: string = 'transaction' + /** + * Action + */ + static action: string = 'getstatus' + + /** + * Hash of the transaction on the ethereum network + */ + txhash: string + + constructor(txhash: string) { + super() + this.txhash = txhash + } + + /** + * Generates JSON for url generation + */ + toJson(): any { + return { + action: ClientTransactionGetstatus.action, + module: ClientTransactionGetstatus.module, + txhash: this.txhash.toString(), + } + } +} diff --git a/api/src/client/transaction/Gettxreceiptstatus.ts b/api/src/client/transaction/Gettxreceiptstatus.ts new file mode 100644 index 0000000..ac40673 --- /dev/null +++ b/api/src/client/transaction/Gettxreceiptstatus.ts @@ -0,0 +1,38 @@ +import { Address } from '../../entities/Address' +import { IClientTransactionGetstatusRequest } from '../../interfaces/Transaction' +import { ClientBase } from '../ClientBase' +/** + * Client for the transaction getstatus + */ +export class ClientTransactionGettxreceiptstatus extends ClientBase implements IClientTransactionGetstatusRequest { + + /** + * module to call + */ + static module: string = 'transaction' + /** + * Action + */ + static action: string = 'gettxreceiptstatus' + + /** + * Hash of the transaction on the ethereum network + */ + txhash: string + + constructor(txhash: string) { + super() + this.txhash = txhash + } + + /** + * Generates JSON for url generation + */ + toJson(): any { + return { + action: ClientTransactionGettxreceiptstatus.action, + module: ClientTransactionGettxreceiptstatus.module, + txhash: this.txhash.toString(), + } + } +} diff --git a/api/src/entities/Address.ts b/api/src/entities/Address.ts new file mode 100644 index 0000000..4024b77 --- /dev/null +++ b/api/src/entities/Address.ts @@ -0,0 +1,21 @@ + +import { IEntity } from '../interfaces/Entity' +import { EntityBase } from './EntityBase' + +/** + * A ethereum address + */ +export class Address extends EntityBase implements IEntity { + + constructor(address: string) { + super(address) + this.errorMessage = 'Invalid Address' + } + /** + * Checks validity of the address + */ + valid(): boolean { + const regex = /0x[a-fA-F0-9]{40}/ + return this.value.match(regex) !== null + } +} diff --git a/api/src/entities/Apikey.ts b/api/src/entities/Apikey.ts new file mode 100644 index 0000000..d4f053a --- /dev/null +++ b/api/src/entities/Apikey.ts @@ -0,0 +1,26 @@ + +import { IEntity } from '../interfaces/Entity' +import { EntityBase } from './EntityBase' + +/** + * Api key for etherscan.io + */ +export class ApiKey extends EntityBase implements IEntity { + /** + * Error Message for validation errors + */ + errorMessage: string = 'invalid' + /** + * Key lenght of a apiKey + */ + private keyLength: number = 34 + constructor(apiKey: string) { + super(apiKey) + } + /** + * Checks validty of a apiKey + */ + valid(): boolean { + return this.value.length === this.keyLength + } +} diff --git a/api/src/entities/Blocktype.ts b/api/src/entities/Blocktype.ts new file mode 100644 index 0000000..0791e92 --- /dev/null +++ b/api/src/entities/Blocktype.ts @@ -0,0 +1,24 @@ + +import { IEntity } from '../interfaces/Entity' +import { blocktypes } from '../parameters/blocktypes' +import { EntityBase } from './EntityBase' + +/** + * Blocktypes: blocks or uncles + */ +export class Blocktype extends EntityBase implements IEntity { + /** + * Error message to display when the value is invalid + */ + errorMessage: string = 'invalid' + + constructor(sortParam: string) { + super(sortParam) + } + /** + * Validates if the error is invalid + */ + valid(): boolean { + return blocktypes.uncles === this.value || blocktypes.blocks === this.value + } +} diff --git a/api/src/entities/Clienttype.ts b/api/src/entities/Clienttype.ts new file mode 100644 index 0000000..fce5207 --- /dev/null +++ b/api/src/entities/Clienttype.ts @@ -0,0 +1,17 @@ +import { clienttypes } from '../parameters/clienttypes' +import { EntityBase } from './EntityBase' + +/** + * Value Object for the Clienttype + */ +export class Clienttype extends EntityBase { + constructor(name: string = 'geth') { + super(name) + } + /** + * Chgecks of the value is valid + */ + valid(): boolean { + return Object.keys(clienttypes).includes(this.value) + } +} diff --git a/api/src/entities/Contractaddress.ts b/api/src/entities/Contractaddress.ts new file mode 100644 index 0000000..a7f0939 --- /dev/null +++ b/api/src/entities/Contractaddress.ts @@ -0,0 +1,21 @@ + +import { IEntity } from '../interfaces/Entity' +import { EntityBase } from './EntityBase' + +/** + * A ethereum address + */ +export class Contractaddress extends EntityBase implements IEntity { + + constructor(address: string) { + super(address) + this.errorMessage = 'Invalid Address' + } + /** + * Checks validity of the address + */ + valid(): boolean { + const regex = /0x[a-fA-F0-9]{40}/ + return this.value.match(regex) !== null + } +} diff --git a/api/src/entities/EntityBase.ts b/api/src/entities/EntityBase.ts new file mode 100644 index 0000000..7aaff46 --- /dev/null +++ b/api/src/entities/EntityBase.ts @@ -0,0 +1,39 @@ + +/** + * base class for entities + */ +export abstract class EntityBase { + /** + * Value of the entity + */ + protected value: any + /** + * Basic error message for exceptions on validations + */ + protected errorMessage: string + + constructor(value: any) { + this.value = value + this.errorMessage = 'invalid value' + } + /** + * checks valid() and throws errorMessage + */ + validate(): void { + if (!this.valid()) { + throw new Error(this.errorMessage) + } + } + /** + * converts the entity to a string + */ + toString(): string { + return this.value.toString() + } + /** + * validates the entity + */ + valid(): boolean { + return true + } +} diff --git a/api/src/entities/HexQuantity.ts b/api/src/entities/HexQuantity.ts new file mode 100644 index 0000000..eae0cf7 --- /dev/null +++ b/api/src/entities/HexQuantity.ts @@ -0,0 +1,23 @@ + +import { IEntity } from '../interfaces/Entity' +import { EntityBase } from './EntityBase' +/** + * Hex representation of a quantity + * @see https://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hexadecimal-in-javascript + */ +export class HexQuantity extends EntityBase implements IEntity { + /** + * Error Message + */ + errorMessage: string = 'invalid' + constructor(hexString: string) { + super(hexString) + } + /** + * Validates a hex quantity + */ + valid(): boolean { + const a = parseInt(this.value, 16) + return (a.toString(16) === this.value) + } +} diff --git a/api/src/entities/Network.ts b/api/src/entities/Network.ts new file mode 100644 index 0000000..ae620e2 --- /dev/null +++ b/api/src/entities/Network.ts @@ -0,0 +1,24 @@ +import { networks } from '../parameters/networks' +import { EntityBase } from './EntityBase' + +/** + * Value Object for the Networks that are out there + */ +export class Network extends EntityBase { + constructor(name: string = 'homestead') { + super(name) + } + /** + * Chgecks of the value is valid + */ + valid(): boolean { + return Object.keys(networks).includes(this.value) + } + /** + * Gets the base url for each API + */ + toUrl() { + const enumVal: string = (networks as any)[this.value] as string + return enumVal + } +} diff --git a/api/src/entities/Operator.ts b/api/src/entities/Operator.ts new file mode 100644 index 0000000..46471e7 --- /dev/null +++ b/api/src/entities/Operator.ts @@ -0,0 +1,17 @@ +import { operand } from '../parameters/operand' +import { EntityBase } from './EntityBase' + +/** + * Value Object for the Networks that are out there + */ +export class Operator extends EntityBase { + constructor(operandString: string = 'and') { + super(operandString) + } + /** + * Chgecks of the value is valid + */ + valid(): boolean { + return Object.keys(operand).includes(this.value) + } +} diff --git a/api/src/entities/Paging.ts b/api/src/entities/Paging.ts new file mode 100644 index 0000000..2a4f2df --- /dev/null +++ b/api/src/entities/Paging.ts @@ -0,0 +1,49 @@ +import { EntityBase } from './EntityBase' +import { PositiveNumber } from './PositiveNumber' + +/** + * A simple paging class + */ +export class Paging extends EntityBase { + /** + * Page we are using + */ + private page: PositiveNumber + /** + * offset + */ + private offset: PositiveNumber + + constructor( + page: PositiveNumber = new PositiveNumber(1), + offset: PositiveNumber = new PositiveNumber(10), + ) { + super(page.toString()) + this.page = page + this.offset = offset + } + /** + * Validates the paging + */ + valid(): boolean { + return !!this.page && !!this.offset + } + /** + * Generates a string representing the data for paging + */ + toString(): string { + return `Page: ${this.page.toString()}, Offset: ${this.offset.toString()}` + } + + /** + * Generates an object representing the sort parameters + */ + toJson(): any { + const page = this.page.toString() + const offset = this.offset.toString() + return { + offset, + page, + } + } +} diff --git a/api/src/entities/PositiveNumber.ts b/api/src/entities/PositiveNumber.ts new file mode 100644 index 0000000..3bb31f6 --- /dev/null +++ b/api/src/entities/PositiveNumber.ts @@ -0,0 +1,31 @@ + +import { IEntity } from '../interfaces/Entity' +import { EntityBase } from './EntityBase' + +/** + * a positive number entity + */ +export class PositiveNumber extends EntityBase implements IEntity { + /** + * Error Message on validation + */ + errorMessage: string + + constructor(positiveNumber: any) { + super(positiveNumber) + this.errorMessage = 'invalid number' + this.value = positiveNumber + } + /** + * Validates if the value is a positive number + */ + valid(): boolean { + + const numerical = parseInt(this.value, 3) + + if (numerical < 0) { + return false + } + return true + } +} diff --git a/api/src/entities/Sort.ts b/api/src/entities/Sort.ts new file mode 100644 index 0000000..fb28641 --- /dev/null +++ b/api/src/entities/Sort.ts @@ -0,0 +1,24 @@ +import { IEntity } from '../interfaces/Entity' +import { sort } from '../parameters/sort' +import { EntityBase } from './EntityBase' + +/** + * Sort Parameter asc or desc + */ +export class Sort extends EntityBase implements IEntity { + /** + * Error message to display when the value is invalid + */ + errorMessage: string = 'invalid' + + constructor(sortParam: string = 'desc') { + super(sortParam) + + } + /** + * Validates if the error is invalid + */ + valid(): boolean { + return sort.asc === this.value || sort.desc === this.value + } +} diff --git a/api/src/entities/Syncmode.ts b/api/src/entities/Syncmode.ts new file mode 100644 index 0000000..6379cf1 --- /dev/null +++ b/api/src/entities/Syncmode.ts @@ -0,0 +1,17 @@ +import { syncmodes } from '../parameters/syncmodes' +import { EntityBase } from './EntityBase' + +/** + * Value Object for the Syncmode + */ +export class Syncmode extends EntityBase { + constructor(name: string = 'default') { + super(name) + } + /** + * Chgecks of the value is valid + */ + valid(): boolean { + return Object.keys(syncmodes).includes(this.value) + } +} diff --git a/api/src/entities/UsDate.ts b/api/src/entities/UsDate.ts new file mode 100644 index 0000000..250b0bc --- /dev/null +++ b/api/src/entities/UsDate.ts @@ -0,0 +1,22 @@ +import { IEntity } from '../interfaces/Entity' +import { EntityBase } from './EntityBase' + +/** + * Sort Parameter asc or desc + */ +export class UsDate extends EntityBase implements IEntity { + /** + * Error message to display when the value is invalid + */ + errorMessage: string = 'invalid date supplied' + + constructor(dateString: string) { + super(dateString) + } + /** + * Validates if the error is invalid + */ + valid(): boolean { + return !!Date.parse(this.value) + } +} diff --git a/api/src/interfaces/Account.ts b/api/src/interfaces/Account.ts new file mode 100644 index 0000000..b0fe413 --- /dev/null +++ b/api/src/interfaces/Account.ts @@ -0,0 +1,143 @@ +import { Address } from '../entities/Address' +import { Contractaddress } from '../entities/Contractaddress' +import { Paging } from '../entities/Paging' +import { Sort } from '../entities/Sort' + +/** + * Interface to the Account/Balance api + */ +export interface IClientAccountBalanceRequest { + /** + * Ethereum address + */ + address: Address + /** + * Tag to limit results + */ + tag: string +} + +/** + * Interface to the Account/Balance api + */ +export interface IClientAccountTokenbalanceRequest { + /** + * Ethereum address + */ + address: Address + + /** + * Ethereum contract address + */ + contractaddress: Address +} + +/** + * Interface to Account/Balancemulti + */ +export interface IClientAccountBalanceMultiRequest { + /** + * Array of ethereum addresses + */ + address: Address[] + /** + * Tag to limit results + */ + tag: string +} + +/** + * Interface to Account/txlist and Txlistinternal + */ +export interface IClientAccountTxlistRequest { + /** + * Ethereum address + */ + address: Address + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * Paging actual page + */ + paging?: Paging + /** + * Sort Parameter + */ + sort: Sort +} + +export interface IClientAccountTxlistInternalTxhash { + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * hash of the transaction + */ + txhash: string + /** + * Paging actual page + */ + paging?: Paging + /** + * Sort Parameter + */ + sort: Sort +} + +export interface IClientAccountTokentx { + /** + * Ethereum address + */ + address?: Address | Contractaddress + /** + * Block to start reading data + */ + startblock: string + /** + * Read data to + */ + endblock: string + /** + * Paging actual page + */ + paging?: Paging + + /** + * Sort Parameter + */ + sort: Sort +} + +export interface IClientAccountGetminedblocks { + /** + * Ethereum address + */ + address?: Address | Contractaddress + /** + * Type of block (block/uncle) + */ + type: string + /** + * Paging actual page + */ + page?: string + /** + * Paging start + */ + offset?: string + /** + * Sort Parameter + */ + sort?: string +} diff --git a/api/src/interfaces/Block.ts b/api/src/interfaces/Block.ts new file mode 100644 index 0000000..b0459e3 --- /dev/null +++ b/api/src/interfaces/Block.ts @@ -0,0 +1,11 @@ +import { PositiveNumber } from '../entities/PositiveNumber' + +/** + * Interface to the Account/Balance api + */ +export interface IClientStakingGetstakingrewardRequest { + /** + * Blocknumber, this is to check against the epochs + */ + blockno: PositiveNumber +} diff --git a/api/src/interfaces/Contract.ts b/api/src/interfaces/Contract.ts new file mode 100644 index 0000000..dc213ff --- /dev/null +++ b/api/src/interfaces/Contract.ts @@ -0,0 +1,20 @@ +import { Address } from '../entities/Address' +/** + * Interface for requests to contract/getabi + */ +export interface IClientContractGetabiRequest { + /** + * Ethereum address + */ + address: Address +} + +/** + * Interface for requests to contract/getsourcecode + */ +export interface IClientContractGetsourcecodeRequest { + /** + * Ethereum address + */ + address: Address +} diff --git a/api/src/interfaces/Entity.ts b/api/src/interfaces/Entity.ts new file mode 100644 index 0000000..c7a89e1 --- /dev/null +++ b/api/src/interfaces/Entity.ts @@ -0,0 +1,13 @@ +/** + * Basic Interface for an entitiy + */ +export interface IEntity { + /** + * Validatees a entitiy and throws on error + */ + validate(): void + /** + * Checks validity + */ + valid(): boolean +} diff --git a/api/src/interfaces/Stats.ts b/api/src/interfaces/Stats.ts new file mode 100644 index 0000000..c08d938 --- /dev/null +++ b/api/src/interfaces/Stats.ts @@ -0,0 +1,36 @@ +import { Clienttype } from '../entities/Clienttype' +import { Syncmode } from '../entities/Syncmode' +import { UsDate } from '../entities/UsDate' + +/** + * Interface to the Stats/Tokensupply api + */ +export interface IClientStatsTokensupplyRequest { + /** + * Contract address + */ + contractaddress: string +} + +export interface IClientStatsChainsizeRequest { + + /** + * Chain startdate + */ + startdate: UsDate + + /** + * Chain enddate + */ + enddate: UsDate + + /** + * Type of client producing the stats + */ + clienttype: Clienttype + + /** + * Syncmode + */ + syncmode: Syncmode +} diff --git a/api/src/interfaces/Transaction.ts b/api/src/interfaces/Transaction.ts new file mode 100644 index 0000000..e9e4e8f --- /dev/null +++ b/api/src/interfaces/Transaction.ts @@ -0,0 +1,13 @@ +export interface IClientTransactionGetstatusRequest { + /** + * hash of the transaction + */ + txhash: string +} + +export interface IClientTransactionGettxreceiptRequest { + /** + * hash of the transaction + */ + txhash: string +} diff --git a/api/src/modules.ts b/api/src/modules.ts new file mode 100644 index 0000000..49f6bb3 --- /dev/null +++ b/api/src/modules.ts @@ -0,0 +1,11 @@ +import { mapFromArray } from './util/mapFromArray' +const moduleNames = [ + 'account', + 'contract', + 'transaction', + 'block', + 'logs', + 'proxy', + 'stats', +] +export const modules = mapFromArray(moduleNames) diff --git a/api/src/parameters/blocktypes.ts b/api/src/parameters/blocktypes.ts new file mode 100644 index 0000000..733a8c9 --- /dev/null +++ b/api/src/parameters/blocktypes.ts @@ -0,0 +1 @@ +export enum blocktypes {blocks = 'blocks', uncles = 'uncles'} diff --git a/api/src/parameters/booleans.ts b/api/src/parameters/booleans.ts new file mode 100644 index 0000000..26f824e --- /dev/null +++ b/api/src/parameters/booleans.ts @@ -0,0 +1 @@ +export enum booleans { true = 'true', false = 'false'} diff --git a/api/src/parameters/clienttypes.ts b/api/src/parameters/clienttypes.ts new file mode 100644 index 0000000..a6649b1 --- /dev/null +++ b/api/src/parameters/clienttypes.ts @@ -0,0 +1 @@ +export enum clienttypes { geth = 'geth', parity = 'parity'} diff --git a/api/src/parameters/networks.ts b/api/src/parameters/networks.ts new file mode 100644 index 0000000..979d09a --- /dev/null +++ b/api/src/parameters/networks.ts @@ -0,0 +1,6 @@ +export enum networks { + ropsten= 'https://api-ropsten.etherscan.io', + kovan= 'https://api-kovan.etherscan.io', + rinkeby= 'https://api-rinkeby.etherscan.io', + homestead= 'https://api.etherscan.io', +} diff --git a/api/src/parameters/operand.ts b/api/src/parameters/operand.ts new file mode 100644 index 0000000..e74e076 --- /dev/null +++ b/api/src/parameters/operand.ts @@ -0,0 +1 @@ +export enum operand {and = 'and', or = 'or'} diff --git a/api/src/parameters/sort.ts b/api/src/parameters/sort.ts new file mode 100644 index 0000000..7f0c6e3 --- /dev/null +++ b/api/src/parameters/sort.ts @@ -0,0 +1 @@ +export enum sort {asc = 'asc', desc = 'desc'} diff --git a/api/src/parameters/syncmodes.ts b/api/src/parameters/syncmodes.ts new file mode 100644 index 0000000..e951528 --- /dev/null +++ b/api/src/parameters/syncmodes.ts @@ -0,0 +1 @@ +export enum syncmodes { default = 'default', archive = 'archive'} diff --git a/api/src/requestUrlBuilder.ts b/api/src/requestUrlBuilder.ts new file mode 100644 index 0000000..9229f0a --- /dev/null +++ b/api/src/requestUrlBuilder.ts @@ -0,0 +1,50 @@ + +import { encode } from 'querystring' +import { + account, + block, + contract, + logs, + proxy, + stats, + tokens, + transaction, +} from './actions/index' +import { Network } from './entities/Network' +import { modules } from './modules' + +const actions = new Map() +actions.set('account', account) +actions.set('block', block) +actions.set('contract', contract) +actions.set('logs', logs) +actions.set('proxy', proxy) +actions.set('stats', stats) +actions.set('tokens', tokens) +actions.set('transaction', transaction) + +export const requestUrlBuilder = ( + chain: Network = new Network(), + module: string, + action: string, + params?: object): string => { + + const base = chain.toUrl() + + if (!modules.get(module)) { + throw Error('unknown module') + } + + if (!actions.get(module).get(action)) { + throw Error('unknown action') + } + + const baseParams = { + action, + module, + } + + const toEncodeParams = Object.assign(baseParams, params) + const query: string = encode(toEncodeParams) + return `${base}/api?${query}` +} diff --git a/api/src/util/mapFromArray.ts b/api/src/util/mapFromArray.ts new file mode 100644 index 0000000..ce06860 --- /dev/null +++ b/api/src/util/mapFromArray.ts @@ -0,0 +1,8 @@ +/** + * easily create js maps from simple arrays + */ +export function mapFromArray(arr: any[]) { + const map = new Map() + arr.map((name) => map.set(name, name)) + return map +} diff --git a/api/src/util/performRequest.ts b/api/src/util/performRequest.ts new file mode 100644 index 0000000..128a512 --- /dev/null +++ b/api/src/util/performRequest.ts @@ -0,0 +1,36 @@ +import { Network } from '../entities/Network' +import { requestUrlBuilder } from '../requestUrlBuilder' + +export const performRequest = ( + network: Network, + module: string, + action: string, + params: any, + timeout: number = 3000) => { + + const url = requestUrlBuilder( + network, + module, + action, + params, + ) + const timer = new Promise((resolve) => { + setTimeout(resolve, timeout, { + timeout: true, + }) + }) + return Promise.race([ + fetch(url), + timer, + ]).then((response: any) => { + if (response.timeout) { + throw new Error('Request timeout') + } + return response + }).then((response: any) => { + if (response.status === 500) { + throw new Error('Internal server error') + } + return response + }) +} diff --git a/api/src/version.ts b/api/src/version.ts new file mode 100644 index 0000000..d7c05cd --- /dev/null +++ b/api/src/version.ts @@ -0,0 +1 @@ +export const VERSION: string = '100.0.1' diff --git a/api/tsconfig.json b/api/tsconfig.json new file mode 100644 index 0000000..0edeca4 --- /dev/null +++ b/api/tsconfig.json @@ -0,0 +1,17 @@ +{ + "exclude": [ + "node_modules", + "dist", + "test" + ], + "compilerOptions": { + "incremental": true, /* Enable incremental compilation */ + "target": "es6", + "module": "commonjs", + "lib": ["es2017", "es7", "dom"], + "outDir": "./dist", /* Redirect output structure to the directory. */ + "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "strict": true, /* Enable all strict type-checking options. */ + "esModuleInterop": true + } +} diff --git a/api/tslint.json b/api/tslint.json new file mode 100644 index 0000000..f898afb --- /dev/null +++ b/api/tslint.json @@ -0,0 +1,17 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "semicolon": [true, "never"], + "indent": [true, "spaces", 4], + "quotemark": [true, "single", "avoid-escape"], + "member-access": [true, "no-public"], + "completed-docs": true + }, + "declaration": true, + "rulesDirectory": [] + +} \ No newline at end of file diff --git a/api/typedoc.json b/api/typedoc.json new file mode 100644 index 0000000..7f2e6c0 --- /dev/null +++ b/api/typedoc.json @@ -0,0 +1,4 @@ +{ + "mode": "file", + "out": "docs" +}