├── VERSION ├── log └── .keep ├── views ├── home.handlebars ├── partials │ ├── ui │ │ ├── time.handlebars │ │ ├── transactionFee.handlebars │ │ ├── noResults.handlebars │ │ ├── address.handlebars │ │ ├── transactionHash.handlebars │ │ ├── nextPreviousTemplate.handlebars │ │ ├── transactionTable.handlebars │ │ └── searchTemplates.handlebars │ ├── pageTitle.handlebars │ ├── address.handlebars │ ├── blockDetailsDataTable.handlebars │ ├── addressDetailsDataTable.handlebars │ ├── homeSummary.handlebars │ ├── details.handlebars │ ├── tokenSummary.handlebars │ ├── tokenAddressDetailsDataTable.handlebars │ └── topHeader.handlebars ├── tokenDetails.handlebars ├── layouts │ ├── empty.handlebars │ ├── main.handlebars │ └── errorPage.handlebars ├── addressDetails.handlebars ├── about.handlebars ├── tokenAddressDetails.handlebars └── blockDetail.handlebars ├── assets ├── js │ ├── mAddressDetails.js │ ├── mTokenAddressDetails.js │ ├── mTransactionDetails.js │ ├── mHome.js │ ├── mBlockDetails.js │ ├── mTokenDetails.js │ ├── mCommon.js │ ├── addressDetails │ │ └── addressDetails.js │ ├── tokenAddressDetails │ │ └── tokenAddressDetails.js │ ├── blockDetails │ │ └── blockDetails.js │ ├── tokenDetails │ │ └── tokenDetails.js │ ├── home │ │ └── home.js │ ├── plugins │ │ └── jquery-visible │ │ │ ├── LICENSE.txt │ │ │ └── README.markdown │ └── common │ │ └── TokenTable.js └── css │ ├── home │ └── home.scss │ ├── mAddressDetails.css │ ├── mBlockDetails.css │ ├── mTokenAddressDetails.css │ ├── mTransactionDetails.css │ ├── mTokenDetails.css │ ├── mHome.css │ ├── mCommon.css │ ├── common │ ├── pageTitle.scss │ ├── graphComponent.scss │ ├── common.scss │ ├── icons.scss │ ├── tableComponent.scss │ ├── header.scss │ └── table.scss │ ├── blockDetails │ └── blockDetails.scss │ ├── addressDetails │ └── addressDetails.scss │ ├── tokenAddressDetails │ └── tokenAddressDetails.scss │ ├── tokenDetails │ └── tokenDetails.scss │ └── transactionDetails │ └── transactionDetails.scss ├── .prettierrc.json ├── lib ├── globalConstant │ ├── storage.js │ ├── placeHolders.js │ ├── cacheManagement.js │ ├── canonical.js │ └── baseRoutes.js ├── Base64 │ └── helper.js ├── formatter │ ├── entities │ │ ├── Base.js │ │ ├── address.js │ │ ├── tokenHolderSearch.js │ │ ├── tokenNameSearch.js │ │ ├── tokenHolder.js │ │ ├── block.js │ │ ├── tokenTransfer.js │ │ ├── token.js │ │ └── transaction.js │ ├── config.js │ └── response.js ├── providers │ ├── blockScanner.js │ ├── inMemoryCache.js │ ├── storage.js │ └── cache.js ├── jwt │ └── jwt_auth.js ├── Authentication │ └── jwt.js ├── models │ └── tableCreation.js ├── validators │ └── Common.js ├── cacheManagement │ ├── TopTokensCache.js │ └── HomePageStats.js ├── cacheMultiManagement │ └── BaseCurrency.js └── contractInteract │ └── contractDecoder.js ├── config └── error │ ├── general.js │ └── param.js ├── helpers ├── customMiddleware.js ├── sanitizer.js └── basic.js ├── .gitignore ├── routes ├── helper.js ├── search.js ├── about.js ├── stats.js ├── index.js ├── tokenDetailsBySymbol.js └── block.js ├── app └── services │ ├── home │ ├── GetHomePageStats.js │ ├── GetDetails.js │ └── TopTokens.js │ ├── transfer │ └── GetDetails.js │ ├── address │ └── GetBasicDetails.js │ ├── block │ └── ChainIds.js │ ├── contract │ └── GetDetailsBySymbol.js │ └── transaction │ └── GetDetails.js ├── devops └── flushCache.js ├── executables ├── upload_assets_s3.js ├── GlobalAggregatorCron.js └── finalizer.js ├── package.json ├── contracts └── abi │ └── ERC20Token.abi ├── README.md └── .eslintrc.js /VERSION: -------------------------------------------------------------------------------- 1 | 2.0.0 -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/home.handlebars: -------------------------------------------------------------------------------- 1 | {{>homeSummary}} 2 | {{>homeDataTable}} -------------------------------------------------------------------------------- /views/partials/ui/time.handlebars: -------------------------------------------------------------------------------- 1 | [[toDate {{timestampKey}}]] -------------------------------------------------------------------------------- /assets/js/mAddressDetails.js: -------------------------------------------------------------------------------- 1 | //= require addressDetails/addressDetails.js 2 | -------------------------------------------------------------------------------- /assets/js/mTokenAddressDetails.js: -------------------------------------------------------------------------------- 1 | //= require tokenAddressDetails/tokenAddressDetails.js 2 | -------------------------------------------------------------------------------- /assets/js/mTransactionDetails.js: -------------------------------------------------------------------------------- 1 | //= require transactionDetails/transactionDetails.js 2 | -------------------------------------------------------------------------------- /assets/js/mHome.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 26/02/18. 3 | */ 4 | 5 | //= require home/home.js 6 | -------------------------------------------------------------------------------- /assets/css/home/home.scss: -------------------------------------------------------------------------------- 1 | #homeTopTokens{ 2 | thead { 3 | th{ 4 | height: 30px; 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /views/partials/ui/transactionFee.handlebars: -------------------------------------------------------------------------------- 1 | 2 | [[getTXFee gasUsed gasPrice]] 3 | -------------------------------------------------------------------------------- /assets/css/mAddressDetails.css: -------------------------------------------------------------------------------- 1 | /*= require common/tableComponent.scss */ 2 | /*= require addressDetails/addressDetails.scss */ 3 | -------------------------------------------------------------------------------- /assets/js/mBlockDetails.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 06/02/18. 3 | */ 4 | 5 | //= require blockDetails/blockDetails.js 6 | -------------------------------------------------------------------------------- /assets/css/mBlockDetails.css: -------------------------------------------------------------------------------- 1 | /*= require common/tableComponent.scss */ 2 | /*= require blockDetails/blockDetails.scss */ 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /views/tokenDetails.handlebars: -------------------------------------------------------------------------------- 1 | {{>pageTitle title="Token Details"}} 2 | {{>details}} 3 | {{>tokenSummary}} 4 | {{>tokenDetailsDataTable}} 5 | -------------------------------------------------------------------------------- /assets/css/mTokenAddressDetails.css: -------------------------------------------------------------------------------- 1 | /*= require common/tableComponent.scss */ 2 | /*= require tokenAddressDetails/tokenAddressDetails.scss */ 3 | -------------------------------------------------------------------------------- /assets/css/mTransactionDetails.css: -------------------------------------------------------------------------------- 1 | /*= require common/tableComponent.scss */ 2 | /*= require transactionDetails/transactionDetails.scss */ 3 | 4 | -------------------------------------------------------------------------------- /assets/css/mTokenDetails.css: -------------------------------------------------------------------------------- 1 | /*= require common/tableComponent.scss */ 2 | /*= require common/graphComponent.scss */ 3 | /*= require tokenDetails/tokenDetails.scss */ 4 | -------------------------------------------------------------------------------- /assets/css/mHome.css: -------------------------------------------------------------------------------- 1 | /*= require common/tableComponent.scss */ 2 | /*= require common/graphComponent.scss */ 3 | /*= require tokenDetails/tokenDetails.scss */ 4 | /*= require home/home.scss */ -------------------------------------------------------------------------------- /assets/js/mTokenDetails.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 06/02/18. 3 | */ 4 | 5 | //= require common/TokenTable.js 6 | //= require common/GoogleCharts.js 7 | //= require tokenDetails/tokenDetails.js 8 | -------------------------------------------------------------------------------- /views/partials/ui/noResults.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
No {{#if noun}}{{noun}}{{else}}results{{/if}} found!
3 |
4 | -------------------------------------------------------------------------------- /assets/css/mCommon.css: -------------------------------------------------------------------------------- 1 | /*= require plugins/jquery.dataTables/jquery.dataTables.scss */ 2 | /*= require common/common.scss */ 3 | /*= require common/pageTitle.scss */ 4 | /*= require common/header.scss */ 5 | /*= require common/icons.scss */ 6 | /*= require common/table.scss */ 7 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "parser": "flow", 11 | "proseWrap": "preserve" 12 | } 13 | -------------------------------------------------------------------------------- /views/partials/pageTitle.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

{{title}}

6 |
7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /views/partials/ui/address.handlebars: -------------------------------------------------------------------------------- 1 | [[#when {{fromTo}}Address '!=' '0x0000000000000000000000000000000000000000']] 2 | [[{{fromTo}}Address]] 3 | [[/when]] 4 | [[#when {{fromTo}}Address '==' '0x0000000000000000000000000000000000000000']] 5 | [[{{fromTo}}Address]] 6 | [[/when]] 7 | -------------------------------------------------------------------------------- /views/partials/address.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{#if toContractAddress}} 3 | Contract Deployed 4 | {{else if address}} 5 | {{address}} 6 | {{/if}} 7 | -------------------------------------------------------------------------------- /lib/globalConstant/storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Storage constants 4 | * 5 | * @module lib/globalConstant/storage 6 | */ 7 | class StorageConstants { 8 | constructor() {} 9 | 10 | get shared() { 11 | return 'shared'; 12 | } 13 | 14 | get sharded() { 15 | return 'sharded'; 16 | } 17 | } 18 | 19 | module.exports = new StorageConstants(); 20 | -------------------------------------------------------------------------------- /config/error/general.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * This file has the general error config. 4 | * 5 | * @module config/error/general 6 | */ 7 | const generalErrorConfig = { 8 | something_went_wrong: { 9 | http_code: '500', 10 | code: 'INTERNAL_SERVER_ERROR', 11 | message: 'Something went wrong.' 12 | } 13 | }; 14 | 15 | module.exports = generalErrorConfig; 16 | -------------------------------------------------------------------------------- /views/partials/ui/transactionHash.handlebars: -------------------------------------------------------------------------------- 1 | {{#when transactionHash '!=' '0x0000000000000000000000000000000000000000'}} 2 | [[transactionHash]] 3 | {{/when}} 4 | {{#when transactionHash '==' '0x0000000000000000000000000000000000000000'}} 5 | [[transactionHash]] 6 | {{/when}} -------------------------------------------------------------------------------- /lib/globalConstant/placeHolders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Url place holders 4 | * 5 | * @module lib/globalConstant/placeHolders 6 | */ 7 | class PlaceHolder { 8 | constructor() {} 9 | 10 | addressPlaceHolderUrl(chainId) {} 11 | 12 | blockPlaceHolderUrl(chainId) {} 13 | 14 | tokenDetailsReDirectUrl(chainId) {} 15 | } 16 | 17 | module.exports = new PlaceHolder(); 18 | -------------------------------------------------------------------------------- /assets/css/common/pageTitle.scss: -------------------------------------------------------------------------------- 1 | .pageTitleBackgroundColor { 2 | background-color: #e2f4f6; 3 | } 4 | 5 | .pageTitleBottomPadding { 6 | padding-bottom: 1px; 7 | } 8 | 9 | .page_title_top_border { 10 | 11 | border-top: 1.5px solid rgb(220, 217, 170); 12 | } 13 | 14 | .page_title_text_color { 15 | color: #438bad; 16 | } 17 | 18 | .page_title_margine{ 19 | margin: 20px 0px 20px 0px; 20 | } -------------------------------------------------------------------------------- /views/partials/ui/nextPreviousTemplate.handlebars: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /helpers/customMiddleware.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * Custom Middleware for Express: 4 | * 5 | * Sets requests id to each request. This request id is later used for logging/debugging purpose. 6 | * 7 | */ 8 | 9 | const uuid = require('uuid'); 10 | 11 | module.exports = function(options) { 12 | return function(req, res, next) { 13 | req.id = uuid.v4(); 14 | req.startTime = process.hrtime(); 15 | next(); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /assets/js/mCommon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 07/02/18. 3 | */ 4 | 5 | //= require plugins/handlebars/handlebars.js 6 | //= require plugins/moment/moment.js 7 | //= require plugins/bignumber.js/bignumber.js 8 | //= require plugins/numeraljs/numeral.js 9 | //= require plugins/jquery-visible/jquery.visible.js 10 | //= require common/PriceOracle.js 11 | //= require common/common.js 12 | //= require common/search.js 13 | //= require common/SimpleDataTable.js 14 | -------------------------------------------------------------------------------- /assets/js/addressDetails/addressDetails.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 08/02/18. 3 | */ 4 | (function(window, $) { 5 | var btx = ns('btx'); 6 | 7 | var oThis = (btx.addressDetails = { 8 | config: {}, 9 | 10 | init: function(config) { 11 | var oThis = this; 12 | $.extend(oThis.config, config); 13 | 14 | new btx.SimpleDataTable({ 15 | jParent: $('#address-transaction-table') 16 | }); 17 | } 18 | }); 19 | })(window, jQuery); 20 | -------------------------------------------------------------------------------- /assets/js/tokenAddressDetails/tokenAddressDetails.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 08/02/18. 3 | */ 4 | (function(window, $) { 5 | var btx = ns('btx'); 6 | 7 | var oThis = (btx.tokenAddressDetails = { 8 | config: {}, 9 | 10 | init: function(config) { 11 | var oThis = this; 12 | $.extend(oThis.config, config); 13 | 14 | new btx.SimpleDataTable({ 15 | jParent: $('#token-address-transaction-table') 16 | }); 17 | } 18 | }); 19 | })(window, jQuery); 20 | -------------------------------------------------------------------------------- /assets/js/blockDetails/blockDetails.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Akshay on 09/03/18. 3 | */ 4 | (function(window, $) { 5 | var btx = ns('btx'); 6 | 7 | var oThis = (btx.blockDetails = { 8 | config: {}, 9 | dataTable: null, 10 | 11 | init: function(config) { 12 | var oThis = this; 13 | $.extend(oThis.config, config); 14 | oThis.dataTable = new btx.SimpleDataTable({ 15 | jParent: $('#block-transaction-table') 16 | }); 17 | } 18 | }); 19 | })(window, jQuery); 20 | -------------------------------------------------------------------------------- /lib/globalConstant/cacheManagement.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Cache management constants. 4 | * 5 | * @module lib/globalConstant/cacheManagement 6 | */ 7 | class CacheManagement { 8 | constructor() {} 9 | 10 | get memcached() { 11 | return 'memcached'; 12 | } 13 | 14 | get inMemory() { 15 | return 'in_memory'; 16 | } 17 | 18 | get redis() { 19 | return 'redis'; 20 | } 21 | 22 | get sharedMemcached() { 23 | return 'shared_memcached'; 24 | } 25 | } 26 | 27 | module.exports = new CacheManagement(); 28 | -------------------------------------------------------------------------------- /config/error/param.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * This file has the error config for certain code validations. 4 | * 5 | * @module config/error/param 6 | */ 7 | 8 | const paramErrorConfig = { 9 | missingChainId: { 10 | parameter: 'chainId', 11 | code: 'missing', 12 | message: 'Missing chain id' 13 | }, 14 | invalidShardName: { 15 | parameter: 'invalidShardName', 16 | code: 'invalid', 17 | message: 'Invalid shard name' 18 | }, 19 | missingTransactionHashes: { 20 | parameter: 'transactionHashes', 21 | code: 'missing', 22 | message: 'Missing transaction hashes' 23 | } 24 | }; 25 | 26 | module.exports = paramErrorConfig; 27 | -------------------------------------------------------------------------------- /lib/globalConstant/canonical.js: -------------------------------------------------------------------------------- 1 | const rootPrefix = '../..', 2 | coreConstants = require(rootPrefix + '/config/coreConstants'); 3 | 4 | class Canonical { 5 | constructor() {} 6 | 7 | forHome() { 8 | if (coreConstants.IS_VIEW_SUB_ENVIRONMENT_MAIN) { 9 | return coreConstants.DOMAIN; 10 | } else { 11 | return coreConstants.DOMAIN + '/' + coreConstants.TESTNET_BASE_URL_PREFIX; 12 | } 13 | } 14 | 15 | forAbout() { 16 | return coreConstants.DOMAIN + '/' + 'about'; 17 | } 18 | 19 | forEconomy(tokenSymbol) { 20 | return coreConstants.DOMAIN + '/' + coreConstants.BASE_URL_PREFIX + '/' + tokenSymbol; 21 | } 22 | } 23 | 24 | module.exports = new Canonical(); 25 | -------------------------------------------------------------------------------- /lib/Base64/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This class is used to encode and decode data in Base64 format. 5 | * @type {string} 6 | */ 7 | 8 | const rootPrefix = '../../..'; 9 | 10 | class Base64Helper { 11 | constructor() {} 12 | 13 | /** 14 | * 15 | * @param data 16 | * @returns {string} 17 | */ 18 | encode(data) { 19 | let buff = new Buffer.from(data), 20 | base64data = buff.toString('base64'); 21 | 22 | return base64data; 23 | } 24 | 25 | /** 26 | * 27 | * @param base64data 28 | * @returns {string} 29 | */ 30 | decode(base64data) { 31 | let buff = new Buffer(base64data, 'base64'), 32 | data = buff.toString('ascii'); 33 | 34 | return data; 35 | } 36 | } 37 | 38 | module.exports = new Base64Helper(); 39 | -------------------------------------------------------------------------------- /views/layouts/empty.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{{js 'mCommon.js'}}} 11 | 12 | {{title}} 13 | 14 | 15 | {{{body}}} 16 | 17 | -------------------------------------------------------------------------------- /assets/js/tokenDetails/tokenDetails.js: -------------------------------------------------------------------------------- 1 | (function(window, $) { 2 | var btx = ns('btx'); 3 | 4 | var oThis = (btx.tokenDetails = { 5 | config: {}, 6 | tokenTransferTable: null, 7 | homeTopTokensTable: null, 8 | init: function(config) { 9 | var oThis = this; 10 | $.extend(oThis.config, config); 11 | 12 | oThis.initDataTables(); 13 | }, 14 | 15 | initDataTables: function() { 16 | oThis.tokenTransferTable = new btx.SimpleDataTable({ 17 | jParent: $('#tokenTransferTable') 18 | }); 19 | 20 | $('#tokenHoldersTab').on('click.simpleDataTable', function() { 21 | oThis.homeTopTokensTable = new btx.SimpleDataTable({ 22 | jParent: $('#tokenHoldersTable') 23 | }); 24 | $(this).off('click.simpleDataTable'); 25 | }); 26 | } 27 | }); 28 | })(window, jQuery); 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore tmp files and folders 2 | /tmp/* 3 | !/log/.keep 4 | !/tmp/.keep 5 | /.idea 6 | /.idea/* 7 | 8 | package-lock.json 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directory 31 | # https://docs.npmjs.com/cli/shrinkwrap#caveats 32 | node_modules 33 | 34 | # Dev Folder 35 | .dev 36 | 37 | # Debug log from npm 38 | npm-debug.log 39 | .vscode 40 | 41 | .DS_Store 42 | 43 | #Ignore configuration file 44 | configuration.json -------------------------------------------------------------------------------- /views/partials/blockDetailsDataTable.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 |

Transactions

9 | 10 |
11 |
13 | 14 | {{>ui/transactionTable}} 15 | 16 |
17 |
18 | 19 | {{>ui/nextPreviousTemplate}} 20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 | -------------------------------------------------------------------------------- /lib/formatter/entities/Base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | responseHelper = require(rootPrefix + '/lib/formatter/response'); 5 | 6 | class BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() {} 11 | 12 | /** 13 | * validateResponse 14 | * 15 | * @param inputData 16 | * @param mandatoryRootLevelKeys 17 | * @return {Promise} 18 | */ 19 | async validateResponse(inputData, mandatoryRootLevelKeys) { 20 | const oThis = this; 21 | 22 | for (let i = 0; i < mandatoryRootLevelKeys.length; i++) { 23 | if (!inputData.hasOwnProperty(mandatoryRootLevelKeys[i])) { 24 | return Promise.reject( 25 | responseHelper.error('l_f_e_b_1', 'entity_formatting_failed_on_key_' + mandatoryRootLevelKeys[i]) 26 | ); 27 | } 28 | } 29 | } 30 | } 31 | 32 | module.exports = BaseFormatter; 33 | -------------------------------------------------------------------------------- /lib/providers/blockScanner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Provider for block scanner 4 | * 5 | * @module lib/providers/blockScanner 6 | */ 7 | const rootPrefix = '../..', 8 | OSTBase = require('@ostdotcom/base'), 9 | coreConstants = require(rootPrefix + '/config/coreConstants'), 10 | OSTBlockScanner = require('@ostdotcom/ost-block-scanner'); 11 | 12 | const InstanceComposer = OSTBase.InstanceComposer; 13 | 14 | class BlockScannerProvider { 15 | constructor(configStrategy, instanceComposer) {} 16 | 17 | /** 18 | * getInstance - Get a new instance of block scanner 19 | * 20 | * @param configStrategy 21 | * @return {*} 22 | */ 23 | getInstance() { 24 | const oThis = this; 25 | let configStrategy = oThis.ic().configStrategy; 26 | 27 | return new OSTBlockScanner(configStrategy); 28 | } 29 | } 30 | 31 | InstanceComposer.registerAsObject(BlockScannerProvider, coreConstants.icNameSpace, 'blockScannerProvider', true); -------------------------------------------------------------------------------- /lib/providers/inMemoryCache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * In-memory cache instance provider. 5 | * 6 | * @module /lib/providers/inMemoryCache 7 | */ 8 | 9 | const rootPrefix = '../..', 10 | OSTCache = require('@ostdotcom/cache'); 11 | 12 | /** 13 | * Constructor 14 | * 15 | * @constructor 16 | */ 17 | const InMemoryCacheProviderKlass = function() {}; 18 | 19 | InMemoryCacheProviderKlass.prototype = { 20 | /** 21 | * Get provider 22 | * 23 | * @return {Object} 24 | */ 25 | getInstance: function(cacheConsistentBehavior) { 26 | const cacheConfigStrategy = { 27 | cache: { 28 | engine: 'none', 29 | namespace: `ostView_${cacheConsistentBehavior}`, 30 | defaultTtl: 36000, 31 | consistentBehavior: cacheConsistentBehavior 32 | } 33 | }; 34 | 35 | return OSTCache.getInstance(cacheConfigStrategy); 36 | } 37 | }; 38 | 39 | module.exports = new InMemoryCacheProviderKlass(); 40 | -------------------------------------------------------------------------------- /lib/jwt/jwt_auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * JWT implementation 5 | * 6 | * * Author: Kedar 7 | * * Date: 25/01/2018 8 | * * Reviewed by: 9 | */ 10 | 11 | const jwt = require('jsonwebtoken'); 12 | 13 | const rootPrefix = '../..', 14 | coreConstants = require(rootPrefix + '/config/coreConstants'); 15 | 16 | const key = coreConstants.JWT_SECRET_KEY; 17 | 18 | class JwtAuth { 19 | constructor() {} 20 | 21 | static encrypt(data) { 22 | const payload = { data: data }, 23 | jwtOptions = { expiresIn: 60 * 5 }; 24 | return jwt.sign(payload, key, jwtOptions); 25 | } 26 | 27 | static decrypt(token) { 28 | return new Promise(function(onResolve, onReject) { 29 | var jwtCB = function(err, decodedToken) { 30 | if (err) { 31 | onReject(err); 32 | } else { 33 | onResolve(decodedToken); 34 | } 35 | }; 36 | 37 | jwt.verify(token, key, {}, jwtCB); 38 | }); 39 | } 40 | } 41 | 42 | module.exports = JwtAuth; 43 | -------------------------------------------------------------------------------- /views/partials/addressDetailsDataTable.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 |

TRANSACTIONS

9 |
10 |
12 | 13 | {{> ui/transactionTable}} 14 | 15 |
16 |
17 | 18 | {{>ui/nextPreviousTemplate}} 19 | 20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 | {{#contentFor "pageScripts"}} 29 | 34 | {{/contentFor}} -------------------------------------------------------------------------------- /assets/js/home/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aniket on 26/02/18. 3 | */ 4 | 5 | (function(window, $) { 6 | var btx = ns('btx'); 7 | 8 | var oThis = (btx.home = { 9 | config: {}, 10 | homeTokenTransactionTable: null, 11 | homeTopTokensTable: null, 12 | 13 | init: function(config) { 14 | $.extend(oThis.config, config); 15 | oThis.initTopTokenTable(); 16 | oThis.bindEvents(); 17 | }, 18 | 19 | initTopTokenTable: function() { 20 | oThis.homeTopTokensTable = new btx.SimpleDataTable({ 21 | jParent: $('#homeTopTokensTable') 22 | }); 23 | }, 24 | 25 | initTokenTransactionTable: function() { 26 | oThis.homeTokenTransactionTable = new btx.SimpleDataTable({ 27 | jParent: $('#homeTokenTransactionTable') 28 | }); 29 | }, 30 | 31 | bindEvents: function() { 32 | $('#latestTokenTransferTab').on('click.simpleDataTable', function() { 33 | $(this).off('click.simpleDataTable'); 34 | oThis.initTokenTransactionTable(); 35 | }); 36 | } 37 | }); 38 | })(window, jQuery); 39 | -------------------------------------------------------------------------------- /assets/js/plugins/jquery-visible/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Digital Fusion, http://teamdf.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /views/addressDetails.handlebars: -------------------------------------------------------------------------------- 1 | {{>pageTitle title="Address"}} 2 | 3 |
4 |
5 |
6 | 7 |
8 |
9 |
Address
10 | {{address.address}} 11 |
12 | 13 |
14 |
Balance
15 | {{fromWei address.balance}} {{ostCurrencySymbol}} 16 |
17 | 18 |
19 |
Total Transactions
20 | {{address.totalTransactions}} 21 |
22 | 23 |
24 |
Chain Id
25 | {{address.chainId}} 26 |
27 |
28 | 29 |
30 |
31 |
32 | 33 | {{>addressDetailsDataTable}} 34 | 35 | -------------------------------------------------------------------------------- /lib/Authentication/jwt.js: -------------------------------------------------------------------------------- 1 | const rootPrefix = '../..', 2 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 3 | jwtAuth = require(rootPrefix + '/lib/jwt/jwt_auth'); 4 | 5 | class Jwt { 6 | constructor() {} 7 | 8 | static authenticate(req, res, next) { 9 | let token; 10 | 11 | if (req.method === 'POST' || req.method === 'DELETE') { 12 | token = req.body.token || ''; 13 | } else if (req.method === 'GET') { 14 | token = req.query.token || ''; 15 | } 16 | 17 | // Set the decoded params in the re and call the next in control flow. 18 | const jwtOnResolve = function(reqParams) { 19 | req.params = reqParams.data; 20 | // Validation passed. 21 | return next(); 22 | }; 23 | 24 | // send error, if token is invalid 25 | const jwtOnReject = function(err) { 26 | return responseHelper.error('401', 'invalid_or_expired_jwt_token').renderResponse(res, 401); 27 | }; 28 | 29 | // Verify token 30 | Promise.resolve(jwtAuth.decrypt(token).then(jwtOnResolve, jwtOnReject)).catch(function(err) { 31 | return responseHelper.error('500', 'something_went_wrong').renderResponse(res, 500); 32 | }); 33 | } 34 | } 35 | 36 | module.exports = Jwt; 37 | -------------------------------------------------------------------------------- /views/about.handlebars: -------------------------------------------------------------------------------- 1 | {{>pageTitle title="About"}} 2 | 3 |
4 |
5 |
6 |
7 |

OST VIEW is the block explorer for viewing Brand Token transactions and contracts on the OST blockchains.

8 |

A multi-chain explorer, OST VIEW is a search engine and browser that people can utilize to view human-readable block, transaction, and account information on the OST blockchains.

9 |

OST VIEW provides individual and aggregate historical data, including statistics and charts, on Brand Token transfers in, and across all Brand Token economies.

10 |

OST VIEW is not a wallet service provider. It does not store private keys, and it does not have control over the transactions that take place on the OST blockchains. OST VIEW only provides data from the OST blockchains. It cannot be used to explore blocks and transactions on other blockchains.

11 |

On Testnet the ticker OSTT indicates the token OST-Test and USDCT indicates the token USDC-Test which are test versions of the OST Token and USDC Token respectively, in the OST Sandbox environment.

12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /views/tokenAddressDetails.handlebars: -------------------------------------------------------------------------------- 1 | {{>pageTitle title="Token Holder"}} 2 | 3 |
4 |
5 |
6 | 7 |
8 | 9 |
10 |
Address
11 | {{tokenHolder.address}} 12 |
13 | 14 |
15 |
Balance
16 | {{toDecimalValue tokenHolder.balance token.decimals}} {{token.symbol}} 17 |
18 | 19 |
20 |
Total Token Transfers
21 | {{tokenHolder.totalTransfers}} 22 |
23 | 24 |
25 |
Chain ID
26 | {{tokenHolder.chainId}} 27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 | {{>tokenAddressDetailsDataTable}} 35 | 36 | -------------------------------------------------------------------------------- /lib/globalConstant/baseRoutes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module for route templates. 3 | * 4 | * @module lib/globalConstant/baseRoutes 5 | */ 6 | 7 | const rootPrefix = '../..', 8 | coreConstants = require(rootPrefix + '/config/coreConstants'); 9 | 10 | /** 11 | * Class for route templates 12 | * 13 | * @class BaseRoutes 14 | */ 15 | class BaseRoutes { 16 | get token() { 17 | return '/' + coreConstants.BASE_URL_PREFIX + '/{{symbol}}'; 18 | } 19 | 20 | get block() { 21 | return '/' + coreConstants.BASE_URL_PREFIX + '/block/bk-{{chainId}}-{{blockNumber}}'; 22 | } 23 | 24 | get transaction() { 25 | return '/' + coreConstants.BASE_URL_PREFIX + '/transaction/tx-{{chainId}}-{{transactionHash}}'; 26 | } 27 | 28 | get address() { 29 | return '/' + coreConstants.BASE_URL_PREFIX + '/address/ad-{{chainId}}-{{address}}'; 30 | } 31 | 32 | get tokenHolder() { 33 | return '/' + coreConstants.BASE_URL_PREFIX + '/token/th-{{chainId}}-{{contractAddress}}-{{address}}'; 34 | } 35 | 36 | getAllUrls() { 37 | const oThis = this; 38 | 39 | return { 40 | token: oThis.token, 41 | block: oThis.block, 42 | transaction: oThis.transaction, 43 | address: oThis.address, 44 | tokenHolder: oThis.tokenHolder 45 | }; 46 | } 47 | } 48 | 49 | module.exports = new BaseRoutes(); 50 | -------------------------------------------------------------------------------- /assets/css/common/graphComponent.scss: -------------------------------------------------------------------------------- 1 | .card-graph{ 2 | 3 | border-color: #e3eef3; 4 | 5 | .card-body{ 6 | padding: 0.75rem; 7 | 8 | .card-title{ 9 | h3{ 10 | color: #438bad; 11 | font-size: 1rem; 12 | text-transform: uppercase; 13 | } 14 | } 15 | 16 | .graph-header{ 17 | font-size: 0.8rem; 18 | color: #438bad; 19 | border-top: 1px solid #e3eef3; 20 | border-bottom: 1px solid #e3eef3; 21 | 22 | .interval{ 23 | cursor: pointer; 24 | border-bottom: 1px solid transparent; 25 | &[data-interval='hour'] { 26 | border-left: 1px solid #e3eef3; 27 | } 28 | &.active{ 29 | color: #e4b030; 30 | border-bottom: 1px solid #e4b030; 31 | font-weight: 500; 32 | } 33 | } 34 | } 35 | 36 | .graph-container{ 37 | position: relative; 38 | width: 100%; 39 | height: 300px; 40 | display: -webkit-box!important; 41 | display: -ms-flexbox!important; 42 | display: flex!important; 43 | -webkit-box-align: center!important; 44 | -ms-flex-align: center!important; 45 | align-items: center!important; 46 | 47 | .noDataHTML{ 48 | font-size: 0.8rem; 49 | color: #438bad; 50 | margin:0 auto; 51 | } 52 | } 53 | } 54 | 55 | 56 | } -------------------------------------------------------------------------------- /assets/css/blockDetails/blockDetails.scss: -------------------------------------------------------------------------------- 1 | .key-color { 2 | color: #5d6d6e; 3 | } 4 | 5 | .title{ 6 | color: #438bad; 7 | } 8 | 9 | .tx-color{ 10 | color: #179eb1; 11 | } 12 | 13 | .container-block-details { 14 | .info-card { 15 | border: none; 16 | font-size: 14px; 17 | 18 | .card-body { 19 | padding: 0.8rem; 20 | background-color: #f1f5f8; 21 | .token-icon { 22 | border-radius: 50%; 23 | width: 1.3rem; 24 | } 25 | 26 | } 27 | } 28 | 29 | .details-card { 30 | 31 | border-radius: 2px; 32 | font-size: 14px; 33 | 34 | .card-body { 35 | padding: 0.8rem; 36 | &:nth-of-type(even) { 37 | background-color: #f1f5f8; 38 | } 39 | 40 | .icon { 41 | border-radius: 50%; 42 | width: 1.3rem; 43 | } 44 | } 45 | } 46 | 47 | .transfer_table{ 48 | .transactions-title-color { 49 | color: #438bad; 50 | } 51 | } 52 | 53 | .card-key { 54 | color: #5d6d6e; 55 | min-width: 150px; 56 | display: inline-block; 57 | 58 | @media (max-width: 576px) { 59 | display: inline-block; 60 | min-width: inherit; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /routes/helper.js: -------------------------------------------------------------------------------- 1 | const OSTBase = require('@ostdotcom/base'); 2 | 3 | const rootPrefix = '..', 4 | coreConstants = require(rootPrefix + '/config/coreConstants'), 5 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 6 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'); 7 | 8 | const InstanceComposer = OSTBase.InstanceComposer; 9 | 10 | const routeMethods = { 11 | performer: function(req, res, next, GetterMethodName, errorCode) { 12 | try { 13 | Object.assign(req.params, req.query); 14 | 15 | const decodedParams = req.params; 16 | 17 | const configStrategy = coreConstants.CONFIG_STRATEGY, 18 | instanceComposer = new InstanceComposer(configStrategy), 19 | getterMethod = instanceComposer.getShadowedClassFor(coreConstants.icNameSpace, GetterMethodName); 20 | 21 | const callerObject = new getterMethod(decodedParams); 22 | 23 | return callerObject.perform(); 24 | } catch (err) { 25 | logger.notify(errorCode, 'Something went wrong', err); 26 | Promise.resolve(responseHelper.error(errorCode, 'Something went wrong')); 27 | } 28 | }, 29 | 30 | validateXhrRequest: function(req, res) { 31 | if (!req.xhr) { 32 | responseHelper.error('NOT_FOUND', 'Resource not found').renderResponse(res, 404); 33 | 34 | return true; 35 | } 36 | 37 | return false; 38 | } 39 | }; 40 | 41 | module.exports = routeMethods; 42 | -------------------------------------------------------------------------------- /app/services/home/GetHomePageStats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | OSTBase = require('@ostdotcom/base'), 5 | coreConstants = require(rootPrefix + '/config/coreConstants'), 6 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 7 | responseHelper = require(rootPrefix + '/lib/formatter/response'); 8 | 9 | const InstanceComposer = OSTBase.InstanceComposer; 10 | 11 | require(rootPrefix + '/lib/cacheManagement/HomePageStats'); 12 | 13 | class GetHomePageStats { 14 | constructor() {} 15 | 16 | /** 17 | * perform 18 | * 19 | */ 20 | perform() { 21 | const oThis = this; 22 | 23 | return oThis.asyncPerform().catch(function(err) { 24 | logger.error(' In catch block of app/services/home/GetHomePageStats.js'); 25 | return responseHelper.error('a_s_h_ghps_1', 'something_went_wrong', err); 26 | }); 27 | } 28 | 29 | /** 30 | * asyncPerform 31 | * 32 | * @return {Promise} 33 | */ 34 | async asyncPerform() { 35 | const oThis = this, 36 | HomePageStatsCache = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'HomePageStatsCache'), 37 | homePageStatsCache = new HomePageStatsCache(); 38 | 39 | let response = await homePageStatsCache.fetch(); 40 | 41 | return responseHelper.successWithData(response.data); 42 | } 43 | } 44 | 45 | InstanceComposer.registerAsShadowableClass(GetHomePageStats, coreConstants.icNameSpace, 'GetHomePageStats'); 46 | 47 | module.exports = GetHomePageStats; 48 | -------------------------------------------------------------------------------- /assets/css/addressDetails/addressDetails.scss: -------------------------------------------------------------------------------- 1 | .key-color { 2 | color: #5d6d6e; 3 | } 4 | 5 | .title{ 6 | color: #438bad; 7 | } 8 | 9 | .container-address-details { 10 | .info-card { 11 | border: none; 12 | font-size: 14px; 13 | 14 | .card-body { 15 | padding: 0.8rem; 16 | background-color: #f1f5f8; 17 | .token-icon { 18 | border-radius: 50%; 19 | width: 1.3rem; 20 | } 21 | 22 | } 23 | } 24 | 25 | .details-card { 26 | 27 | border-radius: 2px; 28 | font-size: 14px; 29 | 30 | .card-body { 31 | padding: 0.8rem; 32 | &:nth-of-type(even) { 33 | background-color: #f1f5f8; 34 | } 35 | 36 | .badge-primary{ 37 | padding: 0.4em; 38 | } 39 | 40 | .coin-amount { 41 | background-color: #9cc5c1; 42 | color: #ffffff; 43 | border-radius: 0.3rem; 44 | padding: 0.16rem; 45 | } 46 | } 47 | } 48 | 49 | .transfer_table{ 50 | .transactions-title-color { 51 | color: #438bad; 52 | } 53 | } 54 | 55 | .card-key { 56 | color: #5d6d6e; 57 | min-width: 150px; 58 | display: inline-block; 59 | 60 | @media (max-width: 576px){ 61 | display: inline; 62 | min-width: inherit; 63 | } 64 | } 65 | } 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /devops/flushCache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Cache flush utility for explorer 4 | * 5 | * Example: node devops/flush_cache.js 'shared' 1000 6 | */ 7 | 8 | const rootPrefix = '..', 9 | OSTBase = require('@ostdotcom/base'), 10 | coreConstants = require(rootPrefix + '/config/coreConstants'), 11 | configStrategy = require(rootPrefix + '/configuration'); 12 | 13 | const InstanceComposer = OSTBase.InstanceComposer; 14 | 15 | require(rootPrefix + '/lib/providers/cache'); 16 | 17 | let cacheType = process.argv[2], 18 | chainId = process.argv[3]; 19 | 20 | class CacheFlush { 21 | /** 22 | * constructor 23 | */ 24 | constructor() {} 25 | 26 | /** 27 | * perform 28 | * 29 | * @return {Promise<*|Promise>} 30 | */ 31 | async perform() { 32 | if (cacheType == 'sharded' && !chainId) { 33 | return Promise.reject('==== chainId is mandatory for sharded cache!!! ===='); 34 | } 35 | 36 | let instanceComposer = new InstanceComposer(configStrategy), 37 | cache = instanceComposer.getInstanceFor(coreConstants.icNameSpace, 'cacheProvider'), 38 | cacheImplementer = cache.getInstance(cacheType, chainId).cacheInstance; 39 | 40 | return cacheImplementer.delAll(); 41 | } 42 | } 43 | 44 | let cacheflush = new CacheFlush(); 45 | 46 | cacheflush 47 | .perform() 48 | .then(function(r) { 49 | console.log('====Flushed', cacheType, 'memcached ===='); 50 | process.exit(0); 51 | }) 52 | .catch(function(err) { 53 | console.log('====Error ====\n', err); 54 | process.exit(1); 55 | }); 56 | -------------------------------------------------------------------------------- /lib/formatter/entities/address.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class Address extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param addressData 18 | * @return {Promise} 19 | */ 20 | async perform(addressData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(addressData, mandatoryRootLevelKeys); 26 | 27 | return oThis.formatEntity(addressData); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param addressData 34 | * @return {{}} 35 | */ 36 | formatEntity(addressData) { 37 | let formattedTransferData = {}; 38 | 39 | formattedTransferData['address'] = addressData['address']; 40 | formattedTransferData['balance'] = addressData['balance']; 41 | formattedTransferData['totalTransactions'] = addressData['totalTransactions']; 42 | formattedTransferData['chainId'] = addressData['chainId']; 43 | 44 | return formattedTransferData; 45 | } 46 | 47 | /** 48 | * responseDefinition 49 | * 50 | * @return {string[]} 51 | */ 52 | responseDefinition() { 53 | const oThis = this, 54 | mandatoryRootLevelKeys = ['address', 'balance', 'totalTransactions', 'chainId']; 55 | return mandatoryRootLevelKeys; 56 | } 57 | } 58 | 59 | module.exports = new Address(); 60 | -------------------------------------------------------------------------------- /assets/css/tokenAddressDetails/tokenAddressDetails.scss: -------------------------------------------------------------------------------- 1 | .key-color { 2 | color: #5d6d6e; 3 | } 4 | 5 | .title{ 6 | color: #438bad; 7 | } 8 | 9 | .container-address-details { 10 | .info-card { 11 | border: none; 12 | font-size: 14px; 13 | 14 | .card-body { 15 | padding: 0.8rem; 16 | background-color: #f1f5f8; 17 | .token-icon { 18 | border-radius: 50%; 19 | width: 1.3rem; 20 | } 21 | 22 | } 23 | } 24 | 25 | .details-card { 26 | 27 | border-radius: 2px; 28 | font-size: 14px; 29 | 30 | .card-body { 31 | padding: 0.8rem; 32 | &:nth-of-type(odd) { 33 | background-color: #f1f5f8; 34 | } 35 | 36 | .badge-primary{ 37 | padding: 0.4em; 38 | } 39 | 40 | .coin-amount { 41 | background-color: #9cc5c1; 42 | color: #ffffff; 43 | border-radius: 0.3rem; 44 | padding: 0.16rem; 45 | } 46 | } 47 | } 48 | 49 | .transfer_table{ 50 | .transactions-title-color { 51 | color: #438bad; 52 | } 53 | } 54 | 55 | .card-key { 56 | color: #5d6d6e; 57 | min-width: 150px; 58 | display: inline-block; 59 | 60 | @media (max-width: 576px){ 61 | display: inline; 62 | min-width: inherit; 63 | } 64 | } 65 | } 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /lib/formatter/entities/tokenHolderSearch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class TokenHolderSearch extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param tokenHolderData 18 | * @return {Promise} 19 | */ 20 | async perform(tokenHolderData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(tokenHolderData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(tokenHolderData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param tokenHolderData 34 | * @return {{}} 35 | */ 36 | formatEntity(tokenHolderData) { 37 | let formattedNameSearchResults = {}; 38 | 39 | formattedNameSearchResults['chainId'] = tokenHolderData['chainId']; 40 | formattedNameSearchResults['contractAddress'] = tokenHolderData['contractAddress']; 41 | formattedNameSearchResults['name'] = tokenHolderData['displayName']; 42 | 43 | return formattedNameSearchResults; 44 | } 45 | 46 | /** 47 | * responseDefinition 48 | * 49 | * @return {string[]} 50 | */ 51 | responseDefinition() { 52 | const oThis = this, 53 | mandatoryRootLevelKeys = ['displayName', 'chainId', 'contractAddress']; 54 | return mandatoryRootLevelKeys; 55 | } 56 | } 57 | 58 | module.exports = new TokenHolderSearch(); 59 | -------------------------------------------------------------------------------- /lib/formatter/entities/tokenNameSearch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class TokenNameSearch extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param tokenNameSearchData 18 | * @return {Promise} 19 | */ 20 | async perform(tokenNameSearchData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(tokenNameSearchData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(tokenNameSearchData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param tokenHolderData 34 | * @return {{}} 35 | */ 36 | formatEntity(tokenHolderData) { 37 | let formattedNameSearchResults = {}; 38 | 39 | formattedNameSearchResults['name'] = tokenHolderData['displayName']; 40 | formattedNameSearchResults['symbol'] = tokenHolderData['displaySymbol']; 41 | formattedNameSearchResults['chainId'] = tokenHolderData['chainId']; 42 | formattedNameSearchResults['contractAddress'] = tokenHolderData['contractAddress']; 43 | 44 | return formattedNameSearchResults; 45 | } 46 | 47 | /** 48 | * responseDefinition 49 | * 50 | * @return {string[]} 51 | */ 52 | responseDefinition() { 53 | const oThis = this, 54 | mandatoryRootLevelKeys = ['displayName', 'displaySymbol', 'chainId', 'contractAddress']; 55 | return mandatoryRootLevelKeys; 56 | } 57 | } 58 | 59 | module.exports = new TokenNameSearch(); 60 | -------------------------------------------------------------------------------- /lib/formatter/entities/tokenHolder.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class TokenHolder extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param tokenHolderData 18 | * @return {Promise} 19 | */ 20 | async perform(tokenHolderData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(tokenHolderData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(tokenHolderData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param tokenHolderData 34 | * @return {{}} 35 | */ 36 | formatEntity(tokenHolderData) { 37 | let formattedTransferData = {}; 38 | 39 | formattedTransferData['address'] = tokenHolderData['address']; 40 | formattedTransferData['balance'] = tokenHolderData['balance']; 41 | formattedTransferData['chainId'] = tokenHolderData['chainId']; 42 | formattedTransferData['totalTransfers'] = tokenHolderData['totalTokenTransfers']; 43 | formattedTransferData['contractAddress'] = tokenHolderData['contractAddress']; 44 | 45 | return formattedTransferData; 46 | } 47 | 48 | /** 49 | * responseDefinition 50 | * 51 | * @return {string[]} 52 | */ 53 | responseDefinition() { 54 | const oThis = this, 55 | mandatoryRootLevelKeys = ['address', 'balance', 'chainId', 'contractAddress']; 56 | return mandatoryRootLevelKeys; 57 | } 58 | } 59 | 60 | module.exports = new TokenHolder(); 61 | -------------------------------------------------------------------------------- /views/partials/homeSummary.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 |
8 |
9 |

Apps

10 |
{{bigNumber_toFromat stats.totalCommunities}}
11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |

Token Holders

21 |
{{bigNumber_toFromat stats.totalTokenHolders}}
22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |

No. of Transfers

32 |
{{bigNumber_toFromat stats.totalTokenTransfers}} 33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
-------------------------------------------------------------------------------- /app/services/home/GetDetails.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * GetHomePageDetails - Service for getting HomePage details 4 | * 5 | * 6 | */ 7 | 8 | const rootPrefix = '../../..', 9 | OSTBase = require('@ostdotcom/base'), 10 | coreConstants = require(rootPrefix + '/config/coreConstants'), 11 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 12 | responseHelper = require(rootPrefix + '/lib/formatter/response'); 13 | 14 | const InstanceComposer = OSTBase.InstanceComposer; 15 | 16 | require(rootPrefix + '/lib/models/GlobalStats'); 17 | 18 | class GetHomeDetails { 19 | /** 20 | * constructor 21 | */ 22 | constructor() {} 23 | 24 | /** 25 | * perform 26 | * 27 | */ 28 | perform() { 29 | const oThis = this; 30 | 31 | return oThis.asyncPerform().catch(function(err) { 32 | logger.error(' In catch block of app/services/home/GetDetails.js'); 33 | return responseHelper.error('s_h_gd_1', 'something_went_wrong', err); 34 | }); 35 | } 36 | 37 | /** 38 | * asyncPerform 39 | * 40 | * @return {Promise} 41 | */ 42 | async asyncPerform() { 43 | const oThis = this; 44 | 45 | let response = await oThis.getHomePageStats(); 46 | 47 | return responseHelper.successWithData(response.data['1']); 48 | } 49 | 50 | /** 51 | * Returns home page stats. 52 | * 53 | * @returns {Promise<*>} 54 | */ 55 | async getHomePageStats() { 56 | const oThis = this, 57 | GlobalStats = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'GlobalStats'), 58 | globalStatsObj = new GlobalStats({ consistentRead: false }); 59 | 60 | let response = await globalStatsObj.getData(); 61 | 62 | return response; 63 | } 64 | } 65 | 66 | InstanceComposer.registerAsShadowableClass(GetHomeDetails, coreConstants.icNameSpace, 'GetHomeDetails'); 67 | -------------------------------------------------------------------------------- /lib/formatter/entities/block.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class Block extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param blockData 18 | * @return {Promise} 19 | */ 20 | async perform(blockData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(blockData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(blockData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param blockData 34 | * @return {{}} 35 | */ 36 | formatEntity(blockData) { 37 | let formattedBlockData = {}; 38 | 39 | formattedBlockData.chainId = blockData.chainId; 40 | formattedBlockData.blockHash = blockData.blockHash; 41 | formattedBlockData.gasUsed = blockData.gasUsed; 42 | formattedBlockData.blockNumber = blockData.blockNumber; 43 | formattedBlockData.totalTransactions = blockData.totalTransactions; 44 | formattedBlockData.blockTimestamp = blockData.blockTimestamp; 45 | formattedBlockData.nonce = blockData.nonce; 46 | 47 | return formattedBlockData; 48 | } 49 | 50 | /** 51 | * responseDefinition 52 | * 53 | * @return {string[]} 54 | */ 55 | responseDefinition() { 56 | const oThis = this, 57 | mandatoryRootLevelKeys = [ 58 | 'chainId', 59 | 'blockHash', 60 | 'gasUsed', 61 | 'blockNumber', 62 | 'totalTransactions', 63 | 'blockTimestamp', 64 | 'nonce' 65 | ]; 66 | return mandatoryRootLevelKeys; 67 | } 68 | } 69 | 70 | module.exports = new Block(); 71 | -------------------------------------------------------------------------------- /lib/providers/storage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * OSTStorage Provider 4 | * 5 | * @module lib/providers/storage 6 | */ 7 | const OSTStorage = require('@ostdotcom/storage'); 8 | 9 | const rootPrefix = '../..', 10 | OSTBase = require('@ostdotcom/base'), 11 | coreConstants = require(rootPrefix + '/config/coreConstants'); 12 | 13 | const InstanceComposer = OSTBase.InstanceComposer; 14 | 15 | // Following require(s) for registering into instance composer 16 | require(rootPrefix + '/lib/formatter/config'); 17 | 18 | /** 19 | * Class for storage provider 20 | * 21 | * @class 22 | */ 23 | class StorageProvider { 24 | /** 25 | * Constructor for storage provider 26 | * 27 | * @param {Object} configStrategy 28 | * @param instanceComposer 29 | */ 30 | constructor(configStrategy, instanceComposer) {} 31 | 32 | /** 33 | * Get instance of OST Storage. 34 | * 35 | * @returns {Object} 36 | */ 37 | getInstance() { 38 | const oThis = this; 39 | 40 | return OSTStorage.getInstance(oThis.getStorageConfigStrategy()); 41 | } 42 | 43 | /** 44 | * Get storage config strategy 45 | * 46 | */ 47 | getStorageConfigStrategy() { 48 | const oThis = this, 49 | explorerConfigStrategy = oThis.ic().configStrategy, 50 | configFormatter = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'configFormatter'); 51 | 52 | if (!explorerConfigStrategy.storage) { 53 | throw 'missing db config for storage'; 54 | } 55 | 56 | return Object.assign( 57 | {}, 58 | configFormatter.formatStorageConfig(explorerConfigStrategy), 59 | configFormatter.formatCacheConfig(explorerConfigStrategy) 60 | ); 61 | } 62 | } 63 | 64 | InstanceComposer.registerAsObject(StorageProvider, coreConstants.icNameSpace, 'storageProvider', true); 65 | 66 | module.exports = StorageProvider; 67 | -------------------------------------------------------------------------------- /routes/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Search route.

4 | * Base url for all routes given below is: base_url = / 5 | * 6 | * @module Explorer Routes - Search 7 | */ 8 | const express = require('express'); 9 | 10 | // Express router to mount search related routes 11 | const router = express.Router({ mergeParams: true }); 12 | 13 | const rootPrefix = '..', 14 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 15 | sanitizer = require(rootPrefix + '/helpers/sanitizer'), 16 | coreConstants = require(rootPrefix + '/config/coreConstants'), 17 | routeHelper = require(rootPrefix + '/routes/helper'); 18 | 19 | // Render final response 20 | const renderResult = function(requestResponse, responseObject, contentType) { 21 | return requestResponse.renderResponse(responseObject, 200, contentType); 22 | }; 23 | 24 | /** 25 | * Search by address, contract address, transaction hash, block number 26 | * 27 | * @name Search 28 | * 29 | * @route {GET} {base_url}/:param 30 | * 31 | * @routeparam {String} :params - search string 32 | */ 33 | router.get('/', sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 34 | require(rootPrefix + '/app/services/search/Index'); 35 | 36 | if (routeHelper.validateXhrRequest(req, res)) { 37 | return; 38 | } 39 | 40 | routeHelper.performer(req, res, next, 'SearchIndex', 'r_i_s_1').then(function(requestResponse) { 41 | let response = {}; 42 | if (requestResponse.isSuccess()) { 43 | response = responseHelper.successWithData({ 44 | searchResults: requestResponse.data 45 | }); 46 | } else { 47 | response = responseHelper.successWithData({ 48 | searchResults: [] 49 | }); 50 | } 51 | response['meta'] = { 52 | baseUrlPrefix: coreConstants.BASE_URL_PREFIX 53 | }; 54 | return renderResult(response, res, 'application/json'); 55 | }); 56 | }); 57 | 58 | module.exports = router; 59 | -------------------------------------------------------------------------------- /executables/upload_assets_s3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'), 4 | path = require('path'), 5 | { exec } = require('child_process'); 6 | 7 | const rootPrefix = '..'; 8 | 9 | const asset_bucket = 'wa.ost.com'; 10 | 11 | const content_types = { 12 | gz: 'application/gzip', 13 | js: 'application/x-javascript', 14 | css: 'text/css' 15 | }; 16 | 17 | const permission_options = 18 | "--acl public-read --content-encoding gzip --cache-control 'public, max-age=315360000' --expires 'Thu, 25 Jun 2025 20:00:00 GMT'"; 19 | 20 | const baseDir = path.join(__dirname, rootPrefix + '/builtAssets'), 21 | s3BaseDir = '/ost-view/js-css'; // Like /some/dir 22 | 23 | fs.readFile(baseDir + '/manifest.json', function(err, data) { 24 | if (err) { 25 | return console.error(err); 26 | } 27 | const manifest = JSON.parse(data.toString()); 28 | 29 | for (let file_key in manifest.assets) { 30 | const file = manifest.assets[file_key], 31 | splitFileNameArray = file.split('.'), 32 | extension = splitFileNameArray[1]; 33 | 34 | let executableString = undefined; 35 | if (content_types[extension]) { 36 | executableString = 37 | 'aws s3 cp ' + 38 | baseDir + 39 | '/' + 40 | file + 41 | '.gz' + 42 | ' s3://' + 43 | asset_bucket + 44 | s3BaseDir + 45 | '/' + 46 | file + 47 | ' ' + 48 | permission_options + 49 | ' --content-type ' + 50 | content_types[extension]; 51 | 52 | console.log('\n\nExecuting command:' + executableString); 53 | 54 | exec(executableString, function(error, stdout, stderr) { 55 | if (error) { 56 | console.log('\n\nExecuting error: ' + error); 57 | } 58 | if (stderr) { 59 | console.log('\n\nStd error: ' + stderr); 60 | } 61 | if (stdout) { 62 | console.log('\n\n' + stdout); 63 | } 64 | }); 65 | } else { 66 | console.log("Can't upload : ", file); 67 | } 68 | } 69 | }); 70 | -------------------------------------------------------------------------------- /assets/css/tokenDetails/tokenDetails.scss: -------------------------------------------------------------------------------- 1 | .container-details{ 2 | 3 | .section{ 4 | border-left: 1px solid #e3eef3; 5 | &:first-child{ 6 | border-left: none; 7 | } 8 | 9 | .token-name{ 10 | font-size: 0.9rem; 11 | font-weight: 400; 12 | letter-spacing: 1.1px; 13 | } 14 | 15 | .token-price{ 16 | font-size: 0.75rem; 17 | font-weight: 300; 18 | letter-spacing: 1.1px; 19 | } 20 | 21 | .name{ 22 | text-transform: uppercase; 23 | color: #438bad; 24 | font-size: 0.75rem; 25 | font-weight: 400; 26 | letter-spacing: 1.1px; 27 | } 28 | 29 | .value{ 30 | font-size: 0.8rem; 31 | font-weight: 300; 32 | } 33 | .creation-date{ 34 | font-size: 10px; 35 | } 36 | 37 | } 38 | 39 | @media (max-width: 576px){ 40 | .section{ 41 | border-left: none; 42 | border-bottom: 1px solid #e3eef3; 43 | padding-top: 15px; 44 | padding-bottom: 15px; 45 | } 46 | } 47 | } 48 | 49 | .container-summary{ 50 | .card{ 51 | border: none; 52 | .card-img-top{ 53 | z-index: 1; 54 | } 55 | .card-body{ 56 | background: #f1f5f8; 57 | margin-top: -20px; 58 | .ost-alpha-badge, 59 | .ost-badge{ 60 | position: absolute; 61 | right: 0; 62 | top: 76px; 63 | } 64 | h3.name{ 65 | color: #438bad; 66 | font-size: 1rem; 67 | font-weight: 500; 68 | text-transform: uppercase; 69 | } 70 | .value{ 71 | color: #e4b030; 72 | font-size: 0.9rem; 73 | font-weight: 500; 74 | text-transform: uppercase; 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /lib/formatter/entities/tokenTransfer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class TokenTransfer extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param transferData 18 | * @return {Promise} 19 | */ 20 | async perform(transferData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(transferData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(transferData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param transferData 34 | * @return {{}} 35 | */ 36 | formatEntity(transferData) { 37 | let formattedTransferData = {}; 38 | 39 | formattedTransferData['transactionHash'] = transferData['transactionHash']; 40 | formattedTransferData['fromAddress'] = transferData['fromAddress']; 41 | formattedTransferData['toAddress'] = transferData['toAddress']; 42 | formattedTransferData['amount'] = transferData['amount']; 43 | formattedTransferData['timestamp'] = transferData['timeStamp'] || null; 44 | formattedTransferData['eventIndex'] = transferData['eventIndex']; 45 | formattedTransferData['contractAddress'] = transferData['contractAddress']; 46 | formattedTransferData['chainId'] = transferData['chainId']; 47 | 48 | return formattedTransferData; 49 | } 50 | 51 | /** 52 | * responseDefinition 53 | * 54 | * @return {string[]} 55 | */ 56 | responseDefinition() { 57 | const oThis = this, 58 | mandatoryRootLevelKeys = [ 59 | 'transactionHash', 60 | 'fromAddress', 61 | 'toAddress', 62 | 'amount', 63 | 'contractAddress', 64 | 'eventIndex', 65 | 'chainId' 66 | ]; 67 | return mandatoryRootLevelKeys; 68 | } 69 | } 70 | 71 | module.exports = new TokenTransfer(); 72 | -------------------------------------------------------------------------------- /views/partials/ui/transactionTable.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | TX # 6 |
7 |
8 | TX TIME 9 |
10 |
11 | FROM 12 |
13 |
14 |
15 | TO 16 |
17 |
18 | TX COST ({{ostCurrencySymbol}}) 19 |
20 |
21 |
22 | 23 |
24 | 54 |
55 |
56 | -------------------------------------------------------------------------------- /assets/js/plugins/jquery-visible/README.markdown: -------------------------------------------------------------------------------- 1 | Element Onscreen Visibility 2 | =========================== 3 | 4 | This is a [jQuery](http://jquery.com/) plugin which allows us to quickly check if an element 5 | is within the browsers [visual viewport](http://www.quirksmode.org/mobile/viewports.html), 6 | regardless of the scroll position. If a user can see this element, the function will return true. 7 | 8 | 9 | Documentation 10 | ------------- 11 | ### Basic visibility check 12 | 13 | This basic check will return `true` if the entire element is visible to the user (within the visual viewport). 14 | 15 | $('#element').visible(); 16 | 17 | If you'd like to check for ANY PART of the element, you can use the following: 18 | 19 | $('#element').visible( true ); 20 | 21 | The plugin ignores the elements visibility by default. E.g., `display:none`, `visibility: hidden`, `offsetWidth` or `offsetHeight` is 0). 22 | To filter on css visibility, you can use the jQuery `:visible` selector: 23 | 24 | $('#element:visible').visible(); 25 | 26 | Optionally, you can specify a second parameter to the `.visible` plugin, which will check whether the element is visible, as well as 27 | whether it's within the viewport too. 28 | 29 | $('#element:visible').visible( false, true ); 30 | 31 | Optionally, you can add a third parameter to specify the direction to check for visibility. This can either be 'horizontal', 'vertical' or 'both'. 32 | Default is to 'both'. 33 | 34 | $('#element').visible( false, false, 'horizontal' ); 35 | 36 | 37 | Demos 38 | ----- 39 | 40 | The Demos for this plugin live under the examples/ directory. Open them directly in your web browser, or view the following online examples: 41 | 42 | - [Basic Demo](http://opensource.teamdf.com/visible/examples/demo-basic.html) 43 | 44 | See the blog article: 45 | 46 | - [Checking if an element is visible on-screen using jQuery](https://www.customd.com/articles/13/checking-if-an-element-is-visible-on-screen-using-jquery) 47 | 48 | 49 | Limitations 50 | ----------- 51 | 52 | Currently, this plugin will not check for visibility in nested scrollable areas, only on the main viewport (window object). 53 | -------------------------------------------------------------------------------- /routes/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * About related routes. 4 | * Base url for all routes given below is: base_url = / 5 | * 6 | * @module Explorer Routes - About 7 | */ 8 | const express = require('express'); 9 | 10 | // Express router to mount block related routes 11 | const router = express.Router({ mergeParams: true }); 12 | 13 | // load all internal dependencies 14 | const rootPrefix = '..', 15 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 16 | sanitizer = require(rootPrefix + '/helpers/sanitizer'), 17 | baseRoutes = require(rootPrefix + '/lib/globalConstant/baseRoutes'), 18 | handlebarHelper = require(rootPrefix + '/helpers/handlebarHelper'), 19 | coreConstants = require(rootPrefix + '/config/coreConstants'), 20 | canonicalConstant = require(rootPrefix + '/lib/globalConstant/canonical'); 21 | 22 | // Render final response 23 | const renderResult = function(requestResponse, responseObject, contentType) { 24 | return requestResponse.renderResponse(responseObject, requestResponse.isSuccess() ? 200 : 500, contentType); 25 | }; 26 | 27 | router.get('/', sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 28 | let rawResponse = { 29 | meta: { 30 | baseUrlPrefix: coreConstants.BASE_URL_PREFIX, 31 | urlTemplates: baseRoutes.getAllUrls(), 32 | currencySymbol: handlebarHelper.ostCurrencySymbol(true) 33 | }, 34 | page_meta: { 35 | title: 'OST VIEW | About', 36 | description: 'About OST VIEW, the home grown block explorer from OST for OpenST Utility Blockchains.', 37 | keywords: 'OST, Simple Token, Utility Chain, Blockchain', 38 | canonical: canonicalConstant.forAbout(), 39 | robots: 'index, follow', 40 | image: `${coreConstants.CLOUD_FRONT_BASE_DOMAIN}/ost-view/images/ost-view-og-image-1.jpg` 41 | }, 42 | title: 'OST VIEW - About', 43 | constants: { 44 | cloud_front_base_domain: coreConstants.CLOUD_FRONT_BASE_DOMAIN 45 | }, 46 | template: coreConstants.about 47 | }; 48 | 49 | return renderResult(responseHelper.successWithData(rawResponse), res, req.headers['content-type']); 50 | }); 51 | 52 | module.exports = router; 53 | -------------------------------------------------------------------------------- /views/blockDetail.handlebars: -------------------------------------------------------------------------------- 1 | {{>pageTitle title="Block Details"}} 2 | 3 |
4 |
5 |
6 | 7 |
8 | 9 |
10 |
Block Number
11 | {{block.blockNumber}} 12 |
13 | 14 |
15 |
Chain Id
16 | {{block.chainId}} 17 |
18 | 19 |
20 |
Time
21 | {{toDate block.blockTimestamp}} 22 |
23 | 24 |
25 |
Block Hash
26 | {{block.blockHash}} 27 |
28 | 29 |
30 |
Total Transactions
31 | {{block.totalTransactions}} 32 |
33 | 34 | 35 |
36 |
Gas Used
37 | {{block.gasUsed}} 38 |
39 | 40 |
41 |
Nonce
42 | {{block.nonce}} 43 |
44 |
45 |
46 |
47 |
48 | 49 | {{#when block.totalTransactions '>' 0}} 50 | 51 | {{>blockDetailsDataTable chainId=blockDetails.chainId blockNumber=blockNumber}} 52 | 53 | {{#contentFor "pageScripts"}} 54 | 59 | {{/contentFor}} 60 | 61 | {{/when}} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ost-view", 3 | "version": "2.0.0", 4 | "description": "OST VIEW is the home grown block explorer from OST for OpenST Utility Blockchains.", 5 | "main": "app.js", 6 | "scripts": { 7 | "pre-commit": "lint-staged", 8 | "start": "node app.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/ostdotcom/ost-view.git" 13 | }, 14 | "keywords": [ 15 | "OpenST Blockchains", 16 | "OST", 17 | "Simple Token", 18 | "OST View" 19 | ], 20 | "author": "OST.com Inc.", 21 | "license": "LGPL-3.0", 22 | "bugs": { 23 | "url": "https://github.com/ostdotcom/ost-view/issues" 24 | }, 25 | "homepage": "https://github.com/ostdotcom/ost-view#readme", 26 | "dependencies": { 27 | "@openst/brandedtoken.js": "0.10.0", 28 | "@openst/mosaic.js": "0.10.0", 29 | "@openst/openst.js": "0.10.0", 30 | "@ostdotcom/base": "^2.0.0", 31 | "@ostdotcom/cache": "1.0.7", 32 | "@ostdotcom/ost-block-scanner": "1.0.1", 33 | "@ostdotcom/ost-price-oracle": "1.0.7", 34 | "@ostdotcom/storage": "1.0.4", 35 | "abi-decoder": "1.0.9", 36 | "basic-auth": "2.0.0", 37 | "bignumber.js": "4.1.0", 38 | "body-parser": "1.18.2", 39 | "child_process": "1.0.2", 40 | "commander": "2.13.0", 41 | "connect-assets": "6.0.1", 42 | "continuation-local-storage": "3.2.1", 43 | "debug": "2.6.9", 44 | "express": "4.17.1", 45 | "express-handlebars": "3.0.0", 46 | "handlebars": "4.5.2", 47 | "helmet": "3.21.1", 48 | "jsonwebtoken": "8.1.0", 49 | "moment": "2.20.1", 50 | "morgan": "1.9.1", 51 | "node-sass": "4.12.0", 52 | "sanitize-html": "1.19.3", 53 | "underscore": "1.8.3", 54 | "uuid": "3.1.0", 55 | "web3": "1.0.0-beta.33" 56 | }, 57 | "devDependencies": { 58 | "jsdoc": "3.6.3", 59 | "lint-staged": "9.4.2", 60 | "nodemon": "1.19.4", 61 | "nyc": "14.1.1", 62 | "pre-commit": "1.2.2", 63 | "prettier": "1.18.2", 64 | "eslint": "6.5.1" 65 | }, 66 | "pre-commit": [ 67 | "pre-commit" 68 | ], 69 | "lint-staged": { 70 | "*.js": [ 71 | "prettier --write --config .prettierrc.json", 72 | "git add" 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /helpers/sanitizer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Sanitize request parameters 4 | * 5 | * @module helpers/sanitizer 6 | */ 7 | 8 | const sanitizeHtml = require('sanitize-html'); 9 | 10 | class SanitizeRecursively { 11 | constructor() {} 12 | 13 | /** 14 | * Recursively sanitize 15 | * 16 | * @param params 17 | * 18 | * @returns {*} 19 | * 20 | * @private 21 | */ 22 | sanitize_params_recursively(params) { 23 | const oThis = this; 24 | 25 | if (typeof params === 'string') { 26 | params = oThis._sanitizeString(params); 27 | } else if (params instanceof Array) { 28 | for (let i in params) { 29 | params[i] = oThis.sanitize_params_recursively(params[i]); 30 | } 31 | } else if (params instanceof Object) { 32 | Object.keys(params).forEach(function(key) { 33 | params[key] = oThis.sanitize_params_recursively(params[key]); 34 | }); 35 | } else if (!params) { 36 | params = oThis._sanitizeString(params); 37 | } else { 38 | console.log('Invalid params type: ', typeof params); 39 | params = ''; 40 | } 41 | return params; 42 | } 43 | 44 | /** 45 | * Sanitize string 46 | * 47 | * @param str 48 | * 49 | * @private 50 | */ 51 | _sanitizeString(str) { 52 | return sanitizeHtml(str, { allowedTags: [] }); 53 | } 54 | } 55 | 56 | const sanitizeRecursively = new SanitizeRecursively(); 57 | 58 | class Sanitizer { 59 | constructor() {} 60 | 61 | /** 62 | * Sanitize Request body and request query params 63 | * 64 | * @param req 65 | * @param res 66 | * @param next 67 | * 68 | * @returns {*} 69 | */ 70 | sanitizeBodyAndQuery(req, res, next) { 71 | req.body = sanitizeRecursively.sanitize_params_recursively(req.body); 72 | req.query = sanitizeRecursively.sanitize_params_recursively(req.query); 73 | return next(); 74 | } 75 | 76 | /** 77 | * Sanitize dynamic params in URL 78 | * 79 | * @param req 80 | * @param res 81 | * @param next 82 | * @returns {*} 83 | */ 84 | sanitizeDynamicUrlParams(req, res, next) { 85 | req.params = sanitizeRecursively.sanitize_params_recursively(req.params); 86 | return next(); 87 | } 88 | } 89 | 90 | module.exports = new Sanitizer(); 91 | -------------------------------------------------------------------------------- /lib/providers/cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Shared cache instance provider which is not chain specific. 4 | * 5 | * @module lib/providers/cache 6 | */ 7 | const rootPrefix = '../..', 8 | OSTCache = require('@ostdotcom/cache'), 9 | OSTBase = require('@ostdotcom/base'), 10 | coreConstants = require(rootPrefix + '/config/coreConstants'), 11 | storageConstants = require(rootPrefix + '/lib/globalConstant/storage'); 12 | 13 | const InstanceComposer = OSTBase.InstanceComposer; 14 | 15 | require(rootPrefix + '/lib/formatter/config'); 16 | /** 17 | * Class for cache provider 18 | * 19 | * @class 20 | */ 21 | class CacheProvider { 22 | constructor(configStrategy, instanceComposer) {} 23 | 24 | /** 25 | * Get instance of OST Cache. 26 | * 27 | * @param {String} cacheType 28 | * @param {Number} chainId 29 | * @returns {Object} 30 | */ 31 | getInstance(cacheType, chainId) { 32 | const oThis = this; 33 | return OSTCache.getInstance(oThis.getCacheConfigStrategy(cacheType, chainId)); 34 | } 35 | 36 | /** 37 | * Get cache config strategy 38 | * 39 | * @param {String} cacheType: shared or sharded 40 | * @param {Number} chainId 41 | * @returns {{} & Object} 42 | */ 43 | getCacheConfigStrategy(cacheType, chainId) { 44 | const oThis = this, 45 | blockScannerConfigStrategy = oThis.ic().configStrategy, 46 | configFormatter = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'configFormatter'); 47 | 48 | switch (cacheType) { 49 | case storageConstants.shared: 50 | if (!blockScannerConfigStrategy.cache) { 51 | throw `missing db config for ${cacheType}`; 52 | } 53 | 54 | return Object.assign({}, configFormatter.formatCacheConfig(blockScannerConfigStrategy)); 55 | 56 | case storageConstants.sharded: 57 | let chainConfig = configFormatter.configFor(chainId); 58 | if (!chainConfig) { 59 | throw `missing db config for ${cacheType} - ${chainId} pair`; 60 | } 61 | return Object.assign({}, configFormatter.formatCacheConfig(chainConfig)); 62 | default: 63 | throw `unsupported ${cacheType}`; 64 | } 65 | } 66 | } 67 | 68 | InstanceComposer.registerAsObject(CacheProvider, coreConstants.icNameSpace, 'cacheProvider', true); 69 | -------------------------------------------------------------------------------- /routes/stats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Stats route.

4 | * 5 | * 6 | * @module Explorer Routes - Stats 7 | */ 8 | const express = require('express'); 9 | 10 | // Express router to mount search related routes 11 | const router = express.Router({ mergeParams: true }); 12 | 13 | const rootPrefix = '..', 14 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 15 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 16 | JwtAuthentication = require(rootPrefix + '/lib/Authentication/jwt'), 17 | sanitizer = require(rootPrefix + '/helpers/sanitizer'), 18 | coreConstants = require(rootPrefix + '/config/coreConstants'), 19 | routeHelper = require(rootPrefix + '/routes/helper'); 20 | 21 | // Render final response 22 | const renderResult = function(requestResponse, responseObject, contentType) { 23 | return requestResponse.renderResponse(responseObject, 200, contentType); 24 | }; 25 | 26 | /** 27 | * Stats route 28 | * 29 | * @name Stats route 30 | * 31 | * @route {GET} {base_url} 32 | * 33 | */ 34 | router.get('/', JwtAuthentication.authenticate, sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 35 | fetchHomeData(req, res, next); 36 | }); 37 | 38 | function fetchHomeData(req, res, next) { 39 | require(rootPrefix + '/app/services/home/GetHomePageStats'); 40 | 41 | return routeHelper.performer(req, res, next, 'GetHomePageStats', 'r_s_1').then(function(requestResponse) { 42 | if (requestResponse.isSuccess()) { 43 | processHomeDetailsResponse(requestResponse.data, req, res); 44 | } else { 45 | logger.log(req.originalUrl + ' : ' + requestResponse.err.code); 46 | return renderResult( 47 | responseHelper.error(requestResponse.err.code, coreConstants.DEFAULT_DATA_NOT_AVAILABLE_TEXT), 48 | res, 49 | req.headers['content-type'] 50 | ); 51 | } 52 | }); 53 | } 54 | 55 | function processHomeDetailsResponse(requestResponse, req, res) { 56 | let rawResponse = { 57 | stats: { 58 | totalCommunities: requestResponse.totalEconomies || 0, 59 | totalTokenHolders: requestResponse.totalTokenHolders || 0, 60 | totalTokenTransfers: requestResponse.totalTokenTransfers || 0 61 | } 62 | }; 63 | 64 | return renderResult(responseHelper.successWithData(rawResponse), res, 'application/json'); 65 | } 66 | 67 | module.exports = router; 68 | -------------------------------------------------------------------------------- /lib/formatter/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Config formatter 4 | * 5 | * @module lib/formatter/config 6 | */ 7 | const rootPrefix = '../..', 8 | OSTBase = require('@ostdotcom/base'), 9 | coreConstants = require(rootPrefix + '/config/coreConstants'); 10 | 11 | const InstanceComposer = OSTBase.InstanceComposer; 12 | 13 | /** 14 | * Class for config formatter 15 | * 16 | * @class 17 | */ 18 | class ConfigFormatter { 19 | /** 20 | * Constructor for config formatter 21 | * 22 | * @param {Object} configStrategy 23 | * @param {Object} instanceComposer 24 | * @constructor 25 | */ 26 | constructor(configStrategy, instanceComposer) { 27 | const oThis = this; 28 | let chainIdConfigMap = {}; 29 | for (let i = 0; i < configStrategy.chains.length; i++) { 30 | let currConfig = configStrategy.chains[i]; 31 | 32 | chainIdConfigMap[currConfig.chainId] = currConfig; 33 | } 34 | oThis.chainIdConfigMap = chainIdConfigMap; 35 | } 36 | 37 | /** 38 | * Get config for a particular chain id 39 | * 40 | * @param {Number} chainId: chain Id to find config for 41 | * @returns {Object}: config for a particular chain id 42 | */ 43 | configFor(chainId) { 44 | const oThis = this; 45 | 46 | return oThis.chainIdConfigMap[chainId]; 47 | } 48 | 49 | /** 50 | * Format cache config 51 | * 52 | * @returns {Object}: returns formatted config 53 | */ 54 | formatCacheConfig(config) { 55 | 56 | return config; 57 | } 58 | 59 | /** 60 | * Format storage config 61 | * 62 | * @param {Object} config: config to format 63 | * @returns {Object}: returns formatted config 64 | */ 65 | formatStorageConfig(config) { 66 | config.storage.enableDax = 0; 67 | 68 | return config; 69 | } 70 | /** 71 | * return extra column config for a given table name 72 | * 73 | * @param {Object} tableIdentifier 74 | * 75 | * @returns {Object} 76 | */ 77 | getExtraColumnConfigFor(tableIdentifier) { 78 | const oThis = this, 79 | configStrategy = oThis.ic().configStrategy, 80 | extraStorageColumns = configStrategy['extraStorageColumns'] || {}; 81 | return extraStorageColumns[tableIdentifier] || {}; 82 | } 83 | } 84 | 85 | InstanceComposer.registerAsObject(ConfigFormatter, coreConstants.icNameSpace, 'configFormatter', true); 86 | 87 | module.exports = ConfigFormatter; 88 | -------------------------------------------------------------------------------- /views/partials/details.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |

{{token.name}} ({{token.symbol}})

8 |
9 |
10 |
11 |
base erc20 token
12 |
{{baseCurrencySymbol token baseCurrencies}}
13 |
14 |
15 |
utility branded token contract
16 | {{>address class="text-truncate value w-100 d-inline-block" chainId=token.chainId address=token.contractAddress}} 17 |
18 |
19 |
decimal
20 |
{{token.decimals}}
21 |
22 |
23 |
24 |
25 |
gateway contract 26 | 27 | 28 |
29 | {{token.gatewayContractAddress}} 32 |
33 |
34 |
value branded token contract 35 | 36 | 37 |
38 | {{token.valueBrandedToken}} 41 |
42 |
43 |
44 | 45 |
46 |
47 |
48 | 49 |
50 | 51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /helpers/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Basic helper functions 4 | * 5 | * @module helpers/basic 6 | */ 7 | const rootPrefix = '..', 8 | paramErrorConfig = require(rootPrefix + '/config/error/param'), 9 | bigNumber = require('bignumber.js'), 10 | generalErrorConfig = require(rootPrefix + '/config/error/general'); 11 | 12 | /** 13 | * Basic helper methods class 14 | * 15 | * @class 16 | */ 17 | class BasicHelperKlass { 18 | constructor() {} 19 | 20 | /** 21 | * Deep duplicate 22 | * 23 | * @param {Object} object: object to deep duplicate 24 | * @returns {Object}: returns deep duplicated object 25 | */ 26 | deepDup(object) { 27 | return JSON.parse(JSON.stringify(object)); 28 | } 29 | 30 | /** 31 | * Invert 32 | * 33 | * @param {Object} object: object to invert 34 | * @returns {Object}: returns the inverted object 35 | */ 36 | invert(object) { 37 | let ret = {}; 38 | for (let key in object) { 39 | ret[object[key]] = key; 40 | } 41 | return ret; 42 | } 43 | 44 | /** 45 | * Convert number to big number. Make sure it's a valid number 46 | * 47 | * @param {Number} number: number to be formatted 48 | * 49 | * @returns {BigNumber} 50 | */ 51 | convertToBigNumber(number) { 52 | return number instanceof bigNumber ? number : new bigNumber(number); 53 | } 54 | 55 | /** 56 | * Get error config 57 | * 58 | * @returns {Object} 59 | */ 60 | getErrorConfig() { 61 | return { 62 | param_error_config: paramErrorConfig, 63 | api_error_config: generalErrorConfig 64 | }; 65 | } 66 | 67 | /** 68 | * Math library 69 | * 70 | * @param lvalue 71 | * @param operator 72 | * @param rvalue 73 | * @return {string} 74 | */ 75 | math(lvalue, operator, rvalue) { 76 | if (!rvalue || !lvalue) { 77 | return ''; 78 | } 79 | 80 | lvalue = new bigNumber(lvalue.toString()); 81 | rvalue = new bigNumber(rvalue.toString()); 82 | 83 | var value = { 84 | '+': lvalue.plus(rvalue), 85 | '-': lvalue.minus(rvalue), 86 | '*': lvalue.times(rvalue), 87 | '/': lvalue.dividedBy(rvalue), 88 | '%': lvalue.modulo(rvalue) 89 | }[operator]; 90 | 91 | if (isNaN(value)) { 92 | return '0'; 93 | } else { 94 | return new bigNumber(value.toString()).toFormat(5); 95 | } 96 | } 97 | } 98 | 99 | module.exports = new BasicHelperKlass(); 100 | -------------------------------------------------------------------------------- /assets/css/transactionDetails/transactionDetails.scss: -------------------------------------------------------------------------------- 1 | .key-color { 2 | color: #5d6d6e; 3 | } 4 | 5 | .link-color{ 6 | color: #179eb1; 7 | } 8 | 9 | .container-token-details { 10 | 11 | .info-card { 12 | border: none; 13 | font-size: 14px; 14 | 15 | .card-body { 16 | padding: 0.8rem; 17 | background-color: #f1f5f8; 18 | } 19 | } 20 | 21 | .details-card{ 22 | 23 | border-radius: 2px; 24 | font-size: 14px; 25 | 26 | .card-body{ 27 | padding: 0.8rem; 28 | &:nth-of-type(odd){ 29 | background-color: #f1f5f8; 30 | } 31 | 32 | .transaction-status-1{ 33 | color: #78a124; 34 | } 35 | 36 | .transaction-status-2{ 37 | color: #a13628; 38 | } 39 | 40 | .token-icon { 41 | border-radius: 50%; 42 | width: 1.5rem; 43 | } 44 | 45 | .coin-amount { 46 | background-color: #9cc5c1; 47 | color: #ffffff; 48 | border-radius: 0.3rem; 49 | padding: 0.16rem; 50 | } 51 | 52 | .input-data-break-all{ 53 | word-break: break-all; 54 | } 55 | .transfer-table-wrapper{ 56 | > div { 57 | &:nth-of-type(even){ 58 | background-color: #f9fbfd; 59 | } 60 | } 61 | .to-arrow{ 62 | color: #179eb1; 63 | } 64 | .show-more-link{ 65 | padding-bottom: 3px; 66 | border-bottom: 1px solid #179eb1; 67 | &:hover{ 68 | text-decoration: none; 69 | } 70 | } 71 | .tx-fee{ 72 | font-weight: 300; 73 | margin-left: 8px; 74 | padding: 2px 8px; 75 | border-radius: 2px; 76 | } 77 | } 78 | } 79 | } 80 | 81 | .card-key { 82 | color: #5d6d6e; 83 | min-width: 150px; 84 | display: inline-block; 85 | 86 | @media (max-width: 576px){ 87 | display: block; 88 | min-width: inherit; 89 | } 90 | } 91 | } 92 | 93 | .token-table { 94 | tr { 95 | background-color: #ffffff !important; 96 | } 97 | } -------------------------------------------------------------------------------- /app/services/home/TopTokens.js: -------------------------------------------------------------------------------- 1 | /* 2 | * TopTokens - Service to get top tokens 3 | * 1. First page from cache 4 | * 2. Rest from DB 5 | */ 6 | 7 | const rootPrefix = '../../..', 8 | OSTBase = require('@ostdotcom/base'), 9 | coreConstants = require(rootPrefix + '/config/coreConstants'), 10 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 11 | responseHelper = require(rootPrefix + '/lib/formatter/response'); 12 | 13 | const InstanceComposer = OSTBase.InstanceComposer; 14 | 15 | require(rootPrefix + '/app/services/home/GetTopTokens'); 16 | require(rootPrefix + '/lib/cacheManagement/TopTokensCache'); 17 | 18 | class TopTokens { 19 | /** 20 | * constructor 21 | * 22 | * @params 23 | * @param [paginationIdentifier] {String} - Identifier for pagination 24 | */ 25 | constructor(params) { 26 | const oThis = this; 27 | 28 | oThis.paginationIdentifier = params.paginationIdentifier; 29 | } 30 | 31 | /** 32 | * perform 33 | * 34 | */ 35 | perform() { 36 | const oThis = this; 37 | 38 | return oThis.asyncPerform().catch(function(err) { 39 | logger.error(' In catch block of app/services/home/TopTokens.js'); 40 | return responseHelper.error('s_h_tt_1', 'something_went_wrong', err); 41 | }); 42 | } 43 | 44 | /** 45 | * asyncPerform 46 | * 47 | * @return {Promise} 48 | */ 49 | async asyncPerform() { 50 | const oThis = this; 51 | 52 | if (!oThis.paginationIdentifier) { 53 | return oThis.getFirstPage(); 54 | } else { 55 | return oThis.getTopTokensFromService(); 56 | } 57 | 58 | return responseHelper.successWithData(result); 59 | } 60 | 61 | /** 62 | * getFirstPage 63 | */ 64 | async getFirstPage() { 65 | const oThis = this, 66 | TopTokensCache = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'TopTokensCache'), 67 | topTokensCache = new TopTokensCache(); 68 | 69 | let response = await topTokensCache.fetch(); 70 | 71 | return responseHelper.successWithData(response.data); 72 | } 73 | 74 | /** 75 | * getTopTokensFromService 76 | */ 77 | async getTopTokensFromService() { 78 | const oThis = this, 79 | GetTopTokens = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'GetTopTokens'), 80 | getTopTokens = new GetTopTokens({ 81 | paginationIdentifier: oThis.paginationIdentifier 82 | }); 83 | 84 | return getTopTokens.perform(); 85 | } 86 | } 87 | 88 | InstanceComposer.registerAsShadowableClass( TopTokens, coreConstants.icNameSpace, 'TopTokens'); 89 | 90 | module.exports = TopTokens; 91 | -------------------------------------------------------------------------------- /assets/css/common/common.scss: -------------------------------------------------------------------------------- 1 | body{ 2 | color: #597A84; 3 | } 4 | pre{ 5 | white-space: pre; 6 | } 7 | .divider{ 8 | margin-top: 0; 9 | margin-bottom: 0; 10 | width:100%; 11 | border: 0; 12 | border-top: 1px solid #e3eef3; 13 | } 14 | 15 | .circle { 16 | width: 60px; 17 | min-width: 60px; 18 | height: 60px; 19 | border-radius: 50%; 20 | font-size: 1.1rem; 21 | color: #fff; 22 | line-height: 60px; 23 | text-align: center; 24 | background: #cccccc; 25 | text-transform: uppercase; 26 | 27 | &.token_icon_1 { 28 | background-color: #92e1d1; 29 | } 30 | 31 | &.token_icon_2 { 32 | background-color: #34445b; 33 | } 34 | 35 | &.token_icon_3 { 36 | background-color: #e4b030; 37 | } 38 | 39 | &.token_icon_4 { 40 | background-color: #9064a2; 41 | } 42 | 43 | &.token_icon_5 { 44 | background-color: #84d1d4; 45 | } 46 | 47 | &.token_icon_6 { 48 | background-color: #ff5f59; 49 | } 50 | } 51 | .circle-sm{ 52 | width: 40px; 53 | min-width: 40px; 54 | height: 40px; 55 | line-height: 40px; 56 | font-size: 0.7rem; 57 | } 58 | .circle-xs{ 59 | width: 30px; 60 | min-width: 30px; 61 | height: 30px; 62 | line-height: 30px; 63 | font-size: 0.6rem; 64 | } 65 | 66 | .badge{ 67 | font-size: 90%; 68 | } 69 | .badge-primary { 70 | background: #9cc5c1; 71 | &.inflow-0{ 72 | background: #FF5F5A; 73 | } 74 | } 75 | a, 76 | a:hover, 77 | a:visited{ 78 | color: #179eb1; 79 | } 80 | .btn-secondary{ 81 | background-color: #ffffff; 82 | border: solid 1px #34445b; 83 | color: #34445b; 84 | &:hover{ 85 | color: #ffffff; 86 | } 87 | } 88 | a.btn-secondary{ 89 | color: #34445b; 90 | } 91 | .loader{ 92 | background: url('https://dxwfxs8b4lg24.cloudfront.net/ost-kit/images/processed-loader-1.gif'); 93 | height: 60px; 94 | width: 60px; 95 | margin: 0 auto; 96 | background-size: 60px; 97 | } 98 | 99 | .jTableLoader { 100 | position: absolute; 101 | top: 0; 102 | left: 0; 103 | height: 100%; 104 | width: 100%; 105 | display: none; 106 | z-index: 1; 107 | opacity: 0.5; 108 | background: #ffffff; 109 | } 110 | 111 | .txHash { 112 | color: #179eb1; 113 | } 114 | 115 | .old-version-link { 116 | &:hover{ 117 | text-decoration: underline; 118 | } 119 | } 120 | 121 | .current-version { 122 | font-size: 0.7rem; 123 | position: relative; 124 | top: -10px; 125 | left: -7px; 126 | color: #02BAD4; 127 | } -------------------------------------------------------------------------------- /lib/formatter/entities/token.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class Token extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param tokenData 18 | * @return {Promise} 19 | */ 20 | async perform(tokenData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(tokenData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(tokenData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param tokenData 34 | * @return {{}} 35 | */ 36 | formatEntity(tokenData) { 37 | let formattedTokenData = {}; 38 | 39 | formattedTokenData.contractAddress = tokenData.contractAddress; 40 | formattedTokenData.sortEconomyBy = tokenData.sortEconomyBy; 41 | formattedTokenData.marketCap = tokenData.marketCap; 42 | formattedTokenData.chainId = tokenData.chainId; 43 | formattedTokenData.totalSupply = tokenData.totalSupply; 44 | formattedTokenData.updatedTimestamp = tokenData.updatedTimestamp; 45 | formattedTokenData.name = tokenData.displayName; 46 | formattedTokenData.symbol = tokenData.displaySymbol; 47 | formattedTokenData.tokenSymbol = tokenData.symbol; 48 | formattedTokenData.decimals = tokenData.decimals; 49 | formattedTokenData.conversionFactor = tokenData.conversionFactor; 50 | formattedTokenData.createdTimestamp = tokenData.createdTimestamp; 51 | formattedTokenData.totalVolume = tokenData.totalVolume || null; 52 | formattedTokenData.gatewayContractAddress = tokenData.gatewayContractAddress; 53 | formattedTokenData.valueBrandedToken = tokenData.originContractAddress; 54 | formattedTokenData.totalTransfers = tokenData.totalTokenTransfers; 55 | formattedTokenData.totalTokenHolders = tokenData.totalTokenHolders; 56 | formattedTokenData.baseCurrencyContractAddress = tokenData.baseCurrencyContractAddress; 57 | 58 | return formattedTokenData; 59 | } 60 | 61 | /** 62 | * responseDefinition 63 | * 64 | * @return {string[]} 65 | */ 66 | responseDefinition() { 67 | const oThis = this, 68 | mandatoryRootLevelKeys = [ 69 | 'contractAddress', 70 | 'sortEconomyBy', 71 | 'marketCap', 72 | 'chainId', 73 | 'totalSupply', 74 | 'updatedTimestamp', 75 | 'name', 76 | 'symbol', 77 | 'decimals', 78 | 'createdTimestamp', 79 | 'conversionFactor' 80 | ]; 81 | return mandatoryRootLevelKeys; 82 | } 83 | } 84 | 85 | module.exports = new Token(); 86 | -------------------------------------------------------------------------------- /lib/models/tableCreation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * This script is used table creation. 4 | * 5 | * Usage: node lib/models/tableCreation 6 | * 7 | * @module lib/models/tableCreation 8 | */ 9 | const program = require('commander'); 10 | 11 | const rootPrefix = '../..', 12 | OSTBase = require('@ostdotcom/base'), 13 | coreConstants = require(rootPrefix + '/config/coreConstants'), 14 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 15 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'); 16 | 17 | const InstanceComposer = OSTBase.InstanceComposer; 18 | 19 | // Following require(s) for registering into instance composer 20 | 21 | require(rootPrefix + '/lib/models/GlobalStats'); 22 | 23 | program.option('--configFile ', 'config strategy absolute file path').parse(process.argv); 24 | 25 | program.on('--help', () => { 26 | logger.log(''); 27 | logger.log(' Example:'); 28 | logger.log(''); 29 | logger.log(" node lib/models/tableCreation.js --configFile './config.json'"); 30 | logger.log(''); 31 | logger.log(''); 32 | }); 33 | 34 | /** 35 | * Constructor for initial setup 36 | * 37 | * @class 38 | */ 39 | class TableCreation { 40 | constructor(params) {} 41 | 42 | /** 43 | * Main performer method for the class. 44 | * 45 | * @returns {Promise} 46 | */ 47 | perform() { 48 | const oThis = this; 49 | 50 | oThis.asyncPerform().catch(function(err) { 51 | logger.error(' In catch block of lib/models/tableCreation::perform'); 52 | return responseHelper.error({ 53 | internal_error_identifier: 'l_m_1', 54 | api_error_identifier: 'something_went_wrong', 55 | debug_options: err, 56 | error_config: {} 57 | }); 58 | }); 59 | } 60 | 61 | /** 62 | * Async performer. 63 | * 64 | * @returns {Promise} 65 | */ 66 | async asyncPerform() { 67 | const oThis = this, 68 | GlobalStats = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'GlobalStats'), 69 | globalStatsObject = new GlobalStats({}); 70 | 71 | // Create GlobalStats Table 72 | await globalStatsObject.createTable(); 73 | } 74 | } 75 | 76 | InstanceComposer.registerAsObject(TableCreation, coreConstants.icNameSpace, 'TableCreation', true); 77 | 78 | /** 79 | * This method performs certain validations on the input params. 80 | */ 81 | const validateAndSanitize = function() { 82 | if (!program.configFile) { 83 | program.help(); 84 | process.exit(1); 85 | } 86 | }; 87 | 88 | validateAndSanitize(); 89 | 90 | const config = require(program.configFile), 91 | instanceComposer = new InstanceComposer(config), 92 | setupInit = instanceComposer.getInstanceFor(coreConstants.icNameSpace, 'TableCreation'); 93 | setupInit.perform(); 94 | -------------------------------------------------------------------------------- /app/services/transfer/GetDetails.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GetBlockDetails - Service for getting block details 3 | * 4 | */ 5 | 6 | const rootPrefix = '../../..', 7 | OSTBase = require('@ostdotcom/base'), 8 | coreConstants = require(rootPrefix + '/config/coreConstants'), 9 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 10 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 11 | CommonValidator = require(rootPrefix + '/lib/validators/Common'); 12 | 13 | const InstanceComposer = OSTBase.InstanceComposer; 14 | 15 | require(rootPrefix + '/lib/providers/blockScanner'); 16 | 17 | class GetTransferDetails { 18 | /** 19 | * constructor 20 | * 21 | * @param {Number} chainId - chain id of the transactionHash 22 | * @param {Array} transferIdentifiers - {txHash: [eventIndexes]} 23 | */ 24 | constructor(params) { 25 | const oThis = this; 26 | 27 | oThis.chainId = params.chainId; 28 | oThis.transferIdentifiers = params.transferIdentifiers; 29 | } 30 | 31 | /** 32 | * perform 33 | * 34 | * @return {Promise|*} 35 | */ 36 | perform() { 37 | const oThis = this; 38 | 39 | return oThis.asyncPerform().catch(function(err) { 40 | logger.error(' In catch block of app/services/transfer/GetDetails.js'); 41 | return responseHelper.error('s_tt_gd_1', 'something_went_wrong', err); 42 | }); 43 | } 44 | 45 | /** 46 | * asyncPerform 47 | * 48 | * @return {Promise<*>} 49 | */ 50 | async asyncPerform() { 51 | const oThis = this; 52 | 53 | let response = await oThis.validateAndSanitize(); 54 | 55 | if (response.isFailure()) return response; 56 | 57 | return oThis.getTransferDetails(); 58 | } 59 | 60 | /** 61 | * validateAndSanitize 62 | * 63 | * @return {Promise<*>} 64 | */ 65 | async validateAndSanitize() { 66 | const oThis = this; 67 | 68 | if (!CommonValidator.isVarInteger(oThis.chainId)) { 69 | return responseHelper.error('s_tt_gd_2', 'chainId missing'); 70 | } 71 | 72 | if (!oThis.transferIdentifiers) { 73 | return responseHelper.error('s_tt_gd_3', 'transferIdentifiers missing'); 74 | } 75 | 76 | return responseHelper.successWithData({}); 77 | } 78 | 79 | /** 80 | * getTransferDetails - get transfer details from block scanner 81 | * 82 | */ 83 | async getTransferDetails() { 84 | const oThis = this, 85 | blockScannerProvider = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 86 | blockScanner = blockScannerProvider.getInstance(), 87 | TransferGet = blockScanner.transfer.Get; 88 | 89 | let transferGet = new TransferGet(oThis.chainId, oThis.transferIdentifiers); 90 | 91 | let response = await transferGet.perform(); 92 | 93 | return responseHelper.successWithData(response.data); 94 | } 95 | } 96 | 97 | InstanceComposer.registerAsShadowableClass(GetTransferDetails, coreConstants.icNameSpace, 'GetTransferDetails'); 98 | -------------------------------------------------------------------------------- /lib/validators/Common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module for common validators. 3 | * 4 | * @module lib/validators/Common 5 | */ 6 | 7 | /** 8 | * Class for common validators. 9 | * 10 | * @class CommonValidator 11 | */ 12 | class CommonValidator { 13 | /** 14 | * Is var null? 15 | * 16 | * @return {boolean} 17 | */ 18 | static isVarNull(variable) { 19 | return typeof variable === 'undefined' || variable == null; 20 | } 21 | 22 | /** 23 | * Is var true? 24 | * 25 | * @return {boolean} 26 | */ 27 | static isVarTrue(variable) { 28 | return variable === true || variable === 'true'; 29 | } 30 | 31 | /** 32 | * Is var false? 33 | * 34 | * @return {boolean} 35 | */ 36 | static isVarFalse(variable) { 37 | return variable === false || variable === 'false'; 38 | } 39 | 40 | /** 41 | * Is var integer? 42 | * 43 | * @return {boolean} 44 | */ 45 | static isVarInteger(variable) { 46 | if (typeof variable === 'number') { 47 | return variable % 1 === 0; 48 | } 49 | const number = Number(variable); 50 | if (isNaN(number)) { 51 | return false; 52 | } 53 | 54 | return CommonValidator.isVarInteger(number); 55 | } 56 | 57 | /** 58 | * Checks if the given string is an address. 59 | * 60 | * @param {string} address: HEX address 61 | * 62 | * @return {boolean} 63 | */ 64 | static isEthAddressValid(address) { 65 | const oThis = this; 66 | 67 | if (oThis.isVarNull(address) || typeof address !== 'string' || address == '') { 68 | return false; 69 | } 70 | 71 | address = address.trim().toLowerCase(); 72 | 73 | return /^(0x)?[0-9a-f]{40}$/i.test(address); 74 | } 75 | 76 | /** 77 | * Check if transaction hash is valid or not. 78 | * 79 | * @param {string} transactionHash 80 | * 81 | * @return {boolean} 82 | */ 83 | static isTxHashValid(transactionHash) { 84 | if (typeof transactionHash !== 'string') { 85 | return false; 86 | } 87 | 88 | return /^0x[0-9a-fA-F]{64}$/.test(transactionHash); 89 | } 90 | 91 | /** 92 | * Check if token name is valid or not. 93 | * 94 | * @param {string} tokenName: Token name 95 | * 96 | * @return {boolean} 97 | */ 98 | static isTokenNameValid(tokenName) { 99 | if (typeof tokenName !== 'string') { 100 | return false; 101 | } 102 | 103 | return /^[0-9a-zA-Z\s]*$/.test(tokenName); 104 | } 105 | 106 | /** 107 | * Check if uuid is valid or not 108 | * 109 | * @param {string} uuid: UUID of user, branded token etc. 110 | * 111 | * @returns {boolean} 112 | */ 113 | static isUuidValid(uuid) { 114 | if (typeof uuid !== 'string') { 115 | return false; 116 | } 117 | 118 | return /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$/.test(uuid); 119 | } 120 | } 121 | 122 | module.exports = CommonValidator; 123 | -------------------------------------------------------------------------------- /assets/css/common/icons.scss: -------------------------------------------------------------------------------- 1 | $sprite-retina-location: "//dxwfxs8b4lg24.cloudfront.net/ost-view/images/ost-view-sprite@2x-6.png"; 2 | $sprite-location: "//dxwfxs8b4lg24.cloudfront.net/ost-view/images/ost-view-sprite@1x-6.png"; 3 | 4 | @mixin icon-generator($width,$height,$posX,$posY,$offsetHover,$offsetActive,$scale:100) { 5 | background-image: url($sprite-location); 6 | background-size: 1250px * $scale/100 1250px * $scale/100; 7 | @media only screen and (-webkit-min-device-pixel-ratio: 2), 8 | only screen and (min--moz-device-pixel-ratio: 2), 9 | only screen and (-o-min-device-pixel-ratio: 2/1), 10 | only screen and (min-device-pixel-ratio: 2), 11 | only screen and (min-resolution: 192dpi), 12 | only screen and (min-resolution: 2dppx) { 13 | background-image: url($sprite-retina-location); 14 | background-size: 1250px * $scale/100 1250px * $scale/100; 15 | } 16 | display: inline-block; 17 | outline: none !important; 18 | &:focus { 19 | outline: none; 20 | background-position: $posX * $scale/100 $posY * $scale/100; 21 | } 22 | width: $width * $scale/100; 23 | height: $height * $scale/100; 24 | background-position: $posX * $scale/100 $posY * $scale/100; 25 | &:hover { 26 | background-position: $offsetHover * $scale/100 $posY * $scale/100; 27 | } 28 | &:active { 29 | background-position: $offsetActive * $scale/100 $posY * $scale/100; 30 | } 31 | } 32 | 33 | .ost-view-logo.icon{ 34 | @include icon-generator(150px, 30px, -320px, -85px, -320px, -320px); 35 | } 36 | 37 | .communities.icon{ 38 | @include icon-generator(135px, 65px, -285px, -15px, -285px, -285px); 39 | } 40 | 41 | .token-holders.icon{ 42 | @include icon-generator(135px, 65px, -140px, -15px, -140px, -140px); 43 | } 44 | 45 | .market-cap.icon{ 46 | @include icon-generator(135px, 65px, -5px, -10px, -5px, -5px); 47 | } 48 | 49 | .circulating-supply.icon{ 50 | @include icon-generator(135px, 65px, -429px, -15px, -429px, -429px); 51 | } 52 | 53 | .total-supply.icon{ 54 | @include icon-generator(135px, 65px, -570px, -15px, -570px, -570px); 55 | } 56 | 57 | .total-volume.icon{ 58 | @include icon-generator(108px, 65px, -858px, -15px, -858px, -858px); 59 | } 60 | 61 | .token-price-new.icon{ 62 | @include icon-generator(135px, 65px, -976px, -15px, -976px, -976px); 63 | } 64 | 65 | .token-price.icon{ 66 | @include icon-generator(135px, 65px, -714px, -15px, -714px, -714px); 67 | } 68 | 69 | .total-transfers{ 70 | @include icon-generator(140px, 65px, -427px, -15px, -427px, -427px); 71 | } 72 | 73 | .ost-alpha-badge.icon{ 74 | @include icon-generator(60px, 30px, -90px, -85px, -90px, -90px); 75 | } 76 | 77 | .ost-badge.icon{ 78 | @include icon-generator(60px, 30px, -541px, -85px, -540px, -540px); 79 | } 80 | 81 | .search.icon{ 82 | @include icon-generator(20px, 20px, -470px, -97px, -470px, -470px); 83 | } 84 | 85 | .staked-coin.icon{ 86 | @include icon-generator(15px, 15px, -9px, -100px, -9px, -9px); 87 | } 88 | 89 | .time.icon{ 90 | @include icon-generator(15px, 15px, -35px, -100px, -35px, -35px); 91 | } 92 | 93 | .etherscan.icon{ 94 | @include icon-generator(28px, 28px, -503px, -88px, -503px, 503px, 70); 95 | } -------------------------------------------------------------------------------- /contracts/abi/ERC20Token.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposedOwner","type":"address"}],"name":"initiateOwnershipTransfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"proposedOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"completeOwnershipTransfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_symbol","type":"string"},{"name":"_name","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_totalSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_proposedOwner","type":"address"}],"name":"OwnershipTransferInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_newOwner","type":"address"}],"name":"OwnershipTransferCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}] -------------------------------------------------------------------------------- /lib/cacheManagement/TopTokensCache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Cache for getting the first page of top tokens 4 | * 5 | * @module lib/cacheManagement/TopTokensCache 6 | */ 7 | const rootPrefix = '../..', 8 | OSTBase = require('@ostdotcom/base'), 9 | coreConstants = require(rootPrefix + '/config/coreConstants'), 10 | BaseCache = require(rootPrefix + '/lib/cacheManagement/Base'), 11 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 12 | storageConstants = require(rootPrefix + '/lib/globalConstant/storage'); 13 | 14 | const InstanceComposer = OSTBase.InstanceComposer; 15 | 16 | // Following require(s) for registering into instance composer 17 | require(rootPrefix + '/app/services/home/GetTopTokens'); 18 | 19 | /** 20 | * Home page stats cache class 21 | * 22 | * @class 23 | */ 24 | class TopTokensCache extends BaseCache { 25 | /** 26 | * Constructor 27 | * 28 | * @augments BaseCache 29 | * @param {Object} params: cache key generation & expiry related params 30 | * @constructor 31 | */ 32 | constructor() { 33 | super(); 34 | 35 | const oThis = this; 36 | 37 | oThis.consistentBehavior = '1'; 38 | oThis.useObject = true; 39 | 40 | // Call sub class method to set cache key using params provided 41 | oThis.setCacheKey(); 42 | 43 | // Call sub class method to set cache expiry using params provided 44 | oThis.setCacheExpiry(); 45 | 46 | // Call sub class method to set cache implementer using params provided 47 | oThis.setCacheImplementer(); 48 | } 49 | 50 | /** 51 | * Set cache key 52 | * 53 | * @returns {String} 54 | */ 55 | setCacheKey() { 56 | const oThis = this; 57 | 58 | oThis.cacheKey = oThis._cacheKeyPrefix() + oThis.ic().configStrategy.ddbTablePrefix + '_s_tt'; 59 | 60 | return oThis.cacheKey; 61 | } 62 | 63 | /** 64 | * Set cache expiry in oThis.cacheExpiry and return it 65 | * 66 | * @returns {Number} 67 | */ 68 | setCacheExpiry() { 69 | const oThis = this; 70 | 71 | oThis.cacheExpiry = 300; // 5 mins 72 | 73 | return oThis.cacheExpiry; 74 | } 75 | 76 | /** 77 | * Set cache implementer in oThis.cacheImplementer and return it 78 | * 79 | * @returns {Object} 80 | */ 81 | setCacheImplementer() { 82 | const oThis = this, 83 | cacheObject = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'cacheProvider').getInstance(storageConstants.shared); 84 | oThis.cacheImplementer = cacheObject.cacheInstance; 85 | 86 | return oThis.cacheImplementer; 87 | } 88 | 89 | /** 90 | * Fetch data from source 91 | * 92 | * @returns {Result} 93 | */ 94 | async fetchDataFromSource() { 95 | const oThis = this, 96 | GetTopTokens = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'GetTopTokens'), 97 | response = await new GetTopTokens({}).perform(); 98 | 99 | if (response.isFailure()) { 100 | return Promise.reject(responseHelper.error('l_cm_ttc_1', 'something_went_wrong')); 101 | } 102 | 103 | return responseHelper.successWithData(response.data); 104 | } 105 | } 106 | 107 | InstanceComposer.registerAsShadowableClass( TopTokensCache, coreConstants.icNameSpace, 'TopTokensCache'); 108 | 109 | module.exports = TopTokensCache; 110 | -------------------------------------------------------------------------------- /executables/GlobalAggregatorCron.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * This is executable for global aggregator, it aggregates data and gives stats to show on home page. 4 | * 5 | * Usage: node executables/GlobalAggregatorCron.js --configFile $(pwd)/config.json 6 | * 7 | * @module executables/GlobalAggregatorCron 8 | */ 9 | 10 | const rootPrefix = '..', 11 | program = require('commander'), 12 | basicHelper = require(rootPrefix + '/helpers/basic'), 13 | OSTBase = require('@ostdotcom/base'), 14 | coreConstants = require(rootPrefix + '/config/coreConstants'), 15 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 16 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 17 | errorConfig = basicHelper.getErrorConfig(); 18 | 19 | const InstanceComposer = OSTBase.InstanceComposer; 20 | 21 | require(rootPrefix + '/app/services/home/GlobalAggregator'); 22 | 23 | program 24 | .option('--chainId ', 'Chain id') 25 | .option('--configFile ', 'OST View config strategy absolute file path') 26 | .parse(process.argv); 27 | 28 | program.on('--help', function() { 29 | logger.log(''); 30 | logger.log(' Example:'); 31 | logger.log(''); 32 | logger.log(" node executables/GlobalAggregatorCron.js --configFile './config.json'"); 33 | logger.log(''); 34 | logger.log(''); 35 | }); 36 | 37 | class GlobalAggregator { 38 | constructor(params) { 39 | const oThis = this; 40 | oThis.config = require(params.configFile); 41 | } 42 | 43 | /** 44 | * Main performer method for the class. 45 | * 46 | * @returns {Promise<>} 47 | */ 48 | perform() { 49 | const oThis = this; 50 | 51 | return oThis.asyncPerform().catch(function(err) { 52 | logger.error(' In catch block of executables/GlobalAggregatorCron'); 53 | return responseHelper.error({ 54 | internal_error_identifier: 'e_gac_1', 55 | api_error_identifier: 'something_went_wrong', 56 | debug_options: err, 57 | error_config: errorConfig 58 | }); 59 | }); 60 | } 61 | 62 | /** 63 | * Async performer. 64 | * 65 | * @returns {Promise<>} 66 | */ 67 | async asyncPerform() { 68 | const oThis = this, 69 | instanceComposer = new InstanceComposer(oThis.config); 70 | 71 | const GlobalAggregator = instanceComposer.getShadowedClassFor(coreConstants.icNameSpace, 'GlobalAggregator'), 72 | globalAggregatorObj = new GlobalAggregator({}); 73 | 74 | globalAggregatorObj.asyncPerform().then(console.log); 75 | } 76 | 77 | /** 78 | * sleep for particular time 79 | * 80 | * @param ms {number} - time in ms 81 | * 82 | * @returns {Promise} 83 | */ 84 | sleep(ms) { 85 | return new Promise(function(resolve) { 86 | setTimeout(resolve, ms); 87 | }); 88 | } 89 | } 90 | 91 | /** 92 | * This method performs certain validations on the input params. 93 | */ 94 | let validateAndSanitize = function() { 95 | if (!program.configFile) { 96 | program.help(); 97 | process.exit(1); 98 | } 99 | }; 100 | let aggregatorObject = new GlobalAggregator(program); 101 | 102 | aggregatorObject 103 | .perform() 104 | .then(function(a) { 105 | logger.info('DONE======'); 106 | }) 107 | .catch(function(a) { 108 | process.exit(1); 109 | }); 110 | -------------------------------------------------------------------------------- /lib/formatter/entities/transaction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const rootPrefix = '../../..', 4 | BaseFormatter = require(rootPrefix + '/lib/formatter/entities/Base'); 5 | 6 | class Transaction extends BaseFormatter { 7 | /** 8 | * constructor 9 | */ 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * perform 16 | * 17 | * @param transactionData 18 | * @return {Promise} 19 | */ 20 | async perform(transactionData) { 21 | const oThis = this; 22 | 23 | const mandatoryRootLevelKeys = oThis.responseDefinition(); 24 | 25 | await oThis.validateResponse(transactionData, mandatoryRootLevelKeys); 26 | 27 | return Promise.resolve(oThis.formatEntity(transactionData)); 28 | } 29 | 30 | /** 31 | * formatEntity 32 | * 33 | * @param transactionData 34 | * @return {{}} 35 | */ 36 | formatEntity(transactionData) { 37 | let formattedTransactionData = {}; 38 | 39 | if (transactionData.transactionStatus != '1') { 40 | // blank or 0 41 | formattedTransactionData.transactionStatus = transactionData.transactionStatus; 42 | } else { 43 | // 1 44 | formattedTransactionData.transactionStatus = transactionData.transactionInternalStatus != '0' ? '1' : '0'; 45 | } 46 | 47 | formattedTransactionData.value = transactionData.value; 48 | formattedTransactionData.nonce = transactionData.nonce; 49 | formattedTransactionData.blockTimestamp = transactionData.blockTimestamp; 50 | formattedTransactionData.transactionInternalStatus = transactionData.transactionInternalStatus; 51 | formattedTransactionData.blockNumber = transactionData.blockNumber; 52 | formattedTransactionData.transactionHash = transactionData.transactionHash; 53 | formattedTransactionData.fromAddress = transactionData.fromAddress; 54 | formattedTransactionData.transactionIndex = transactionData.transactionIndex; 55 | formattedTransactionData.gasPrice = transactionData.gasPrice; 56 | formattedTransactionData.gasUsed = transactionData.gasUsed; 57 | formattedTransactionData.contractAddress = transactionData.contractAddress || null; 58 | formattedTransactionData.updatedTimestamp = transactionData.updatedTimestamp; 59 | formattedTransactionData.toAddress = transactionData.toAddress || null; 60 | formattedTransactionData.chainId = transactionData.chainId; 61 | formattedTransactionData.totalTokenTransfers = transactionData.totalTokenTransfers; 62 | formattedTransactionData.totalTransferedTokens = transactionData.totalTransferedTokens || null; 63 | formattedTransactionData.inputData = transactionData.inputData || null; 64 | 65 | return formattedTransactionData; 66 | } 67 | 68 | /** 69 | * responseDefinition 70 | * 71 | * @return {string[]} 72 | */ 73 | responseDefinition() { 74 | const oThis = this, 75 | mandatoryRootLevelKeys = [ 76 | 'value', 77 | 'nonce', 78 | 'blockTimestamp', 79 | 'transactionInternalStatus', 80 | 'blockNumber', 81 | 'transactionHash', 82 | 'fromAddress', 83 | 'transactionIndex', 84 | 'gasPrice', 85 | 'gasUsed', 86 | 'transactionStatus', 87 | 'updatedTimestamp', 88 | 'chainId', 89 | 'totalTokenTransfers' 90 | ]; 91 | 92 | return mandatoryRootLevelKeys; 93 | } 94 | } 95 | 96 | module.exports = new Transaction(); 97 | -------------------------------------------------------------------------------- /lib/cacheManagement/HomePageStats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Block number to chain Ids cache 4 | * 5 | * * @module lib/cacheManagement/shared/BlockChainIds 6 | */ 7 | const rootPrefix = '../..', 8 | OSTBase = require('@ostdotcom/base'), 9 | coreConstants = require(rootPrefix + '/config/coreConstants'), 10 | BaseCache = require(rootPrefix + '/lib/cacheManagement/Base'), 11 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 12 | storageConstants = require(rootPrefix + '/lib/globalConstant/storage'); 13 | 14 | const InstanceComposer = OSTBase.InstanceComposer; 15 | 16 | // Following require(s) for registering into instance composer 17 | require(rootPrefix + '/app/services/home/GetDetails'); 18 | 19 | /** 20 | * Home page stats cache class 21 | * 22 | * @class 23 | */ 24 | class HomePageStatsCache extends BaseCache { 25 | /** 26 | * Constructor for block number to chainIds cache 27 | * 28 | * @augments BaseCache 29 | * @param {Object} params: cache key generation & expiry related params 30 | * @constructor 31 | */ 32 | constructor() { 33 | super(); 34 | 35 | const oThis = this; 36 | 37 | oThis.consistentBehavior = '1'; 38 | oThis.useObject = true; 39 | 40 | // Call sub class method to set cache key using params provided 41 | oThis.setCacheKey(); 42 | 43 | // Call sub class method to set cache expiry using params provided 44 | oThis.setCacheExpiry(); 45 | 46 | // Call sub class method to set cache implementer using params provided 47 | oThis.setCacheImplementer(); 48 | } 49 | 50 | /** 51 | * Set cache key 52 | * 53 | * @returns {String} 54 | */ 55 | setCacheKey() { 56 | const oThis = this; 57 | 58 | oThis.cacheKey = oThis._cacheKeyPrefix() + oThis.ic().configStrategy.ddbTablePrefix + '_s_hps'; 59 | 60 | return oThis.cacheKey; 61 | } 62 | 63 | /** 64 | * Set cache expiry in oThis.cacheExpiry and return it 65 | * 66 | * @returns {Number} 67 | */ 68 | setCacheExpiry() { 69 | const oThis = this; 70 | 71 | oThis.cacheExpiry = 300; // 5 mins 72 | 73 | return oThis.cacheExpiry; 74 | } 75 | 76 | /** 77 | * Set cache implementer in oThis.cacheImplementer and return it 78 | * 79 | * @returns {Object} 80 | */ 81 | setCacheImplementer() { 82 | const oThis = this, 83 | cacheObject = oThis 84 | .ic() 85 | .getInstanceFor(coreConstants.icNameSpace, 'cacheProvider') 86 | .getInstance(storageConstants.shared); 87 | 88 | oThis.cacheImplementer = cacheObject.cacheInstance; 89 | 90 | return oThis.cacheImplementer; 91 | } 92 | 93 | /** 94 | * Fetch data from source 95 | * 96 | * @returns {Result} 97 | */ 98 | async fetchDataFromSource() { 99 | const oThis = this, 100 | GetHomeStats = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'GetHomeDetails'), 101 | getHomeStats = new GetHomeStats(), 102 | response = await getHomeStats.perform(); 103 | 104 | if (response.isFailure()) { 105 | return Promise.reject(responseHelper.error('l_cm_s_bcid_1', 'something_went_wrong')); 106 | } 107 | return Promise.resolve(response); 108 | } 109 | } 110 | 111 | InstanceComposer.registerAsShadowableClass(HomePageStatsCache, coreConstants.icNameSpace, 'HomePageStatsCache'); 112 | 113 | module.exports = HomePageStatsCache; 114 | -------------------------------------------------------------------------------- /lib/cacheMultiManagement/BaseCurrency.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Economy details cache 4 | * 5 | * @module lib/cacheMultiManagement/shared/Economy 6 | */ 7 | const rootPrefix = '../..', 8 | basicHelper = require(rootPrefix + '/helpers/basic'), 9 | OSTBase = require('@ostdotcom/base'), 10 | coreConstants = require(rootPrefix + '/config/coreConstants'), 11 | BaseCache = require(rootPrefix + '/lib/cacheMultiManagement/Base'), 12 | cacheManagementConst = require(rootPrefix + '/lib/globalConstant/cacheManagement'); 13 | 14 | const InstanceComposer = OSTBase.InstanceComposer; 15 | 16 | // Following require(s) for registering into instance composer 17 | require(rootPrefix + '/lib/models/BaseCurrency'); 18 | 19 | /** 20 | * Class for economy contract address cache 21 | * 22 | * @class 23 | */ 24 | class BaseCurrencyCache extends BaseCache { 25 | /** 26 | * Constructor for economy contract address cache 27 | * 28 | * @augments BaseCache 29 | * 30 | * @param {Object} params: cache key generation & expiry related params 31 | * @param {Array} params.baseCurrencyContractAddresses 32 | * 33 | * @constructor 34 | */ 35 | constructor(params) { 36 | super(params); 37 | 38 | const oThis = this; 39 | 40 | oThis.baseCurrencyContractAddresses = params['baseCurrencyContractAddresses']; 41 | oThis.consistentBehavior = '1'; 42 | oThis.useObject = true; 43 | oThis.cacheType = cacheManagementConst.inMemory; 44 | 45 | // Call sub class method to set cache key using params provided 46 | oThis.setCacheKeys(); 47 | 48 | // Call sub class method to set cache expiry using params provided 49 | oThis.setCacheExpiry(); 50 | 51 | // Call sub class method to set cache implementer using params provided 52 | oThis.setCacheImplementer(); 53 | } 54 | 55 | /** 56 | * Set cache key 57 | * 58 | * @returns {{}} 59 | */ 60 | setCacheKeys() { 61 | const oThis = this; 62 | 63 | for (let i = 0; i < oThis.baseCurrencyContractAddresses.length; i++) { 64 | oThis.cacheKeys[oThis._cacheKeyPrefix() + 's_bc_' + oThis.baseCurrencyContractAddresses[i].toLowerCase()] = 65 | oThis.baseCurrencyContractAddresses[i]; 66 | } 67 | 68 | oThis.invertedCacheKeys = basicHelper.invert(oThis.cacheKeys); 69 | 70 | return oThis.cacheKeys; 71 | } 72 | 73 | /** 74 | * Set cache expiry in oThis.cacheExpiry and return it 75 | * 76 | * @returns {Number} 77 | */ 78 | setCacheExpiry() { 79 | const oThis = this; 80 | 81 | oThis.cacheExpiry = 24 * 3600; // 24 hour 82 | 83 | return oThis.cacheExpiry; 84 | } 85 | 86 | /** 87 | * Fetch data from source 88 | * 89 | * @param {Array} cacheMissBaseCurrencyContractAddresses 90 | * 91 | * @returns {response} 92 | */ 93 | async fetchDataFromSource(cacheMissBaseCurrencyContractAddresses) { 94 | const oThis = this, 95 | BaseCurrencyModel = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'BaseCurrencyModel'), 96 | response = await new BaseCurrencyModel({ 97 | consistentRead: oThis.consistentRead 98 | }).getData(cacheMissBaseCurrencyContractAddresses); 99 | 100 | return Promise.resolve(response); 101 | } 102 | } 103 | 104 | InstanceComposer.registerAsShadowableClass(BaseCurrencyCache, coreConstants.icNameSpace, 'BaseCurrencyCache'); 105 | 106 | module.exports = BaseCurrencyCache; 107 | -------------------------------------------------------------------------------- /app/services/address/GetBasicDetails.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GetBasicDetails - Service for getting basic details of an address 3 | * 4 | */ 5 | 6 | const rootPrefix = '../../..', 7 | OSTBase = require('@ostdotcom/base'), 8 | coreConstants = require(rootPrefix + '/config/coreConstants'), 9 | addressFormatter = require(rootPrefix + '/lib/formatter/entities/address'), 10 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 11 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 12 | CommonValidator = require(rootPrefix + '/lib/validators/Common'); 13 | 14 | const InstanceComposer = OSTBase.InstanceComposer; 15 | 16 | require(rootPrefix + '/lib/providers/blockScanner'); 17 | 18 | class GetAddressBasicDetails { 19 | /** 20 | * constructor 21 | */ 22 | constructor(params) { 23 | const oThis = this; 24 | 25 | oThis.chainId = params.chainId; 26 | oThis.address = params.address; 27 | } 28 | 29 | /** 30 | * perform 31 | * 32 | */ 33 | perform() { 34 | const oThis = this; 35 | 36 | return oThis.asyncPerform().catch(function(err) { 37 | logger.error(' In catch block of app/services/address/GetBasicDetails.js'); 38 | return responseHelper.error('s_a_gbd_1', 'something_went_wrong'); 39 | }); 40 | } 41 | 42 | /** 43 | * 44 | * @returns {Promise<*|result|Object|Object>} 45 | */ 46 | async asyncPerform() { 47 | const oThis = this; 48 | 49 | let validateAndSanitizeResponse = await oThis.validateAndSanitize(); 50 | 51 | if (validateAndSanitizeResponse.isFailure()) { 52 | return validateAndSanitizeResponse; 53 | } 54 | 55 | return oThis._getAddressDetails(); 56 | } 57 | 58 | /** 59 | * 60 | * @returns {Promise} 61 | */ 62 | async validateAndSanitize() { 63 | const oThis = this; 64 | 65 | if (!CommonValidator.isVarInteger(oThis.chainId)) { 66 | return responseHelper.paramValidationError('s_a_gbd_2', ['chainId']); 67 | } 68 | 69 | if (!CommonValidator.isEthAddressValid(oThis.address)) { 70 | return responseHelper.paramValidationError('s_a_gbd_3', ['address']); 71 | } 72 | 73 | return responseHelper.successWithData({}); 74 | } 75 | 76 | /** 77 | * 78 | * @returns {Promise<*>} 79 | */ 80 | async _getAddressDetails() { 81 | const oThis = this; 82 | 83 | const blockScannerProvider = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 84 | blockScanner = blockScannerProvider.getInstance(), 85 | AddressBasicDetails = blockScanner.address.GetBasicDetails; 86 | 87 | let addressBasicDetails = new AddressBasicDetails(oThis.chainId, oThis.address); 88 | 89 | let addressBasicDetailsResponse = await addressBasicDetails.perform(); 90 | 91 | if (addressBasicDetailsResponse.isFailure()) { 92 | return Promise.resolve(responseHelper.error('s_a_gbd_5', 'block details get service failed')); 93 | } 94 | 95 | let addressBasicDetailsInfo = addressBasicDetailsResponse.data[oThis.address]; 96 | 97 | addressBasicDetailsInfo['chainId'] = oThis.chainId; 98 | 99 | let address = await addressFormatter.perform(addressBasicDetailsInfo); 100 | 101 | return responseHelper.successWithData(address); 102 | } 103 | } 104 | 105 | InstanceComposer.registerAsShadowableClass(GetAddressBasicDetails, coreConstants.icNameSpace, 'GetAddressBasicDetails'); 106 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Index route.

4 | * Base url for all routes given below is: base_url = / 5 | * 6 | * @module Explorer Routes - Index 7 | */ 8 | const express = require('express'); 9 | 10 | // Express router to mount search related routes 11 | const router = express.Router({ mergeParams: true }); 12 | 13 | const rootPrefix = '..', 14 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 15 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 16 | sanitizer = require(rootPrefix + '/helpers/sanitizer'), 17 | handlebarHelper = require(rootPrefix + '/helpers/handlebarHelper'), 18 | coreConstants = require(rootPrefix + '/config/coreConstants'), 19 | baseRoutes = require(rootPrefix + '/lib/globalConstant/baseRoutes'), 20 | routeHelper = require(rootPrefix + '/routes/helper'), 21 | canonicalConstant = require(rootPrefix + '/lib/globalConstant/canonical'); 22 | 23 | // Render final response 24 | const renderResult = function(requestResponse, responseObject, contentType) { 25 | return requestResponse.renderResponse(responseObject, 200, contentType); 26 | }; 27 | 28 | /** 29 | * Index route 30 | * 31 | * @name Index route 32 | * 33 | * @route {GET} {base_url} 34 | * 35 | */ 36 | router.get('/', sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 37 | fetchHomeData(req, res, next); 38 | }); 39 | 40 | function fetchHomeData(req, res, next) { 41 | require(rootPrefix + '/app/services/home/GetHomePageStats'); 42 | 43 | return routeHelper.performer(req, res, next, 'GetHomePageStats', 'r_a_2').then(function(requestResponse) { 44 | if (requestResponse.isSuccess()) { 45 | processHomeDetailsResponse(requestResponse.data, req, res); 46 | } else { 47 | logger.log(req.originalUrl + ' : ' + requestResponse.err.code); 48 | return renderResult( 49 | responseHelper.error(requestResponse.err.code, coreConstants.DEFAULT_DATA_NOT_AVAILABLE_TEXT), 50 | res, 51 | req.headers['content-type'] 52 | ); 53 | } 54 | }); 55 | } 56 | 57 | function processHomeDetailsResponse(requestResponse, req, res) { 58 | let rawResponse = { 59 | stats: { 60 | totalCommunities: requestResponse.totalEconomies || 0, 61 | totalTokenHolders: requestResponse.totalTokenHolders || 0, 62 | totalTokenTransfers: requestResponse.totalTokenTransfers || 0 63 | }, 64 | meta: { 65 | baseUrlPrefix: coreConstants.BASE_URL_PREFIX, 66 | urlTemplates: baseRoutes.getAllUrls(), 67 | currencySymbol: handlebarHelper.ostCurrencySymbol(true) 68 | }, 69 | title: 'OST View - OST SideChains Explorer and Search', 70 | mCss: ['mHome.css'], 71 | mJs: ['mHome.js'], 72 | view_data: {}, 73 | page_meta: { 74 | title: 'OST VIEW - Block Explorer for OpenST Utility Blockchains', 75 | description: 'OST VIEW is the home grown block explorer from OST for OpenST Utility Blockchains.', 76 | keywords: 'OST, Simple Token, Utility Chain, Blockchain', 77 | canonical: canonicalConstant.forHome(), 78 | robots: 'index, follow', 79 | image: `${coreConstants.CLOUD_FRONT_BASE_DOMAIN}/ost-view/images/ost-view-og-image-1.jpg` 80 | }, 81 | constants: { 82 | cloud_front_base_domain: coreConstants.CLOUD_FRONT_BASE_DOMAIN 83 | }, 84 | template: coreConstants.home 85 | }; 86 | 87 | return renderResult(responseHelper.successWithData(rawResponse), res, req.headers['content-type']); 88 | } 89 | 90 | module.exports = router; 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OST View 2 | 3 | OST VIEW is the home grown multi-chain block explorer from OST for OpenST Utility Blockchains. 4 | 5 | To parse blockchains, OST VIEW uses the [OST Block Scanner](https://github.com/ostdotcom/ost-block-scanner/). 6 | 7 | ## Install 8 | 9 | ```bash 10 | git clone https://github.com/ostdotcom/ost-view.git 11 | cd ost-view/ 12 | npm install 13 | ``` 14 | 15 | ## Setup 16 | 17 | ### 1. Install Prerequisites 18 | - [nodejs](https://nodejs.org/) >= 8.0.0 19 | - [Geth](https://github.com/ethereum/go-ethereum/) >=1.8.17 20 | - [Memcached](https://memcached.org/) 21 | - AWS DynamoDB Service OR [DynamoDBLocal.jar](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html) 22 | - [Java](https://www.java.com/) >= 6.0, if using DynamoDBLocal.jar 23 | - [DB Browser for SQLite](https://sqlitebrowser.org/) optionally to browse DynamoDB 24 | 25 | ### 2. Run DynamoDBLocal.jar, if you are not using AWS DynamoDB Service 26 | 27 | ```bash 28 | # NOTE: Make sure to change DYNAMODB_PATH 29 | export DYNAMODB_PATH=~/dynamodb_local_latest 30 | java -Djava.library.path=$DYNAMODB_PATH/DynamoDBLocal_lib/ -jar $DYNAMODB_PATH/DynamoDBLocal.jar -sharedDb -dbPath $DYNAMODB_PATH/ 31 | ``` 32 | 33 | ### 3. Create OST VIEW configuration file 34 | Refer to [configuration.json.example](configuration.json.example) to create a new configuration file. 35 | 36 | ### 4. Create Global DynamoDB tables: 37 | 38 | ```bash 39 | node ./node_modules/@ostdotcom/ost-block-scanner/tools/initialSetup.js --configFile $(pwd)/configuration.json 40 | ``` 41 | 42 | ### 5. Add a new chain and create chain-specific shared DynamoDB tables: 43 | * Mandatory parameters: `chainId`, `networkId`, `configFile` 44 | * Optional parameters (each defaults to 1): 45 | * `blockShardCount`: number of block shards to be created 46 | * `economyAddressShardCount`: number of economy address shards to be created 47 | * `transactionShardCount`: number of transaction shards to be created 48 | 49 | ```bash 50 | # NOTE: 51 | # Make sure chain configuration is present in configuration file before starting this step. 52 | # Optional parameters are used to create entity-specific sharded tables. 53 | # By default only one shard is created for each entity. 54 | node ./node_modules/@ostdotcom/ost-block-scanner/tools/addChain.js --configFile $(pwd)/configuration.json --chainId 2000 --networkId 1 --blockShardCount 2 --economyAddressShardCount 2 --transactionShardCount 2 55 | ``` 56 | 57 | _For additional configuration options, please see documentation for the [OST Block Scanner](https://github.com/ostdotcom/ost-block-scanner/)._ 58 | 59 | ### 6. Add Global Stats DynamoDB table: 60 | 61 | ```bash 62 | node lib/models/tableCreation.js --configFile $(pwd)/configuration.json 63 | ``` 64 | 65 | ## Start Block Scanner 66 | * Mandatory parameters: `chainId`, `configFile` 67 | * Optional parameters: startBlockNumber, endBlockNumber 68 | ```bash 69 | node ./node_modules/@ostdotcom/ost-block-scanner/executables/blockScanner.js --configFile $(pwd)/configuration.json --chainId 2000 --startBlockNumber 0 --endBlockNumber 100 70 | ``` 71 | 72 | 73 | ## Start Global Stats Aggregator 74 | 75 | ```bash 76 | node executables/GlobalAggregatorCron.js --configFile $(pwd)/configuration.json 77 | ``` 78 | 79 | ## Start OST VIEW application server 80 | 81 | ```bash 82 | npm start 83 | ``` 84 | 85 | With the browser of your choice, visit `http://:`. 86 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true 6 | }, 7 | extends: 'eslint:recommended', 8 | globals: { 9 | Atomics: 'readonly', 10 | SharedArrayBuffer: 'readonly' 11 | }, 12 | parserOptions: { 13 | ecmaVersion: 2018, 14 | sourceType: 'module' 15 | }, 16 | rules: { 17 | 'linebreak-style': ['error', 'unix'], 18 | quotes: ['error', 'single'], 19 | semi: ['error', 'always'], 20 | 'no-var': 'error', 21 | 'block-scoped-var': 'error', 22 | curly: 'error', 23 | 'default-case': 'error', 24 | 'dot-notation': 'error', 25 | 'no-empty-function': 'error', 26 | 'no-else-return': 'error', 27 | 'no-eval': 'error', 28 | 'no-extra-bind': 'error', 29 | 'no-extra-label': 'error', 30 | 'no-implied-eval': 'error', 31 | 'no-invalid-this': 'error', 32 | 'no-multi-spaces': 'error', 33 | 'no-new': 'error', 34 | 'no-new-func': 'error', 35 | 'no-new-wrappers': 'error', 36 | 'no-return-assign': 'error', 37 | 'no-return-await': 'error', 38 | 'no-self-compare': 'error', 39 | 'no-sequences': 'error', 40 | 'no-throw-literal': 'error', 41 | 'no-useless-call': 'error', 42 | 'no-useless-catch': 'error', 43 | 'no-useless-concat': 'error', 44 | 'no-with': 'error', 45 | 'no-void': 'error', 46 | 'prefer-promise-reject-errors': 'error', 47 | yoda: 'error', 48 | 'prefer-const': 'error', 49 | //"prefer-arrow-callback": "error", 50 | 'handle-callback-err': 'error', 51 | 'no-async-promise-executor': 'error', 52 | 'no-case-declarations': 'error', 53 | 'no-shadow': 'error', 54 | 'no-undef-init': 'error', 55 | 'no-undefined': 'error', 56 | 'no-use-before-define': 'error', 57 | 'no-new-require': 'error', 58 | 'no-process-env': 'error', 59 | 'no-sync': 'error', 60 | strict: 'error', 61 | 'arrow-spacing': 'error', 62 | 'no-floating-decimal': 'error', 63 | 'no-buffer-constructor': 'error', 64 | // "no-mixed-requires": ["error", { "grouping": true }], 65 | 'max-classes-per-file': ['error', 1], 66 | 'arrow-body-style': ['error', 'as-needed'], 67 | 'no-confusing-arrow': ['error', { allowParens: true }], 68 | 'no-duplicate-imports': 'error', 69 | 'no-useless-computed-key': 'error', 70 | 'no-useless-constructor': 'error', 71 | 'no-useless-rename': 'error', 72 | // "prefer-destructuring": "error", 73 | // "prefer-template": "error", 74 | 'capitalized-comments': ['error'], 75 | 'consistent-this': ['error', 'oThis'], 76 | 'eol-last': ['error', 'always'], 77 | 'id-length': ['error', { min: 2 }], 78 | 'key-spacing': ['error', { beforeColon: false, afterColon: true }], 79 | 'lines-around-comment': ['error', { beforeBlockComment: false, beforeLineComment: false }], 80 | 'lines-between-class-members': ['error', 'always'], 81 | 'no-array-constructor': 'error', 82 | 'max-depth': ['error', 4], 83 | 'max-lines-per-function': ['error', { max: 80, skipBlankLines: true, skipComments: true }], 84 | 'no-lonely-if': 'error', 85 | 'no-multiple-empty-lines': 'error', 86 | 'no-negated-condition': 'error', 87 | 'no-new-object': 'error', 88 | 'no-trailing-spaces': 'error', 89 | 'no-unneeded-ternary': 'error', 90 | 'operator-assignment': ['error', 'always'], 91 | 'padding-line-between-statements': ['error', { blankLine: 'always', prev: '*', next: 'return' }], 92 | 'spaced-comment': ['error', 'always', { exceptions: ['-', '+'] }] 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /assets/js/common/TokenTable.js: -------------------------------------------------------------------------------- 1 | ;(function (window, $) { 2 | 3 | var TokenTable = function ( config ) { 4 | 5 | var oThis = this; 6 | 7 | oThis.dtConfig = { 8 | "bLengthChange": false, 9 | "searching": false, 10 | "processing": true, 11 | "serverSide": true, 12 | "paging":true, 13 | "responsive": true, 14 | "autoWidth": false, 15 | "pagingType": 'simple', 16 | "language": { 17 | "info": "", 18 | "infoEmpty": "", 19 | "infoFiltered": "", 20 | "processing": "
", 21 | "paginate": { 22 | "next": "❯", 23 | "previous" : "❮" 24 | } 25 | }, 26 | "columns": [], 27 | }; 28 | 29 | $.extend( true, oThis, config ); 30 | 31 | return oThis.loadDataTable(); 32 | 33 | }; 34 | 35 | TokenTable.prototype = { 36 | 37 | constructor: TokenTable, 38 | selector: null, 39 | ajaxURL: null, 40 | dtConfig: null, 41 | 42 | loadDataTable: function(){ 43 | var oThis = this; 44 | var meta; 45 | var payload; 46 | var previousStartIndex; 47 | 48 | oThis.dtConfig.ajax = function (data, callback, settings) { 49 | 50 | // Disabling pagination while ajax is in process 51 | $(settings.nTableWrapper).find('.dataTables_paginate').css({pointerEvents: 'none'}); 52 | 53 | var currentStart = settings.oAjaxData.start; 54 | 55 | if (meta !== undefined) { 56 | 57 | if (currentStart > previousStartIndex) { 58 | payload = {page_payload: meta.next_page_payload}; 59 | 60 | if(Object.keys(meta.next_page_payload).length <= 1) { 61 | $(settings.nTableWrapper).find('.dataTables_processing').hide(); 62 | return; 63 | } 64 | 65 | } else { 66 | payload = {page_payload: meta.prev_page_payload}; 67 | } 68 | 69 | } 70 | 71 | $.ajax({ 72 | url: oThis.ajaxURL, 73 | data: payload, 74 | contentType: "application/json", 75 | success: function (response) { 76 | 77 | if(response.data){ 78 | meta = response.data.meta; 79 | oThis.responseReceived.apply( oThis, arguments ); 80 | previousStartIndex = settings.oAjaxData.start; 81 | 82 | var recordsFilteredCount = 0; 83 | 84 | if(Object.keys(meta.next_page_payload).length > 1){ 85 | recordsFilteredCount = settings.oAjaxData.start + settings.oAjaxData.length + 1; 86 | }else{ 87 | recordsFilteredCount = settings.oAjaxData.start + settings.oAjaxData.length 88 | } 89 | 90 | callback({ 91 | data: response.data[response.data.result_type], 92 | meta: response.data.meta, 93 | recordsFiltered: recordsFilteredCount, 94 | }); 95 | 96 | // Enabling pagination after ajax completion 97 | $(settings.nTableWrapper).find('.dataTables_paginate').css({pointerEvents: 'inherit'}); 98 | }else { 99 | callback({ 100 | data: '', 101 | meta: '', 102 | recordsFiltered: '', 103 | }); 104 | } 105 | 106 | } 107 | }) 108 | }; 109 | 110 | return $(oThis.selector).DataTable(oThis.dtConfig); 111 | }, 112 | 113 | responseReceived: function ( response ) { 114 | 115 | } 116 | 117 | }; 118 | 119 | window.TokenTable = TokenTable; 120 | 121 | })(window, jQuery); 122 | -------------------------------------------------------------------------------- /routes/tokenDetailsBySymbol.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Token details by symbol related routes.

3 | * Base url for all routes given below is: base_url = /:tokenSymbol 4 | * 5 | * @module routes/tokenDetailsBySymbol 6 | */ 7 | const express = require('express'); 8 | 9 | // Express router to mount contract related routes. 10 | const router = express.Router({ mergeParams: true }); 11 | 12 | // Load all internal dependencies. 13 | const rootPrefix = '..', 14 | routeHelper = require(rootPrefix + '/routes/helper'), 15 | sanitizer = require(rootPrefix + '/helpers/sanitizer'), 16 | coreConstants = require(rootPrefix + '/config/coreConstants'), 17 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 18 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 19 | baseRoutes = require(rootPrefix + '/lib/globalConstant/baseRoutes'), 20 | canonicalConstant = require(rootPrefix + '/lib/globalConstant/canonical'); 21 | 22 | // Render final response. 23 | const renderResult = function(requestResponse, responseObject, contentType) { 24 | return requestResponse.renderResponse(responseObject, requestResponse.isSuccess() ? 200 : 500, contentType); 25 | }; 26 | 27 | function processTokenDetailsResponse(response, req, res) { 28 | const tokenDetails = response, 29 | baseCurrenciesDetails = response.baseCurrencies; 30 | 31 | const rawResponse = { 32 | token: tokenDetails, 33 | baseCurrencies: baseCurrenciesDetails, 34 | meta: { 35 | baseUrlPrefix: coreConstants.BASE_URL_PREFIX, 36 | urlTemplates: baseRoutes.getAllUrls() 37 | } 38 | }; 39 | 40 | const webViewResponse = { 41 | page_meta: { 42 | title: `OST VIEW | Economy Details - ${tokenDetails.contractAddress}`, 43 | description: `OST VIEW is the home grown block explorer from OST for OpenST Utility Blockchains. Economy Details - ${ 44 | tokenDetails.contractAddress 45 | }`, 46 | keywords: 'OST, Simple Token, Utility Chain, Blockchain', 47 | robots: 'index, nofollow', 48 | canonical: canonicalConstant.forEconomy(response.tokenSymbol), 49 | image: `${coreConstants.CLOUD_FRONT_BASE_DOMAIN}/ost-view/images/ost-view-og-image-1.jpg` 50 | }, 51 | mCss: ['mTokenDetails.css'], 52 | mJs: ['mTokenDetails.js'], 53 | title: `Economy Details - ${tokenDetails.contractAddress}`, 54 | constants: { 55 | cloud_front_base_domain: coreConstants.CLOUD_FRONT_BASE_DOMAIN 56 | }, 57 | template: coreConstants.tokenDetails 58 | }; 59 | 60 | if (req.headers['content-type'] !== 'application/json') { 61 | Object.assign(rawResponse, webViewResponse); 62 | } 63 | 64 | return renderResult(responseHelper.successWithData(rawResponse), res, req.headers['content-type']); 65 | } 66 | 67 | /** 68 | * Get token details by symbol. 69 | * 70 | * @name Token details by symbol. 71 | * 72 | * @route {GET} {base_url}/:tokenSymbol 73 | * 74 | * @routeparam {Number} :tokenSymbol - Token symbol 75 | */ 76 | router.get('/', sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 77 | require(rootPrefix + '/app/services/contract/GetDetailsBySymbol'); 78 | 79 | routeHelper.performer(req, res, next, 'GetTokenDetailsBySymbol', 'r_tbs_1').then(function(requestResponse) { 80 | if (requestResponse.isSuccess()) { 81 | processTokenDetailsResponse(requestResponse.data, req, res); 82 | } else { 83 | logger.log(req.originalUrl + ' : ' + requestResponse.err.code); 84 | 85 | return renderResult( 86 | responseHelper.error(requestResponse.err.code, coreConstants.DEFAULT_DATA_NOT_AVAILABLE_TEXT), 87 | res, 88 | req.headers['content-type'] 89 | ); 90 | } 91 | }); 92 | }); 93 | 94 | module.exports = router; 95 | -------------------------------------------------------------------------------- /lib/formatter/response.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Restful API response formatter 5 | * 6 | * @module lib/formatter/response 7 | */ 8 | 9 | const rootPrefix = '../..', 10 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 11 | coreConstants = require(rootPrefix + '/config/coreConstants'), 12 | baseRoutes = require(rootPrefix + '/lib/globalConstant/baseRoutes'), 13 | path = require('path'); 14 | 15 | function Result(data, errCode, errMsg) { 16 | this.success = typeof errCode === 'undefined'; 17 | 18 | this.data = data || {}; 19 | 20 | if (!this.success) { 21 | this.err = { 22 | code: errCode, 23 | msg: errMsg 24 | }; 25 | } 26 | 27 | // Check if response has success 28 | this.isSuccess = function() { 29 | return this.success; 30 | }; 31 | 32 | // Check if response is not success. More often not success is checked, so adding a method. 33 | this.isFailure = function() { 34 | return !this.isSuccess(); 35 | }; 36 | 37 | // Format data to hash 38 | this.toHash = function() { 39 | var s = {}; 40 | if (this.success) { 41 | s.success = true; 42 | s.data = this.data; 43 | } else { 44 | s.success = false; 45 | if (this.data instanceof Object && Object.keys(this.data).length > 0) { 46 | //Error with data case. 47 | s.data = this.data; 48 | } 49 | s.err = this.err; 50 | } 51 | 52 | return s; 53 | }; 54 | 55 | // Render final error or success response 56 | this.renderResponse = function(res, status, contentType) { 57 | status = status || 200; 58 | res.status(status); 59 | 60 | if (contentType != 'application/json') { 61 | //logger.log("#renderResponse resultType", status, constants[this.data.result_type], this.data); 62 | if (this.err) { 63 | let meta = { 64 | baseUrlPrefix: coreConstants.BASE_URL_PREFIX, 65 | urlTemplates: baseRoutes.getAllUrls() 66 | }; 67 | 68 | this.err.layout = 'errorPage'; 69 | this.err.meta = meta; 70 | logger.log('#Error Response', this.err); 71 | return res.render('layouts/errorPage', this.err); 72 | } 73 | return res.render(this.data.template, this.data); 74 | } 75 | 76 | return res.json(this.toHash()); 77 | }; 78 | } 79 | 80 | /** 81 | * Response helper 82 | * 83 | * @constructor 84 | */ 85 | const ResponseHelper = function() {}; 86 | 87 | ResponseHelper.prototype = { 88 | /** 89 | * Generate success response object 90 | * 91 | * @param {Object} data - data to be formatted 92 | * 93 | * @returns {Object} - formatted success result 94 | */ 95 | successWithData: function(data) { 96 | return new Result(data); 97 | }, 98 | 99 | /** 100 | * Generate error response object 101 | * 102 | * @param {String} errCode - Error Code 103 | * @param {String} errMsg - Error Message 104 | * @param {String} errPrefix - Error Prefix 105 | * 106 | * @returns {Object} - formatted error result 107 | */ 108 | error: function(errCode, errMsg, errPrefix) { 109 | errCode = 'ostView(' + errCode + ')'; 110 | 111 | console.error('### Error ### ' + errCode + ' ###'); 112 | console.error('### Error MSG ### ' + errMsg + ' ###'); 113 | console.error('### Error Details ### ' + errPrefix + ' ###'); 114 | 115 | return new Result({}, errCode, errMsg); 116 | }, 117 | 118 | /** 119 | * return true if the object passed is of Result class 120 | * 121 | * @param {object} obj - object to check instanceof 122 | * 123 | * @return {boolean} 124 | */ 125 | isCustomResult: function(obj) { 126 | return obj instanceof Result; 127 | } 128 | }; 129 | 130 | module.exports = new ResponseHelper(); 131 | -------------------------------------------------------------------------------- /views/partials/ui/searchTemplates.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 32 | 33 | 48 | 49 | 61 | 62 | 76 | 77 | -------------------------------------------------------------------------------- /app/services/block/ChainIds.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ChainIds - Service for getting chain ids where a particular block is present 3 | * 4 | * @module app/services/block/ChainIds 5 | */ 6 | const rootPrefix = '../../..', 7 | OSTBase = require('@ostdotcom/base'), 8 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 9 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 10 | coreConstants = require(rootPrefix + '/config/coreConstants'), 11 | CommonValidator = require(rootPrefix + '/lib/validators/Common'); 12 | 13 | const InstanceComposer = OSTBase.InstanceComposer; 14 | 15 | // Following require(s) for registering into instance composer 16 | require(rootPrefix + '/lib/providers/blockScanner'); 17 | 18 | /** 19 | * Class for getting chainIds. 20 | * 21 | * @class 22 | */ 23 | class GetChainIds { 24 | /** 25 | * Constructor for getting chainIds. 26 | * 27 | * @constructor 28 | */ 29 | constructor(blockNumber) { 30 | const oThis = this; 31 | 32 | oThis.blockNumber = blockNumber; 33 | } 34 | 35 | /** 36 | * Main performer method for the class. 37 | * 38 | * @returns {Promise<*>} 39 | */ 40 | perform() { 41 | const oThis = this; 42 | 43 | return oThis.asyncPerform().catch(function(err) { 44 | logger.error(' In catch block of app/services/block/ChainIds.js'); 45 | return responseHelper.error('s_b_ci_1', 'something_went_wrong'); 46 | }); 47 | } 48 | 49 | /** 50 | * Async performer. 51 | * 52 | * @returns {Promise<*>} 53 | */ 54 | async asyncPerform() { 55 | const oThis = this; 56 | 57 | let validateAndSanitizeResponse = await oThis.validateAndSanitize(); 58 | 59 | if (validateAndSanitizeResponse.isFailure()) { 60 | return validateAndSanitizeResponse; 61 | } 62 | 63 | let blockChainIdsResponse = await oThis.getBlockChainIds(); 64 | 65 | return blockChainIdsResponse; 66 | } 67 | 68 | /** 69 | * This method performs certain validations on the input params. 70 | * 71 | * @returns {Promise<>} 72 | */ 73 | async validateAndSanitize() { 74 | const oThis = this; 75 | 76 | if (!CommonValidator.isVarInteger(oThis.blockNumber)) { 77 | return responseHelper.paramValidationError('s_b_ci_2', ['blockNumber']); 78 | } 79 | 80 | return responseHelper.successWithData({}); 81 | } 82 | 83 | /** 84 | * This method fetches the chainIds which have the blockNumber being passed. 85 | * 86 | * @returns {Promise<*>} 87 | */ 88 | async getBlockChainIds() { 89 | const oThis = this; 90 | let searchResultArray = []; 91 | 92 | const blockScannerProvider = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 93 | blockScanner = blockScannerProvider.getInstance(), 94 | blockGetChainIdsService = blockScanner.block.GetChainIds; 95 | 96 | let blockGetChainIds = new blockGetChainIdsService(oThis.blockNumber), 97 | blockGetChainIdsResponse = await blockGetChainIds.perform().catch(function(err) { 98 | logger.error('Error in block get service'); 99 | return Promise.resolve(responseHelper.error('s_b_ci_4', 'something_went_wrong')); 100 | }); 101 | 102 | for (let index in blockGetChainIdsResponse.data[oThis.blockNumber]) { 103 | let searchResultHash = {}; 104 | searchResultHash['kind'] = coreConstants.blockEntity; 105 | searchResultHash['payload'] = {}; 106 | searchResultHash['payload']['chainId'] = blockGetChainIdsResponse.data[oThis.blockNumber][index]; 107 | searchResultHash['payload']['blockNumber'] = oThis.blockNumber; 108 | searchResultArray.push(searchResultHash); 109 | } 110 | 111 | return Promise.resolve(responseHelper.successWithData(searchResultArray)); 112 | } 113 | } 114 | 115 | InstanceComposer.registerAsShadowableClass(GetChainIds, coreConstants.icNameSpace, 'GetChainIds'); 116 | -------------------------------------------------------------------------------- /assets/css/common/tableComponent.scss: -------------------------------------------------------------------------------- 1 | .container-datatable{ 2 | 3 | .nav-tab-ost{ 4 | 5 | .nav-pills{ 6 | 7 | border-bottom: 1px solid #c6e9ee; 8 | box-shadow: 0px 4px 10px -5px #c6e9ee; 9 | 10 | .nav-link{ 11 | 12 | color: #438bad; 13 | text-transform: uppercase; 14 | font-size: 1rem; 15 | letter-spacing: 1.1px; 16 | 17 | &.active{ 18 | background: none; 19 | font-weight: 500; 20 | position: relative; 21 | &:after{ 22 | content: ''; 23 | z-index: 1; 24 | position: absolute; 25 | width: 0; 26 | height: 0; 27 | margin-left: -0.5em; 28 | bottom: -10px; 29 | left: 50%; 30 | box-sizing: border-box; 31 | border: 6px solid #ffffff; 32 | transform-origin: 0 0; 33 | transform: rotate(-45deg); 34 | box-shadow: -2px 2px 1px 0 #c6e9ee; 35 | } 36 | } 37 | 38 | @media (max-width: 576px){ 39 | font-size: 0.75rem; 40 | letter-spacing: 1px; 41 | padding-left: 0.5rem; 42 | 43 | } 44 | } 45 | 46 | } 47 | 48 | } 49 | 50 | } 51 | 52 | table.token-table { 53 | 54 | thead { 55 | th{ 56 | color: #438bad; 57 | text-transform: uppercase; 58 | font-size: 0.75rem; 59 | font-weight: 500; 60 | letter-spacing: 1px; 61 | border-collapse: collapse !important; 62 | background-color: #ffffff !important; 63 | border: none; 64 | } 65 | } 66 | 67 | 68 | tbody, 69 | tr { 70 | background-color: #fafcfd !important; 71 | } 72 | 73 | th, 74 | td { 75 | padding: 0 !important; 76 | font-size: 0.75rem; 77 | border-top: 1rem solid #ffffff; 78 | a, 79 | a:visited, 80 | a:active, 81 | a:hover { 82 | color: #179eb1; 83 | } 84 | 85 | .cell { 86 | height: 40px; 87 | .symbol:hover{ 88 | text-decoration: none; 89 | } 90 | .badge-primary{ 91 | min-width: 80px; 92 | text-align: left; 93 | } 94 | .ost-value{ 95 | font-size: 0.65rem; 96 | } 97 | .text-truncate { 98 | max-width: 170px 99 | } 100 | @media (max-width: 768px) { 101 | min-width: 80px; 102 | .text-truncate { 103 | max-width: 75px 104 | } 105 | } 106 | } 107 | 108 | .rank{ 109 | height: 100%; 110 | width: 45px; 111 | text-align: center; 112 | font-weight: 300; 113 | font-size: 1rem; 114 | line-height: 40px; 115 | background: #9cc5c1; 116 | color: #fff; 117 | border-right: 5px solid #fff; 118 | } 119 | } 120 | 121 | td.arrow { 122 | @media (max-width: 576px) { 123 | .cell{ 124 | min-width: 30px; 125 | } 126 | } 127 | } 128 | } 129 | 130 | table.dataTable.no-footer { 131 | border-bottom:none!important; 132 | } 133 | 134 | .dataTables_wrapper { 135 | 136 | .dataTables_processing{ 137 | background: inherit; 138 | } 139 | 140 | .dataTables_info{ 141 | @media (min-width: 576px) { 142 | right: 100px; 143 | bottom: 10px; 144 | position: absolute; 145 | } 146 | font-size: 0.7rem; 147 | color: #979797; 148 | } 149 | 150 | .dataTables_paginate{ 151 | 152 | padding-top: 1.25rem; 153 | 154 | .paginate_button{ 155 | 156 | color: #9cc5c1 !important; 157 | background: #ffffff; 158 | border: 1px solid #9cc5c1; 159 | border-radius: inherit; 160 | margin-left: 5px; 161 | padding: 0.2em 1em; 162 | 163 | &:hover{ 164 | color: #ffffff; 165 | background: #9cc5c1; 166 | border: 1px solid #9cc5c1; 167 | } 168 | 169 | &.disabled, 170 | &.disabled:hover{ 171 | color: #acb1ae !important; 172 | background: #f3f3f3; 173 | border: 1px solid #f3f3f3; 174 | } 175 | 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /views/partials/tokenSummary.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 | 8 |
9 | 10 |
11 |

Market Cap

12 |
{{displayToFixed token.marketCap}} {{baseCurrencySymbol token baseCurrencies }}
13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |

Price

23 |
24 | {{inverseDisplayBtToOst token.conversionFactor 5}} {{baseCurrencySymbol token baseCurrencies }} 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Total Supply

36 |
37 | {{toDecimalValueToFixed token.totalSupply token.decimals}} 38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 |

Total Volume

50 |
51 | {{bigNumber_toFromat token.totalVolume}} {{baseCurrencySymbol token baseCurrencies }} 52 |
53 |
54 |
55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 |

Total Transfers

64 |
65 | {{bigNumber_toFromat token.totalTransfers}} 66 |
67 |
68 |
69 |
70 |
71 | 72 |
73 |
74 | 75 |
76 |
77 |

Token Holders

78 |
79 | {{bigNumber_toFromat token.totalTokenHolders}} 80 |
81 |
82 |
83 |
84 |
85 | 86 |
87 | 88 |
89 | 90 |
-------------------------------------------------------------------------------- /app/services/contract/GetDetailsBySymbol.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module to get token details by token symbol. 3 | * 4 | * @module app/services/token/GetDetails 5 | */ 6 | 7 | const OSTBase = require('@ostdotcom/base'); 8 | 9 | const rootPrefix = '../../..', 10 | CommonValidator = require(rootPrefix + '/lib/validators/Common'), 11 | coreConstants = require(rootPrefix + '/config/coreConstants'), 12 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 13 | responseHelper = require(rootPrefix + '/lib/formatter/response'); 14 | 15 | const InstanceComposer = OSTBase.InstanceComposer; 16 | 17 | // Following require(s) for registering into instance composer. 18 | require(rootPrefix + '/lib/providers/blockScanner'); 19 | require(rootPrefix + '/app/services/contract/GetDetails'); 20 | 21 | /** 22 | * Class to get token details by token symbol. 23 | * 24 | * @class GetTokenDetailsBySymbol 25 | */ 26 | class GetTokenDetailsBySymbol { 27 | /** 28 | * Constructor to get token details by token symbol. 29 | * 30 | * @param {object} params 31 | * @param {string} params.tokenSymbol 32 | * 33 | * @constructor 34 | */ 35 | constructor(params) { 36 | const oThis = this; 37 | 38 | oThis.tokenSymbol = params.tokenSymbol; 39 | 40 | oThis.chainId = null; 41 | oThis.contractAddress = null; 42 | } 43 | 44 | /** 45 | * Main performer for class. 46 | * 47 | * @return {Promise|*} 48 | */ 49 | perform() { 50 | const oThis = this; 51 | 52 | return oThis.asyncPerform().catch(function(err) { 53 | logger.error(' In catch block of app/services/token/GetDetails.js'); 54 | 55 | return responseHelper.error('s_t_gd_1', 'something_went_wrong', err); 56 | }); 57 | } 58 | 59 | /** 60 | * Async perform. 61 | * 62 | * @return {Promise<*>} 63 | */ 64 | async asyncPerform() { 65 | const oThis = this; 66 | 67 | const response = await oThis.validateAndSanitize(); 68 | if (response.isFailure()) { 69 | return response; 70 | } 71 | 72 | const tokenDetailsResponse = await oThis.getTokenDetails(); 73 | if (tokenDetailsResponse.isFailure()) { 74 | return tokenDetailsResponse; 75 | } 76 | 77 | const GetContractDetailsService = oThis.ic().getShadowedClassFor(coreConstants.icNameSpace, 'GetContractDetails'); 78 | 79 | return new GetContractDetailsService({ chainId: oThis.chainId, contractAddress: oThis.contractAddress }).perform(); 80 | } 81 | 82 | /** 83 | * Validate and sanitize. 84 | * 85 | * @return {Promise<*>} 86 | */ 87 | async validateAndSanitize() { 88 | const oThis = this; 89 | 90 | if (!CommonValidator.isTokenNameValid(oThis.tokenSymbol)) { 91 | return responseHelper.error('s_t_gd_2', 'Token symbol missing.'); 92 | } 93 | 94 | return responseHelper.successWithData({}); 95 | } 96 | 97 | /** 98 | * Get token details. 99 | * 100 | * @sets oThis.chainId, oThis.contractAddress, oThis.baseCurrencyContractAddress 101 | * 102 | * @returns {Promise} 103 | */ 104 | async getTokenDetails() { 105 | const oThis = this; 106 | 107 | const blockScannerProvider = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 108 | blockScanner = blockScannerProvider.getInstance(), 109 | EconomyService = blockScanner.model.Economy; 110 | 111 | const Economy = new EconomyService({ consistentRead: false }), 112 | economyRsp = await Economy.searchBySymbol(oThis.tokenSymbol); 113 | 114 | if (economyRsp.isSuccess() && economyRsp.data.length === 1) { 115 | const tokenDetails = economyRsp.data[0]; 116 | 117 | oThis.chainId = tokenDetails.chainId; 118 | oThis.contractAddress = tokenDetails.contractAddress; 119 | 120 | return responseHelper.successWithData({}); 121 | } 122 | 123 | return responseHelper.error('s_t_gd_3', 'Token data not found.'); 124 | } 125 | } 126 | 127 | InstanceComposer.registerAsShadowableClass( 128 | GetTokenDetailsBySymbol, 129 | coreConstants.icNameSpace, 130 | 'GetTokenDetailsBySymbol' 131 | ); 132 | -------------------------------------------------------------------------------- /assets/css/common/header.scss: -------------------------------------------------------------------------------- 1 | header.container-fluid{ 2 | box-shadow: 0 1px 6px 3.5px rgba(237, 242, 246, 0.89); 3 | } 4 | 5 | .navbar-ost{ 6 | padding:0; 7 | 8 | .logo{ 9 | height: 30px; 10 | } 11 | 12 | .navbar-nav{ 13 | .nav-item{ 14 | .nav-link, 15 | .dropdown-item{ 16 | font-size: 0.8rem; 17 | color: rgba(0,0,0,.5); 18 | text-transform: uppercase; 19 | } 20 | } 21 | } 22 | 23 | .dropdown-menu{ 24 | margin-top: 13px; 25 | .dropdown-item{ 26 | padding: .5rem 1.5rem; 27 | font-size: 0.8rem; 28 | color: rgba(0,0,0,.5); 29 | text-transform: uppercase; 30 | &:active{ 31 | background-color: transparent; 32 | } 33 | } 34 | .selected{ 35 | background-color:#f6f7f7 ; 36 | } 37 | } 38 | 39 | 40 | .dropdown-toggle:after { 41 | border: 0; 42 | content: ""; 43 | display: inline-block; 44 | background: url("https://dxwfxs8b4lg24.cloudfront.net/ost-kit/images/drop-down-arrow.svg") no-repeat center; 45 | background-size: 75%; 46 | height: 10px; 47 | width: 14px; 48 | position: relative; 49 | top: 4px; 50 | } 51 | 52 | .input-group{ 53 | .input-group-append{ 54 | .input-group-text{ 55 | padding-right: 0.8rem ; 56 | border-top-right-radius: 20px; 57 | border-bottom-right-radius: 20px; 58 | background: #fff; 59 | border-left: none; 60 | } 61 | .btn{ 62 | &:hover, 63 | &:active{ 64 | border-color: #ced4da; 65 | //background: #f1f4f7; 66 | } 67 | &:focus{ 68 | border-color: #ced4da; 69 | box-shadow: none; 70 | } 71 | } 72 | } 73 | input[type=search]{ 74 | padding-left: 1rem ; 75 | padding-right: 0; 76 | border-top-left-radius: 20px; 77 | border-bottom-left-radius: 20px; 78 | border-right: none; 79 | font-size: 0.8rem; 80 | width: 255px; 81 | @media (max-width: 576px){ 82 | width: 200px; 83 | } 84 | &:focus{ 85 | border-color: #ced4da; 86 | outline:none; 87 | box-shadow: none; 88 | } 89 | } 90 | } 91 | 92 | } 93 | 94 | .container-page-title{ 95 | width: 100%; 96 | background: #e2f4f6; 97 | border-top: solid 1px #69d3d9; 98 | h1{ 99 | color: #438bad; 100 | font-size: 1.5rem; 101 | } 102 | } 103 | 104 | .view-search{ 105 | position: relative; 106 | .search-result{ 107 | position: absolute; 108 | z-index: 2; 109 | top: 100%; 110 | width: 100%; 111 | border-radius: 10px; 112 | background-color: #ffffff; 113 | margin-top: 5px; 114 | max-height: 400px; 115 | overflow-y: auto; 116 | box-shadow: 1.5px 1.5px 5px 0px rgba(0, 0, 0, 0.1); 117 | a{ 118 | &:hover{ 119 | display: block; 120 | text-decoration: none; 121 | background-color: #f4f8f9; 122 | } 123 | &.block{ 124 | display: block; 125 | .number{ 126 | font-weight: 500; 127 | color: #34445b; 128 | display: block; 129 | } 130 | .chain-id{ 131 | font-weight: 300; 132 | color: #9b9b9b; 133 | font-size: 13px; 134 | display: block; 135 | } 136 | } 137 | } 138 | } 139 | .search-block-number-wrap{ 140 | &:first-child{ 141 | background-color: #f4f8f9; 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /assets/css/common/table.scss: -------------------------------------------------------------------------------- 1 | 2 | .view-table{ 3 | .table-info-wrapper{ 4 | @media screen and (max-width: 767px){ 5 | overflow-x: scroll; 6 | overflow-y: hidden; 7 | .table-header, 8 | .table-body{ 9 | width: 1000px; 10 | } 11 | } 12 | } 13 | 14 | .table-header{ 15 | padding: 0.6rem 1rem; 16 | font-weight: 500; 17 | color: #438bad; 18 | text-transform: uppercase; 19 | font-size: .9rem; 20 | letter-spacing: 1px; 21 | 22 | 23 | > div{ 24 | //letter-spacing: 1px; 25 | //color: #597a84; 26 | //font-size: 14px; 27 | } 28 | } 29 | .table-body{ 30 | .card-body{ 31 | padding: 1.2rem 1rem !important; 32 | background-color: #fafcfd; 33 | margin-top: 5px; 34 | font-size: 0.9rem; 35 | font-weight: 400; 36 | } 37 | .tx-fee{ 38 | padding: 2px 8px; 39 | font-weight: 500; 40 | //margin-left: 8px; 41 | border-radius: 2px; 42 | &.badge-primary{ 43 | color: #ffffff; 44 | } 45 | } 46 | } 47 | .paginate-section{ 48 | margin-top: 15px; 49 | .paginate-button{ 50 | 51 | color: #9cc5c1; 52 | background: #ffffff; 53 | border: 1px solid #9cc5c1; 54 | border-radius: inherit; 55 | margin-left: 5px; 56 | display: inline-block; 57 | padding: 0.4em 0.8em; 58 | text-decoration: none; 59 | 60 | &:hover{ 61 | color: #ffffff; 62 | background: #9cc5c1; 63 | border: 1px solid #9cc5c1; 64 | } 65 | 66 | &.disabled, 67 | &.disabled:hover{ 68 | color: #acb1ae !important; 69 | background: #f3f3f3; 70 | cursor: default; 71 | border: 1px solid #f3f3f3; 72 | } 73 | 74 | } 75 | } 76 | } 77 | 78 | #homeTopTokensTable { 79 | //.table-header{ 80 | // color: #438bad; 81 | // text-transform: uppercase; 82 | // font-size: .75rem; 83 | // font-weight: 500; 84 | // letter-spacing: 1px; 85 | // border-collapse: collapse!important; 86 | // background-color: #fff!important; 87 | // border: none; 88 | //} 89 | .table-body{ 90 | .card-body{ 91 | //padding: 0.6rem 0 !important; 92 | //font-size: 0.75rem; 93 | //border-top: 1rem solid #ffffff; 94 | //background-color: #fafcfd !important; 95 | a, 96 | a:visited, 97 | a:active, 98 | a:hover { 99 | color: #179eb1; 100 | } 101 | .cell { 102 | height: 40px; 103 | .symbol:hover{ 104 | text-decoration: none; 105 | } 106 | .badge-primary{ 107 | min-width: 80px; 108 | text-align: left; 109 | } 110 | .ost-value{ 111 | font-size: 0.65rem; 112 | } 113 | .text-truncate { 114 | max-width: 170px 115 | } 116 | @media (max-width: 768px) { 117 | min-width: 80px; 118 | .text-truncate { 119 | max-width: 75px 120 | } 121 | } 122 | } 123 | 124 | .rank{ 125 | height: 100%; 126 | width: 45px; 127 | text-align: center; 128 | font-weight: 300; 129 | font-size: 1rem; 130 | line-height: 40px; 131 | background: #9cc5c1; 132 | color: #fff; 133 | border-right: 5px solid #fff; 134 | } 135 | 136 | &.arrow { 137 | @media (max-width: 576px) { 138 | .cell{ 139 | min-width: 30px; 140 | } 141 | } 142 | } 143 | } 144 | } 145 | .paginate-section{ 146 | margin-top: 15px; 147 | .paginate-button{ 148 | 149 | color: #9cc5c1; 150 | background: #ffffff; 151 | border: 1px solid #9cc5c1; 152 | border-radius: inherit; 153 | margin-left: 5px; 154 | padding: 0.4em 0.8em; 155 | text-decoration: none; 156 | 157 | &:hover{ 158 | color: #ffffff; 159 | background: #9cc5c1; 160 | border: 1px solid #9cc5c1; 161 | } 162 | 163 | &.disabled, 164 | &.disabled:hover{ 165 | color: #acb1ae !important; 166 | background: #f3f3f3; 167 | cursor: default; 168 | border: 1px solid #f3f3f3; 169 | } 170 | 171 | } 172 | } 173 | } 174 | 175 | -------------------------------------------------------------------------------- /views/partials/tokenAddressDetailsDataTable.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 | 9 |
10 |
12 |
13 |
14 |
15 |
16 | TX # 17 |
18 |
19 | TX TIME 20 |
21 |
22 | FROM 23 |
24 |
25 |
26 | TO 27 |
28 |
29 | Amount 30 |
31 |
32 |
33 | 34 |
35 | 65 |
66 |
67 | 68 |
69 |
70 | 71 | {{>ui/nextPreviousTemplate}} 72 | 73 | 74 |
75 |
76 | 77 |
78 | 79 |
80 | 81 | {{#contentFor "pageScripts"}} 82 | 87 | {{/contentFor}} -------------------------------------------------------------------------------- /views/layouts/main.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{#if page_meta}}{{page_meta.title}}{{else}}{{title}}{{/if}} 7 | 8 | 9 | {{#if page_meta}} 10 | 11 | 12 | 13 | 14 | {{#if page_meta.canonical}} 15 | 16 | {{/if}} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{/if}} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{{css 'mCommon.css'}}} 48 | {{#each mCss}} 49 | {{{css this}}} 50 | {{/each}} 51 | 52 | 53 | 54 | 55 | 56 | {{>topHeader}} 57 | {{{body}}} 58 | 59 | 60 | 61 | 62 | 63 | {{{js 'mCommon.js'}}} 64 | {{#each mJs}} 65 | {{{js this}}} 66 | {{/each}} 67 | {{{block "pageScripts"}}} 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /executables/finalizer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Finalizer - This executable helps in finalizing a particular block 3 | * 4 | */ 5 | const rootPrefix = '..', 6 | program = require('commander'), 7 | OSTBase = require('@ostdotcom/base'), 8 | coreConstants = require(rootPrefix + '/config/coreConstants'), 9 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 10 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'); 11 | 12 | const InstanceComposer = OSTBase.InstanceComposer; 13 | 14 | require(rootPrefix + '/lib/providers/blockScanner'); 15 | 16 | let sigIntReceived = false, 17 | pendingTasksPresent = false; 18 | 19 | program 20 | .option('--chainId ', 'Chain id') 21 | .option('--configFile ', 'Block scanner config strategy absolute file path') 22 | .option('--blockDelay ', 'No of blocks finalizer should run behind') 23 | .parse(process.argv); 24 | 25 | program.on('--help', function() { 26 | logger.log(''); 27 | logger.log(' Example:'); 28 | logger.log(''); 29 | logger.log(" node executables/finalizer.js --chainId 189 --configFile './config.json' --blockDelay 6"); 30 | logger.log(''); 31 | logger.log(''); 32 | }); 33 | 34 | class Finalizer { 35 | /** 36 | * constructor 37 | * 38 | * @param params 39 | */ 40 | constructor(params) { 41 | const oThis = this; 42 | 43 | oThis.chainId = params.chainId; 44 | oThis.blockDelay = params.blockDelay; 45 | oThis.config = require(params.configFile); 46 | } 47 | 48 | /** 49 | * perform 50 | * 51 | */ 52 | perform() { 53 | const oThis = this; 54 | 55 | return oThis.asyncPerform().catch(function(err) { 56 | pendingTasksPresent = false; 57 | logger.error(' In catch block of executables/finalizer.js'); 58 | return responseHelper.error('e_f_1', 'something_went_wrong', err); 59 | }); 60 | } 61 | 62 | /** 63 | * asyncPerform 64 | * 65 | * @return {Promise} 66 | */ 67 | async asyncPerform() { 68 | const oThis = this; 69 | 70 | while (true) { 71 | let response = await oThis.startFinalizing(); 72 | 73 | if (!response.data.blockProcessable) { 74 | logger.win('====Block not processable. Waiting for 2 secs...'); 75 | await oThis.waitForNextBlock(); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * startFinalizing 82 | * 83 | * @return {Promise} 84 | */ 85 | async startFinalizing() { 86 | const oThis = this, 87 | instanceComposer = new InstanceComposer(oThis.config), 88 | blockScannerProvider = instanceComposer.getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 89 | blockScanner = blockScannerProvider.getInstance(), 90 | Finalizer = blockScanner.block.Finalize; 91 | 92 | while (true) { 93 | let finalizer = new Finalizer({ 94 | chainId: oThis.chainId, 95 | blockDelay: oThis.blockDelay 96 | }); 97 | 98 | pendingTasksPresent = true; 99 | 100 | let response = await finalizer.perform(); 101 | 102 | if (response.data.hasOwnProperty('blockProcessable') && !response.data.blockProcessable) { 103 | pendingTasksPresent = false; 104 | return response; 105 | } 106 | 107 | if (response.isFailure()) { 108 | pendingTasksPresent = false; 109 | break; 110 | } 111 | } 112 | 113 | return responseHelper.successWithData({}); 114 | } 115 | 116 | async waitForNextBlock() { 117 | return new Promise(function(resolve, reject) { 118 | setTimeout(resolve, 2000); 119 | }); 120 | } 121 | } 122 | 123 | /** 124 | * This method performs certain validations on the input params. 125 | */ 126 | let validateAndSanitize = function() { 127 | if (!program.chainId || !program.configFile || !program.blockDelay) { 128 | program.help(); 129 | process.exit(1); 130 | } 131 | }; 132 | 133 | validateAndSanitize(); 134 | 135 | let finalizer = new Finalizer(program); 136 | 137 | finalizer 138 | .perform() 139 | .then(function(a) { 140 | process.exit(0); 141 | }) 142 | .catch(function(a) { 143 | process.exit(1); 144 | }); 145 | 146 | let sigIntHandler = function() { 147 | sigIntReceived = true; 148 | 149 | if (pendingTasksPresent) { 150 | logger.warn(':: There are pending tasks. Waiting for completion.'); 151 | setTimeout(sigIntHandler, 1000); 152 | } else { 153 | logger.warn(':: No pending tasks. Killing the process. '); 154 | process.exit(0); 155 | } 156 | }; 157 | 158 | process.on('SIGINT', sigIntHandler); 159 | process.on('SIGTERM', sigIntHandler); 160 | -------------------------------------------------------------------------------- /views/partials/topHeader.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 56 | 57 |
58 | 59 |
60 | 61 | {{>ui/searchTemplates}} 62 | 63 | {{#contentFor "pageScripts"}} 64 | 71 | 72 | {{/contentFor}} 73 | 74 | -------------------------------------------------------------------------------- /lib/contractInteract/contractDecoder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File to decode the transfer event logs from the LogsArray provided. 3 | * 4 | * @module lib/contract_interact/contractDecoder 5 | */ 6 | 7 | // Load external libraries 8 | const brandedToken = require('@openst/brandedtoken.js'), 9 | openSt = require('@openst/openst.js'), 10 | mosaic = require('@openst/mosaic.js'), 11 | priceOracle = require('@ostdotcom/ost-price-oracle'), 12 | abiDecoder = require('abi-decoder'); 13 | 14 | // Load internal files 15 | const rootPrefix = '../..', 16 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'); 17 | 18 | const brandedTokenAbiBinProvider = new brandedToken.AbiBinProvider(), 19 | openStAbiBinProvider = new openSt.AbiBinProvider(), 20 | mosaicAbiBinProvider = new mosaic.AbiBinProvider(), 21 | priceOracleAbiBinProvider = new priceOracle.AbiBinProvider(); 22 | 23 | // Initialize decoder needed ABIs 24 | const abiList = [ 25 | brandedTokenAbiBinProvider.getABI('UtilityBrandedToken'), 26 | openStAbiBinProvider.getABI('DelayedRecoveryModule'), 27 | openStAbiBinProvider.getABI('TokenHolder'), 28 | openStAbiBinProvider.getABI('UserWalletFactory'), 29 | openStAbiBinProvider.getABI('GnosisSafe'), 30 | openStAbiBinProvider.getABI('TokenRules'), 31 | openStAbiBinProvider.getABI('PricerRule'), 32 | mosaicAbiBinProvider.getABI('EIP20Gateway'), 33 | mosaicAbiBinProvider.getABI('EIP20CoGateway'), 34 | mosaicAbiBinProvider.getABI('Anchor'), 35 | priceOracleAbiBinProvider.getABI('PriceOracle') 36 | ]; 37 | abiList.forEach((abi) => abiDecoder.addABI(abi)); 38 | 39 | /** 40 | * Class for ERC 20 token contract interact. 41 | * 42 | * @class ERC20TokenContractInteract 43 | */ 44 | class ERC20TokenContractInteract { 45 | /** 46 | * Decode the transfer event logs from the LogsArray provided 47 | * 48 | * @param {array} logsArray: logs array of the transactions receipt 49 | * 50 | * @returns {array}: Array of all the transfer events in the log 51 | */ 52 | decodeTransactionsFromLogs(logsArray) { 53 | const decodeEvents = abiDecoder.decodeLogs(logsArray); 54 | 55 | // Decode transaction from event. 56 | const transferEventsArray = []; 57 | 58 | for (const eventIndex in decodeEvents) { 59 | const decodeEvent = decodeEvents[eventIndex]; 60 | if (decodeEvent != undefined) { 61 | if (decodeEvent.name === 'Transfer') { 62 | const transferEvent = {}; 63 | transferEvent.address = decodeEvent.address; 64 | const events = decodeEvent.events; 65 | 66 | for (const event in events) { 67 | transferEvent[events[event].name] = events[event].value; 68 | } 69 | 70 | transferEventsArray.push(transferEvent); 71 | } 72 | } 73 | } 74 | 75 | return transferEventsArray; 76 | } 77 | 78 | /** 79 | * Decode event logs from the LogsArray provided. 80 | * 81 | * @param {array} logsArray: logs array of the transactions receipt 82 | * 83 | * @returns {object}: Hash of all the events in the log 84 | */ 85 | decodeLogs(logsArray) { 86 | const decodeEvents = abiDecoder.decodeLogs(logsArray); 87 | 88 | // Console.debug("Decoded Events ", JSON.stringify(decodeEvents)); 89 | // Decode Transaction from event 90 | const eventHash = {}; 91 | for (const eventIndex in decodeEvents) { 92 | try { 93 | const decodedEvent = decodeEvents[eventIndex]; 94 | const event = {}; 95 | event.address = decodedEvent.address.toLowerCase(); 96 | const events = decodedEvent.events; 97 | 98 | for (const ind in events) { 99 | event[events[ind].name] = events[ind].value.toLowerCase(); 100 | } 101 | eventHash[decodedEvent.name] || (eventHash[decodedEvent.name] = []); 102 | eventHash[decodedEvent.name].push(event); 103 | } catch (err) { 104 | // Error because of some undefined event. 105 | logger.error('contractDecoder :: decodeLogs :: try catch ', err); 106 | } 107 | } 108 | 109 | return eventHash; 110 | } 111 | 112 | decodeMethodFromInputData(inputData) { 113 | if (inputData) { 114 | const decodedMethod = abiDecoder.decodeMethod(inputData.toString()); 115 | 116 | if (decodedMethod) { 117 | return JSON.stringify(decodedMethod, null, 4); 118 | } 119 | 120 | return inputData; 121 | } 122 | 123 | return ''; 124 | } 125 | } 126 | 127 | module.exports = new ERC20TokenContractInteract(); 128 | 129 | /* 130 | Const ErcToken = require('./lib/contract_interact/contractDecoder.js'); 131 | ErcToken.decodeLogs(); 132 | */ 133 | -------------------------------------------------------------------------------- /views/layouts/errorPage.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OST VIEW - Something went wrong! 7 | 8 | 9 | {{#if page_meta}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {{/if}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {{{css 'mCommon.css'}}} 41 | {{#each mCss}} 42 | {{{css this}}} 43 | {{/each}} 44 | 45 | 46 | 47 | 48 | {{>topHeader}} 49 | 50 |
51 | 52 |
53 |
54 |
Oops, looks like there was some problem!
55 |

{{msg}}

56 | Home 57 |
58 |
59 | 60 |
61 | 62 | 63 | 64 | 65 | {{{js 'mCommon.js'}}} 66 | {{#each mJs}} 67 | {{{js this}}} 68 | {{/each}} 69 | {{{block "pageScripts"}}} 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /routes/block.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Block related routes.

4 | * Base url for all routes given below is: base_url = /chain-id/:chainId/block 5 | * 6 | * @module Explorer Routes - Block 7 | */ 8 | const express = require('express'); 9 | 10 | // Express router to mount block related routes 11 | const router = express.Router({ mergeParams: true }); 12 | 13 | // load all internal dependencies 14 | const rootPrefix = '..', 15 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 16 | sanitizer = require(rootPrefix + '/helpers/sanitizer'), 17 | coreConstants = require(rootPrefix + '/config/coreConstants'), 18 | baseRoutes = require(rootPrefix + '/lib/globalConstant/baseRoutes'), 19 | routeHelper = require(rootPrefix + '/routes/helper'); 20 | 21 | // Render final response 22 | const renderResult = function(requestResponse, responseObject, contentType) { 23 | return requestResponse.renderResponse(responseObject, requestResponse.isSuccess() ? 200 : 500, contentType); 24 | }; 25 | 26 | /** 27 | * Get block details for a given block number 28 | * 29 | * @name Block Details 30 | * 31 | * @route {GET} {base_url}/:blockNumber 32 | * 33 | * @routeparam {Integer} :block_number - number of block need to be fetched 34 | */ 35 | router.get('/bk-:chainId-:blockNumber', sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 36 | require(rootPrefix + '/app/services/block/GetDetails'); 37 | 38 | routeHelper.performer(req, res, next, 'GetBlockDetails', 'r_b_1').then(function(requestResponse) { 39 | if (requestResponse.isSuccess()) { 40 | processBlockResponse(requestResponse.data, req, res); 41 | } else { 42 | processBlockError(requestResponse.err.code, req, res); 43 | } 44 | }); 45 | }); 46 | 47 | function processBlockResponse(response, req, res) { 48 | let blockDetails = response, 49 | urlTemplates = baseRoutes.getAllUrls(); 50 | 51 | let rawResponse = { 52 | block: blockDetails, 53 | meta: { 54 | baseUrlPrefix: coreConstants.BASE_URL_PREFIX, 55 | urlTemplates: urlTemplates 56 | } 57 | }; 58 | 59 | let webViewResponse = { 60 | page_meta: { 61 | title: `OST VIEW | Block Details - ${blockDetails.blockNumber}`, 62 | description: 'OST VIEW is the home grown block explorer from OST for OpenST Utility Blockchains.', 63 | keywords: 'OST, Simple Token, Utility Chain, Blockchain', 64 | robots: 'noindex, nofollow', 65 | image: `${coreConstants.CLOUD_FRONT_BASE_DOMAIN}/ost-view/images/ost-view-og-image-1.jpg` 66 | }, 67 | mCss: ['mBlockDetails.css'], 68 | mJs: ['mBlockDetails.js'], 69 | title: `Block Details - ${blockDetails.blockNumber}`, 70 | constants: { 71 | cloud_front_base_domain: coreConstants.CLOUD_FRONT_BASE_DOMAIN 72 | }, 73 | template: coreConstants.block 74 | }; 75 | 76 | if (req.headers['content-type'] != 'application/json') { 77 | Object.assign(rawResponse, webViewResponse); 78 | } 79 | 80 | return renderResult(responseHelper.successWithData(rawResponse), res, req.headers['content-type']); 81 | } 82 | 83 | function processBlockError(errorCode, req, res) { 84 | return renderResult( 85 | responseHelper.error(errorCode, coreConstants.DEFAULT_DATA_NOT_AVAILABLE_TEXT), 86 | res, 87 | req.headers['content-type'] 88 | ); 89 | } 90 | 91 | /** 92 | * Get paginated token transactions for a given block number 93 | * 94 | * @name Block Token Transactions 95 | * 96 | * @route {GET} {base_url}/:block_number/token-transactions 97 | * 98 | * @routeparam {Integer} :block_number - number of block need to be fetched 99 | */ 100 | router.get('/bk-:chainId-:blockNumber/transactions', sanitizer.sanitizeDynamicUrlParams, function(req, res, next) { 101 | require(rootPrefix + '/app/services/block/Transactions'); 102 | 103 | if (routeHelper.validateXhrRequest(req, res)) { 104 | return; 105 | } 106 | 107 | routeHelper.performer(req, res, next, 'BlockTransactions', 'r_b_1').then(function(requestResponse) { 108 | if (requestResponse.isSuccess()) { 109 | const response = responseHelper.successWithData({ 110 | transactions: requestResponse.data.transactions, 111 | layout: 'empty', 112 | meta: { 113 | nextPagePayload: requestResponse.data.nextPagePayload 114 | }, 115 | template: coreConstants['block'] 116 | }); 117 | return renderResult(response, res, 'application/json'); 118 | } else { 119 | return renderResult( 120 | responseHelper.error(requestResponse.err.code, coreConstants.DEFAULT_DATA_NOT_AVAILABLE_TEXT), 121 | res, 122 | 'application/json' 123 | ); 124 | } 125 | }); 126 | }); 127 | 128 | module.exports = router; 129 | -------------------------------------------------------------------------------- /app/services/transaction/GetDetails.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GetTransactionDetails - Service for getting block details 3 | * 4 | */ 5 | 6 | const rootPrefix = '../../..', 7 | OSTBase = require('@ostdotcom/base'), 8 | coreConstants = require(rootPrefix + '/config/coreConstants'), 9 | logger = require(rootPrefix + '/lib/logger/customConsoleLogger'), 10 | transactionFormatter = require(rootPrefix + '/lib/formatter/entities/transaction'), 11 | responseHelper = require(rootPrefix + '/lib/formatter/response'), 12 | CommonValidator = require(rootPrefix + '/lib/validators/Common'); 13 | 14 | const InstanceComposer = OSTBase.InstanceComposer; 15 | 16 | require(rootPrefix + '/lib/providers/blockScanner'); 17 | 18 | class GetTransactionDetails { 19 | /** 20 | * constructor 21 | * 22 | * @param chainId - chain id of the transactionHash 23 | * @param transactionHash - transactionHash for which details to be fetched 24 | */ 25 | constructor(params) { 26 | const oThis = this; 27 | 28 | oThis.chainId = params.chainId; 29 | oThis.transactionHashes = params.transactionHash ? [params.transactionHash] : []; 30 | } 31 | 32 | /** 33 | * perform 34 | * 35 | * @return {Promise|*} 36 | */ 37 | perform() { 38 | const oThis = this; 39 | 40 | return oThis.asyncPerform().catch(function(err) { 41 | logger.error(' In catch block of app/services/transaction/GetDetails.js'); 42 | return responseHelper.error('s_t_gd_1', 'something_went_wrong', err); 43 | }); 44 | } 45 | 46 | /** 47 | * asyncPerform 48 | * 49 | * @return {Promise<*>} 50 | */ 51 | async asyncPerform() { 52 | const oThis = this; 53 | 54 | let response = await oThis.validateAndSanitize(); 55 | 56 | if (response.isFailure()) return response; 57 | 58 | let result = {}; 59 | 60 | let transaction = await oThis.getTransactionDetails(); 61 | 62 | if (!transaction) { 63 | return responseHelper.error('s_t_gd_2', 'Data Not found'); 64 | } 65 | 66 | const pricePointsRsp = await oThis.getLatestPricePoints(); 67 | 68 | if (pricePointsRsp.isFailure()) { 69 | return Promise.reject(pricePointsRsp); 70 | } 71 | 72 | const pricePointsRspData = pricePointsRsp.data; 73 | result['transaction'] = await transactionFormatter.perform(transaction); 74 | 75 | result['pricePoint'] = pricePointsRspData; 76 | 77 | return responseHelper.successWithData(result); 78 | } 79 | 80 | /** 81 | * validateAndSanitize 82 | * 83 | * @return {Promise<*>} 84 | */ 85 | async validateAndSanitize() { 86 | const oThis = this; 87 | 88 | if (!CommonValidator.isVarInteger(oThis.chainId)) { 89 | return responseHelper.error('s_t_gd_3', 'chainId missing'); 90 | } 91 | 92 | if (oThis.transactionHashes[0] && !CommonValidator.isTxHashValid(oThis.transactionHashes[0])) { 93 | return responseHelper.error('s_t_gd_4', 'transactionHash missing'); 94 | } 95 | 96 | return responseHelper.successWithData({}); 97 | } 98 | 99 | /** 100 | * getTransactionDetails - get details from block scanner 101 | */ 102 | async getTransactionDetails() { 103 | const oThis = this, 104 | blockScannerProvider = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 105 | blockScanner = blockScannerProvider.getInstance(), 106 | TransactionGet = blockScanner.transaction.Get, 107 | TransactionExtended = blockScanner.transaction.GetExtended; 108 | 109 | let transactionGet = new TransactionGet(oThis.chainId, oThis.transactionHashes); 110 | 111 | let response = await transactionGet.perform(); 112 | 113 | let result = response.data[oThis.transactionHashes[0]]; 114 | 115 | let transactionGetExtended = new TransactionExtended(oThis.chainId, oThis.transactionHashes); 116 | 117 | let transactionGetExtendedResponse = await transactionGetExtended.perform(); 118 | 119 | let input = transactionGetExtendedResponse.data[oThis.transactionHashes[0]].input; 120 | 121 | if (input) { 122 | result['inputData'] = input; 123 | } 124 | 125 | return result; 126 | } 127 | 128 | /** 129 | * Get latest price points. 130 | * 131 | * @returns {Promise|*|Promise>} 132 | */ 133 | async getLatestPricePoints() { 134 | const oThis = this, 135 | blockScannerProvider = oThis.ic().getInstanceFor(coreConstants.icNameSpace, 'blockScannerProvider'), 136 | blockScanner = blockScannerProvider.getInstance(); 137 | 138 | const LatestPricePointsCache = blockScanner.cache.LatestPricePoint; 139 | 140 | return new LatestPricePointsCache().fetch(); 141 | } 142 | } 143 | 144 | InstanceComposer.registerAsShadowableClass(GetTransactionDetails, coreConstants.icNameSpace, 'GetTransactionDetails'); 145 | --------------------------------------------------------------------------------