├── .gitignore ├── src ├── index.tsx ├── utils │ ├── types.ts │ ├── providers.ts │ ├── getContractBalances.ts │ └── getClassicBalances.ts ├── index.html ├── Loader.tsx ├── App.scss ├── App.tsx └── tokens.json ├── docs ├── index.html └── style.css ├── tsconfig.json ├── README.md ├── package.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App'; 4 | 5 | const rootEl = document.getElementById("root"); 6 | 7 | render(, rootEl); -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | import { AddressBalanceMap } from 'eth-balance-checker'; 2 | 3 | export enum LIBRARY { 4 | WEB3 = 'WEB3', 5 | ETHERS = 'ETHERS', 6 | }; 7 | 8 | export type Balances = AddressBalanceMap; -------------------------------------------------------------------------------- /src/utils/providers.ts: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3'; 2 | import * as Ethers from 'ethers'; 3 | 4 | export const web3 = new Web3(new Web3.providers.HttpProvider('https://mainnet.infura.io/v3/b509a32f1d2a412d958d135660d62b8a')); 5 | export const ethers = Ethers.getDefaultProvider(); 6 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eth Balance Checker Demo 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Eth Balance Checker Demo 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "dist", 6 | "rootDir": "src/", 7 | "strict": true, 8 | "allowSyntheticDefaultImports": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "noImplicitAny": true, 12 | "resolveJsonModule": true, 13 | "esModuleInterop": true, 14 | "lib": ["dom", "es2017"], 15 | "jsx": "react" 16 | }, 17 | "include": ["src"] 18 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `eth-balance-checker-demo` 2 | 3 | Demo for the [`eth-balance-checker`](https://github.com/wbobeirne/eth-balance-checker) module. 4 | 5 | ## Development 6 | 7 | Install packages: 8 | 9 | ```bash 10 | npm install 11 | # OR # 12 | yarn 13 | ``` 14 | 15 | Watch for file changes and compile in dev mode, output to `dist/`: 16 | ```bash 17 | npm run dev 18 | # OR # 19 | yarn dev 20 | ``` 21 | 22 | Compile for production, output to `docs/`: 23 | ```bash 24 | npm run build 25 | # OR # 26 | yarn build 27 | ``` 28 | -------------------------------------------------------------------------------- /src/Loader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | ) -------------------------------------------------------------------------------- /src/utils/getContractBalances.ts: -------------------------------------------------------------------------------- 1 | import { AddressBalanceMap } from 'eth-balance-checker'; 2 | import { getAddressesBalances as getWeb3Balances } from 'eth-balance-checker/lib/web3'; 3 | import { getAddressesBalances as getEthersBalances } from 'eth-balance-checker/lib/ethers'; 4 | import { LIBRARY } from './types'; 5 | import { web3, ethers } from './providers'; 6 | 7 | async function getContractBalances( 8 | lib: LIBRARY, 9 | addresses: string[], 10 | tokens: string[], 11 | ): Promise { 12 | let balances; 13 | 14 | if (lib === LIBRARY.WEB3) { 15 | balances = await getWeb3Balances(web3, addresses, tokens); 16 | } else { 17 | balances = await getEthersBalances(ethers, addresses, tokens); 18 | } 19 | 20 | return balances; 21 | } 22 | 23 | export default getContractBalances; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "balance-checker-demo", 3 | "version": "1.0.0", 4 | "description": "Demo for the balance-checker repo", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "webpack --mode=development --watch", 9 | "build": "webpack --mode=production" 10 | }, 11 | "devDependencies": { 12 | "@types/react": "16.4.18", 13 | "@types/react-dom": "16.0.9", 14 | "@types/web3": "1.0.10", 15 | "css-loader": "1.0.0", 16 | "html-webpack-plugin": "3.2.0", 17 | "mini-css-extract-plugin": "0.4.4", 18 | "node-sass": "4.9.4", 19 | "sass-loader": "7.1.0", 20 | "style-loader": "0.23.1", 21 | "ts-loader": "5.2.2", 22 | "typescript": "3.1.3", 23 | "webpack": "4.21.0", 24 | "webpack-cli": "3.1.2" 25 | }, 26 | "dependencies": { 27 | "@types/bn.js": "4.11.2", 28 | "bn.js": "4.11.8", 29 | "eth-balance-checker": "0.1.0", 30 | "ethers": "4.0.7", 31 | "react": "16.5.2", 32 | "react-dom": "16.5.2", 33 | "reset-css": "4.0.1", 34 | "web3": "1.0.0-beta.36" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5 | 6 | config = (env, argv) => ({ 7 | entry: { 8 | script: ['./src/index.tsx'] 9 | }, 10 | output: { 11 | filename: '[name].js', 12 | path: path.resolve( 13 | __dirname, 14 | argv.mode === 'production' ? './docs/' : './dist/', 15 | ), 16 | publicPath: './', 17 | }, 18 | resolve: { 19 | modules: ['node_modules'], 20 | extensions:['.ts', '.tsx', '.js', '.jsx'], 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.tsx?$/, 26 | use: 'ts-loader', 27 | }, 28 | { 29 | test: /\.scss$/, 30 | use: [ 31 | { 32 | loader: MiniCssExtractPlugin.loader, 33 | }, 34 | { 35 | loader: 'css-loader', 36 | options: { 37 | minimize: true, 38 | }, 39 | }, 40 | 'sass-loader?sourceMap', 41 | ], 42 | }, 43 | ], 44 | }, 45 | plugins: [ 46 | new MiniCssExtractPlugin({ 47 | filename: 'style.css', 48 | }), 49 | new HtmlWebpackPlugin({ 50 | template: path.resolve(__dirname, 'src/index.html'), 51 | }), 52 | ], 53 | performance: { 54 | hints: false, 55 | }, 56 | }); 57 | 58 | module.exports = config; -------------------------------------------------------------------------------- /src/utils/getClassicBalances.ts: -------------------------------------------------------------------------------- 1 | import BN from 'bn.js'; 2 | import { Contract as EthersContract } from 'ethers'; 3 | import { AddressBalanceMap } from 'eth-balance-checker'; 4 | import { LIBRARY } from './types'; 5 | import { web3, ethers } from './providers'; 6 | 7 | const tokenAbi = [{ 8 | "constant": true, 9 | "inputs": [{ "name":"_owner", "type":"address" }], 10 | "name": "balanceOf", 11 | "outputs": [{ "name":"balance", "type":"uint256" }], 12 | "type": "function" 13 | }]; 14 | 15 | async function getClassicBalances( 16 | library: LIBRARY, 17 | addresses: string[], 18 | tokens: string[], 19 | ): Promise { 20 | const promises: Array> = []; 21 | 22 | addresses.forEach(address => { 23 | tokens.forEach(tokenAddr => { 24 | if (library === LIBRARY.WEB3) { 25 | if (tokenAddr === '0x0000000000000000000000000000000000000000') { 26 | promises.push(web3.eth.getBalance(address) as any); 27 | } else { 28 | const contract: any = new web3.eth.Contract(tokenAbi, tokenAddr); 29 | promises.push(contract.methods.balanceOf(address).call().catch(() => new BN(0))); 30 | } 31 | } else { 32 | if (tokenAddr === '0x0000000000000000000000000000000000000000') { 33 | promises.push(ethers.getBalance(address) as any); 34 | } else { 35 | const contract: any = new EthersContract(tokenAddr, tokenAbi, ethers); 36 | promises.push(contract.balanceOf(address).catch(() => new BN(0))); 37 | } 38 | } 39 | }); 40 | }); 41 | 42 | return Promise.all(promises).then(responses => { 43 | const balances: AddressBalanceMap = {}; 44 | addresses.forEach((address, addressIdx) => { 45 | balances[address] = {}; 46 | tokens.forEach((tokenAddr, tokenIdx) => { 47 | const balance = responses[addressIdx * tokens.length + tokenIdx]; 48 | balances[address][tokenAddr] = balance.toString(); 49 | }); 50 | }); 51 | return balances; 52 | }); 53 | } 54 | 55 | export default getClassicBalances; -------------------------------------------------------------------------------- /src/App.scss: -------------------------------------------------------------------------------- 1 | $blue: #3498db; 2 | $green: #2ecc71; 3 | $red: #e74c3c; 4 | 5 | @mixin small-breakpoint { 6 | @media (max-width: 800px) { 7 | @content; 8 | } 9 | } 10 | 11 | html, 12 | body { 13 | line-height: 1.6; 14 | } 15 | 16 | body { 17 | color: #333; 18 | font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif; 19 | box-sizing: border-box; 20 | } 21 | 22 | *, *:before, *:after { 23 | box-sizing: inherit; 24 | } 25 | 26 | 27 | 28 | .Template { 29 | &-top { 30 | display: flex; 31 | margin-bottom: 30px; 32 | padding: 30px; 33 | background: linear-gradient(to top, #141e30, #243b55); 34 | color: #FFF; 35 | box-shadow: 0 0 20px rgba(#000, 0.2); 36 | 37 | > * { 38 | flex: 1; 39 | width: 50%; 40 | } 41 | 42 | @include small-breakpoint { 43 | flex-direction: column; 44 | 45 | > * { 46 | width: 100%; 47 | border-right: none; 48 | } 49 | } 50 | } 51 | 52 | &-bottom { 53 | padding: 30px; 54 | } 55 | } 56 | 57 | .Info { 58 | padding-right: 40px; 59 | max-width: 840px; 60 | margin: 0 auto; 61 | border-right: 1px solid rgba(#FFF, 0.08); 62 | 63 | @include small-breakpoint { 64 | border-right: none; 65 | padding-right: 0; 66 | padding-bottom: 20px; 67 | border-bottom: 1px solid rgba(#FFF, 0.08); 68 | } 69 | 70 | &-title { 71 | font-size: 32px; 72 | margin-bottom: 10px; 73 | } 74 | 75 | &-text { 76 | font-size: 16px; 77 | opacity: 0.8; 78 | margin-bottom: 10px; 79 | } 80 | 81 | &-links { 82 | &-link { 83 | position: relative; 84 | display: block; 85 | padding: 12px 20px 12px 50px; 86 | margin-bottom: 10px; 87 | font-size: 16px; 88 | color: #FFF; 89 | border-radius: 4px; 90 | text-decoration: none; 91 | 92 | &:after { 93 | content: ''; 94 | display: block; 95 | position: absolute; 96 | left: 15px; 97 | top: 50%; 98 | transform: translateY(-50%); 99 | width: 20px; 100 | height: 20px; 101 | background-size: contain; 102 | background-repeat: no-repeat; 103 | background-position: center; 104 | } 105 | 106 | &.is-medium { 107 | background: linear-gradient(-180deg,#60BD72, #50A360 90%); 108 | color: #FFF; 109 | 110 | &:after { 111 | background-image: url('https://i.imgur.com/Fu2AHNY.png'); 112 | } 113 | } 114 | 115 | &.is-github { 116 | background: linear-gradient(-180deg,#fafbfc, #eff3f6 90%); 117 | color: #24292e; 118 | 119 | &:after { 120 | background-image: url('https://i.imgur.com/w0sPagx.png'); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | .Form { 128 | padding-left: 40px; 129 | 130 | @include small-breakpoint { 131 | padding-left: 0; 132 | padding-top: 20px; 133 | } 134 | 135 | &-field { 136 | margin-bottom: 10px; 137 | 138 | &-label { 139 | margin-bottom: 5px; 140 | font-weight: bold; 141 | } 142 | } 143 | 144 | &-addresses { 145 | &-input { 146 | display: block; 147 | width: 100%; 148 | padding: 8px 12px; 149 | font-size: 13px; 150 | border-radius: 4px; 151 | margin-bottom: 5px; 152 | box-shadow: none; 153 | border: 1px solid rgba(#000, 0.15); 154 | font-family: 'Lucida Console', Monaco, monospace; 155 | outline: none; 156 | 157 | &:hover { 158 | border-color: rgba(#000, 0.4); 159 | } 160 | &:focus, 161 | &:active { 162 | border-color: $blue; 163 | } 164 | } 165 | 166 | &-add { 167 | display: block; 168 | width: 100%; 169 | border: 1px solid #FFF; 170 | color: #FFF; 171 | background: none; 172 | font-size: 12px; 173 | padding: 8px 12px; 174 | border-radius: 4px; 175 | opacity: 0.8; 176 | outline: none; 177 | 178 | &:hover, 179 | &:focus { 180 | opacity: 1; 181 | } 182 | } 183 | } 184 | 185 | &-library { 186 | &-radio { 187 | display: inline-block; 188 | margin-right: 10px; 189 | 190 | &-label { 191 | margin-left: 3px; 192 | font-size: 13px; 193 | } 194 | } 195 | } 196 | 197 | &-submit { 198 | display: block; 199 | width: 100%; 200 | background: linear-gradient( 201 | -180deg, 202 | lighten($blue, 5%), 203 | darken($blue, 5%) 90% 204 | ); 205 | border: none; 206 | border-radius: 4px; 207 | font-size: 15px; 208 | color: #FFF; 209 | padding: 12px 15px; 210 | outline: none; 211 | 212 | &:hover, 213 | &:focus { 214 | background: linear-gradient( 215 | -180deg, 216 | lighten($blue, 10%), 217 | $blue 90% 218 | ); 219 | } 220 | } 221 | } 222 | 223 | .Balances { 224 | display: flex; 225 | 226 | &-column { 227 | flex: 1; 228 | margin: 0 10px; 229 | 230 | &-title { 231 | text-align: center; 232 | font-size: 22px; 233 | margin-bottom: 10px; 234 | } 235 | 236 | &-summary { 237 | display: flex; 238 | align-items: center; 239 | justify-content: center; 240 | margin-bottom: 10px; 241 | color: #555; 242 | font-size: 12px; 243 | 244 | > * { 245 | padding: 0 15px; 246 | border-right: 1px solid #CCC; 247 | 248 | &:last-child { 249 | border-right: none; 250 | } 251 | } 252 | } 253 | 254 | &-balances { 255 | padding: 10px; 256 | font-size: 10px; 257 | line-height: 1.8; 258 | background: #FAFAFA; 259 | border: 1px solid #E7E7E7; 260 | color: #777; 261 | border-radius: 4px; 262 | font-family: 'Lucida Console', Monaco, monospace; 263 | } 264 | 265 | &-loading { 266 | padding-top: 30px; 267 | text-align: center; 268 | 269 | svg { 270 | display: block; 271 | margin: 0 auto 20px; 272 | width: 50px; 273 | height: 50px; 274 | stroke: $blue; 275 | } 276 | 277 | span { 278 | font-size: 16px; 279 | opacity: 0.5; 280 | } 281 | } 282 | 283 | &-waiting { 284 | padding-top: 60px; 285 | font-size: 16px; 286 | opacity: 0.5; 287 | text-align: center; 288 | } 289 | 290 | &-error { 291 | background: $red; 292 | border-radius: 2px; 293 | padding: 12px 20px; 294 | color: #FFF; 295 | text-align: center; 296 | font-size: 16px; 297 | } 298 | } 299 | } -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v4.0 | 20180602 3 | License: none (public domain) 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | main, menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; } 24 | 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, hgroup, main, menu, nav, section { 28 | display: block; } 29 | 30 | /* HTML5 hidden-attribute fix for newer browsers */ 31 | *[hidden] { 32 | display: none; } 33 | 34 | body { 35 | line-height: 1; } 36 | 37 | ol, ul { 38 | list-style: none; } 39 | 40 | blockquote, q { 41 | quotes: none; } 42 | 43 | blockquote:before, blockquote:after, 44 | q:before, q:after { 45 | content: ''; 46 | content: none; } 47 | 48 | table { 49 | border-collapse: collapse; 50 | border-spacing: 0; } 51 | 52 | html, 53 | body { 54 | line-height: 1.6; } 55 | 56 | body { 57 | color: #333; 58 | font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif; 59 | box-sizing: border-box; } 60 | 61 | *, *:before, *:after { 62 | box-sizing: inherit; } 63 | 64 | .Template-top { 65 | display: flex; 66 | margin-bottom: 30px; 67 | padding: 30px; 68 | background: linear-gradient(to top, #141e30, #243b55); 69 | color: #FFF; 70 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); } 71 | .Template-top > * { 72 | flex: 1; 73 | width: 50%; } 74 | @media (max-width: 800px) { 75 | .Template-top { 76 | flex-direction: column; } 77 | .Template-top > * { 78 | width: 100%; 79 | border-right: none; } } 80 | 81 | .Template-bottom { 82 | padding: 30px; } 83 | 84 | .Info { 85 | padding-right: 40px; 86 | max-width: 840px; 87 | margin: 0 auto; 88 | border-right: 1px solid rgba(255, 255, 255, 0.08); } 89 | @media (max-width: 800px) { 90 | .Info { 91 | border-right: none; 92 | padding-right: 0; 93 | padding-bottom: 20px; 94 | border-bottom: 1px solid rgba(255, 255, 255, 0.08); } } 95 | .Info-title { 96 | font-size: 32px; 97 | margin-bottom: 10px; } 98 | .Info-text { 99 | font-size: 16px; 100 | opacity: 0.8; 101 | margin-bottom: 10px; } 102 | .Info-links-link { 103 | position: relative; 104 | display: block; 105 | padding: 12px 20px 12px 50px; 106 | margin-bottom: 10px; 107 | font-size: 16px; 108 | color: #FFF; 109 | border-radius: 4px; 110 | text-decoration: none; } 111 | .Info-links-link:after { 112 | content: ''; 113 | display: block; 114 | position: absolute; 115 | left: 15px; 116 | top: 50%; 117 | transform: translateY(-50%); 118 | width: 20px; 119 | height: 20px; 120 | background-size: contain; 121 | background-repeat: no-repeat; 122 | background-position: center; } 123 | .Info-links-link.is-medium { 124 | background: linear-gradient(-180deg, #60BD72, #50A360 90%); 125 | color: #FFF; } 126 | .Info-links-link.is-medium:after { 127 | background-image: url("https://i.imgur.com/Fu2AHNY.png"); } 128 | .Info-links-link.is-github { 129 | background: linear-gradient(-180deg, #fafbfc, #eff3f6 90%); 130 | color: #24292e; } 131 | .Info-links-link.is-github:after { 132 | background-image: url("https://i.imgur.com/w0sPagx.png"); } 133 | 134 | .Form { 135 | padding-left: 40px; } 136 | @media (max-width: 800px) { 137 | .Form { 138 | padding-left: 0; 139 | padding-top: 20px; } } 140 | .Form-field { 141 | margin-bottom: 10px; } 142 | .Form-field-label { 143 | margin-bottom: 5px; 144 | font-weight: bold; } 145 | .Form-addresses-input { 146 | display: block; 147 | width: 100%; 148 | padding: 8px 12px; 149 | font-size: 13px; 150 | border-radius: 4px; 151 | margin-bottom: 5px; 152 | box-shadow: none; 153 | border: 1px solid rgba(0, 0, 0, 0.15); 154 | font-family: 'Lucida Console', Monaco, monospace; 155 | outline: none; } 156 | .Form-addresses-input:hover { 157 | border-color: rgba(0, 0, 0, 0.4); } 158 | .Form-addresses-input:focus, .Form-addresses-input:active { 159 | border-color: #3498db; } 160 | .Form-addresses-add { 161 | display: block; 162 | width: 100%; 163 | border: 1px solid #FFF; 164 | color: #FFF; 165 | background: none; 166 | font-size: 12px; 167 | padding: 8px 12px; 168 | border-radius: 4px; 169 | opacity: 0.8; 170 | outline: none; } 171 | .Form-addresses-add:hover, .Form-addresses-add:focus { 172 | opacity: 1; } 173 | .Form-library-radio { 174 | display: inline-block; 175 | margin-right: 10px; } 176 | .Form-library-radio-label { 177 | margin-left: 3px; 178 | font-size: 13px; } 179 | .Form-submit { 180 | display: block; 181 | width: 100%; 182 | background: linear-gradient(-180deg, #4aa3df, #258cd1 90%); 183 | border: none; 184 | border-radius: 4px; 185 | font-size: 15px; 186 | color: #FFF; 187 | padding: 12px 15px; 188 | outline: none; } 189 | .Form-submit:hover, .Form-submit:focus { 190 | background: linear-gradient(-180deg, #5faee3, #3498db 90%); } 191 | 192 | .Balances { 193 | display: flex; } 194 | .Balances-column { 195 | flex: 1; 196 | margin: 0 10px; } 197 | .Balances-column-title { 198 | text-align: center; 199 | font-size: 22px; 200 | margin-bottom: 10px; } 201 | .Balances-column-summary { 202 | display: flex; 203 | align-items: center; 204 | justify-content: center; 205 | margin-bottom: 10px; 206 | color: #555; 207 | font-size: 12px; } 208 | .Balances-column-summary > * { 209 | padding: 0 15px; 210 | border-right: 1px solid #CCC; } 211 | .Balances-column-summary > *:last-child { 212 | border-right: none; } 213 | .Balances-column-balances { 214 | padding: 10px; 215 | font-size: 10px; 216 | line-height: 1.8; 217 | background: #FAFAFA; 218 | border: 1px solid #E7E7E7; 219 | color: #777; 220 | border-radius: 4px; 221 | font-family: 'Lucida Console', Monaco, monospace; } 222 | .Balances-column-loading { 223 | padding-top: 30px; 224 | text-align: center; } 225 | .Balances-column-loading svg { 226 | display: block; 227 | margin: 0 auto 20px; 228 | width: 50px; 229 | height: 50px; 230 | stroke: #3498db; } 231 | .Balances-column-loading span { 232 | font-size: 16px; 233 | opacity: 0.5; } 234 | .Balances-column-waiting { 235 | padding-top: 60px; 236 | font-size: 16px; 237 | opacity: 0.5; 238 | text-align: center; } 239 | .Balances-column-error { 240 | background: #e74c3c; 241 | border-radius: 2px; 242 | padding: 12px 20px; 243 | color: #FFF; 244 | text-align: center; 245 | font-size: 16px; } 246 | 247 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import getContractBalances from './utils/getContractBalances'; 3 | import getClassicBalances from './utils/getClassicBalances'; 4 | import { LIBRARY, Balances } from './utils/types'; 5 | import Loader from './Loader'; 6 | import TOKENS from './tokens.json'; 7 | import 'reset-css/sass/_reset.scss'; 8 | import './App.scss'; 9 | 10 | interface State { 11 | addresses: string[]; 12 | library: LIBRARY; 13 | classicBalances: null | Balances; 14 | contractBalances: null | Balances; 15 | isClassicLoading: boolean; 16 | isContractLoading: boolean; 17 | contractError: null | string; 18 | classicError: null | string; 19 | contractTime: number; 20 | classicTime: number; 21 | } 22 | 23 | export default class App extends React.Component<{}, State> { 24 | state: State = { 25 | addresses: [ 26 | // Binance hot wallet 27 | '0xfe9e8709d3215310075d67e3ed32a380ccf451c8', 28 | // Bitfinex hot wallet 29 | '0x742d35cc6634c0532925a3b844bc454e4438f44e', 30 | // Bittrex hot wallet 31 | '0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98', 32 | // Huobi hot wallet 33 | '0xdc76cd25977e0a5ae17155770273ad58648900d3', 34 | // Kraken hot wallet 35 | '0xe853c56864a2ebe4576a807d26fdc4a0ada51919', 36 | ], 37 | library: LIBRARY.WEB3, 38 | classicBalances: null, 39 | contractBalances: null, 40 | isClassicLoading: false, 41 | isContractLoading: false, 42 | classicError: null, 43 | contractError: null, 44 | classicTime: 0, 45 | contractTime: 0, 46 | }; 47 | 48 | render() { 49 | const { 50 | addresses, 51 | library, 52 | classicBalances, 53 | contractBalances, 54 | isClassicLoading, 55 | isContractLoading, 56 | classicError, 57 | contractError, 58 | classicTime, 59 | contractTime, 60 | } = this.state; 61 | 62 | const balanceCols = [{ 63 | title: 'Classic Batched RPC Calls', 64 | balances: classicBalances, 65 | error: classicError, 66 | isLoading: isClassicLoading, 67 | waitingMessage: null, 68 | numRequests: addresses.length * TOKENS.length, 69 | time: classicTime, 70 | }, { 71 | title: 'ETH Balance Checker Contract', 72 | balances: contractBalances, 73 | error: contractError, 74 | isLoading: isContractLoading, 75 | waitingMessage: isClassicLoading ? 'Waiting for classic to finish...' : null, 76 | numRequests: 1, 77 | time: contractTime, 78 | }]; 79 | 80 | return ( 81 |
82 |
83 |
84 |

ETH Balance Checker Demo

85 |

86 | Put the ETH balance checker to the test against traditional 87 | batched RPC requests. Just set the addresses and hit the 88 | "Get Balance" button to try it out. Open your network panel if 89 | you want to inspect the calls in greater detail. 90 |

91 | 114 |
115 |
116 |
117 |
Addresses
118 | {addresses.map((address, idx) => ( 119 | this.changeAddress(idx, ev.currentTarget.value)} 124 | placeholder="0x123..." 125 | /> 126 | ))} 127 |
128 |
129 |
130 | Library 131 |
132 | 142 | 152 |
153 | 154 |
155 |
156 |
157 | {balanceCols.map(bc => ( 158 |
159 |

{bc.title}

160 | {bc.error && 161 |
162 | {bc.error} 163 |
164 | } 165 | {bc.waitingMessage && 166 |
167 | {bc.waitingMessage} 168 |
169 | } 170 | {bc.isLoading && 171 |
172 | 173 | Loading... 174 |
175 | } 176 | {bc.balances && ( 177 | <> 178 |
179 | Duration: {bc.time}ms 180 | # of HTTP Requests: {bc.numRequests} 181 |
182 |
183 |                     {JSON.stringify(bc.balances, null, 2)}
184 |                   
185 | 186 | )} 187 |
188 | ))} 189 |
190 |
191 | ); 192 | } 193 | 194 | private getBalances = () => { 195 | const { addresses, library } = this.state; 196 | this.setState({ 197 | classicBalances: null, 198 | contractBalances: null, 199 | classicError: null, 200 | contractError: null, 201 | }, async () => { 202 | const tokenAddresses = TOKENS.map(t => t.address); 203 | const addrs = addresses.filter(a => !!a); 204 | 205 | // First get classic balances 206 | try { 207 | const startTime = Date.now(); 208 | this.setState({ isClassicLoading: true }); 209 | const classicBalances = await getClassicBalances(library, addrs, tokenAddresses); 210 | this.setState({ 211 | classicBalances, 212 | classicTime: Date.now() - startTime, 213 | isClassicLoading: false, 214 | }); 215 | } catch(err) { 216 | console.error('Failed to get balances from classic method:', err); 217 | this.setState({ 218 | classicError: err.message, 219 | isClassicLoading: false, 220 | }); 221 | } 222 | 223 | // Then get contract balances 224 | try { 225 | const startTime = Date.now(); 226 | this.setState({ isContractLoading: true }); 227 | const contractBalances = await getContractBalances(library, addrs, tokenAddresses); 228 | this.setState({ 229 | contractBalances, 230 | contractTime: Date.now() - startTime, 231 | isContractLoading: false, 232 | }); 233 | } catch(err) { 234 | console.error('Failed to get balances from contract method:', err); 235 | this.setState({ 236 | contractError: err.message, 237 | isContractLoading: false, 238 | }); 239 | } 240 | }); 241 | }; 242 | 243 | private handleFormSubmit = (ev: React.FormEvent) => { 244 | ev.preventDefault(); 245 | this.getBalances(); 246 | }; 247 | 248 | private handleChangeLibrary = (ev: React.ChangeEvent) => { 249 | this.setState({ library: ev.currentTarget.value } as any); 250 | }; 251 | 252 | private changeAddress = (idx: number, address: string) => { 253 | const addresses = [...this.state.addresses]; 254 | addresses[idx] = address; 255 | this.setState({ addresses }); 256 | }; 257 | } 258 | -------------------------------------------------------------------------------- /src/tokens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "address": "0x0000000000000000000000000000000000000000", 4 | "symbol": "ETH", 5 | "decimal": 18 6 | }, 7 | { 8 | "address": "0x5CA9a71B1d01849C0a95490Cc00559717fCF0D1d", 9 | "symbol": "AE", 10 | "decimal": 18 11 | }, 12 | { 13 | "address": "0x8eB24319393716668D768dCEC29356ae9CfFe285", 14 | "symbol": "AGI", 15 | "decimal": 8 16 | }, 17 | { 18 | "address": "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466", 19 | "symbol": "AION", 20 | "decimal": 8 21 | }, 22 | { 23 | "address": "0x960b236A07cf122663c4303350609A66A7B288C0", 24 | "symbol": "ANT", 25 | "decimal": 18 26 | }, 27 | { 28 | "address": "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", 29 | "symbol": "BAT", 30 | "decimal": 18 31 | }, 32 | { 33 | "address": "0x5732046A883704404F284Ce41FfADd5b007FD668", 34 | "symbol": "BLZ", 35 | "decimal": 18 36 | }, 37 | { 38 | "address": "0xB8c77482e45F1F44dE1745F52C74426C631bDD52", 39 | "symbol": "BNB", 40 | "decimal": 18 41 | }, 42 | { 43 | "address": "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", 44 | "symbol": "BNT", 45 | "decimal": 18 46 | }, 47 | { 48 | "address": "0x558EC3152e2eb2174905cd19AeA4e34A23DE9aD6", 49 | "symbol": "BRD", 50 | "decimal": 18 51 | }, 52 | { 53 | "address": "0xcB97e65F07DA24D46BcDD078EBebd7C6E6E3d750", 54 | "symbol": "BTM", 55 | "decimal": 8 56 | }, 57 | { 58 | "address": "0x26E75307Fc0C021472fEb8F727839531F112f317", 59 | "symbol": "C20", 60 | "decimal": 18 61 | }, 62 | { 63 | "address": "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F", 64 | "symbol": "CMT", 65 | "decimal": 18 66 | }, 67 | { 68 | "address": "0xd4c435F5B09F855C3317c8524Cb1F586E42795fa", 69 | "symbol": "CND", 70 | "decimal": 18 71 | }, 72 | { 73 | "address": "0x4E0603e2A27A30480E5e3a4Fe548e29EF12F64bE", 74 | "symbol": "CREDO", 75 | "decimal": 18 76 | }, 77 | { 78 | "address": "0x80A7E048F37A50500351C204Cb407766fA3baE7f", 79 | "symbol": "CRPT", 80 | "decimal": 18 81 | }, 82 | { 83 | "address": "0x41e5560054824eA6B0732E656E3Ad64E20e94E45", 84 | "symbol": "CVC", 85 | "decimal": 8 86 | }, 87 | { 88 | "address": "0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359", 89 | "symbol": "DAI", 90 | "decimal": 18 91 | }, 92 | { 93 | "address": "0x08d32b0da63e2C3bcF8019c9c5d849d7a9d791e6", 94 | "symbol": "DCN", 95 | "decimal": 0 96 | }, 97 | { 98 | "address": "0x3597bfD533a99c9aa083587B074434E61Eb0A258", 99 | "symbol": "DENT", 100 | "decimal": 8 101 | }, 102 | { 103 | "address": "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A", 104 | "symbol": "DGD", 105 | "decimal": 9 106 | }, 107 | { 108 | "address": "0x1C83501478f1320977047008496DACBD60Bb15ef", 109 | "symbol": "DGTX", 110 | "decimal": 18 111 | }, 112 | { 113 | "address": "0x419c4dB4B9e25d6Db2AD9691ccb832C8D9fDA05E", 114 | "symbol": "DRGN", 115 | "decimal": 18 116 | }, 117 | { 118 | "address": "0x3c75226555FC496168d48B88DF83B95F16771F37", 119 | "symbol": "DROP", 120 | "decimal": 0 121 | }, 122 | { 123 | "address": "0xCeD4E93198734dDaFf8492d525Bd258D49eb388E", 124 | "symbol": "EDO", 125 | "decimal": 18 126 | }, 127 | { 128 | "address": "0xc528c28FEC0A90C083328BC45f587eE215760A0F", 129 | "symbol": "EDR", 130 | "decimal": 18 131 | }, 132 | { 133 | "address": "0xbf2179859fc6D5BEE9Bf9158632Dc51678a4100e", 134 | "symbol": "ELF", 135 | "decimal": 18 136 | }, 137 | { 138 | "address": "0xf0Ee6b27b759C9893Ce4f094b49ad28fd15A23e4", 139 | "symbol": "ENG", 140 | "decimal": 8 141 | }, 142 | { 143 | "address": "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c", 144 | "symbol": "ENJ", 145 | "decimal": 18 146 | }, 147 | { 148 | "address": "0x86Fa049857E0209aa7D9e616F7eb3b3B78ECfdb0", 149 | "symbol": "EOS", 150 | "decimal": 18 151 | }, 152 | { 153 | "address": "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b", 154 | "symbol": "FUN", 155 | "decimal": 8 156 | }, 157 | { 158 | "address": "0x6810e776880C02933D47DB1b9fc05908e5386b96", 159 | "symbol": "GNO", 160 | "decimal": 18 161 | }, 162 | { 163 | "address": "0xa74476443119A942dE498590Fe1f2454d7D4aC0d", 164 | "symbol": "GNT", 165 | "decimal": 18 166 | }, 167 | { 168 | "address": "0xC5bBaE50781Be1669306b9e001EFF57a2957b09d", 169 | "symbol": "GTO", 170 | "decimal": 5 171 | }, 172 | { 173 | "address": "0x103c3A209da59d3E7C4A89307e66521e081CFDF0", 174 | "symbol": "GVT", 175 | "decimal": 18 176 | }, 177 | { 178 | "address": "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2", 179 | "symbol": "HOT", 180 | "decimal": 18 181 | }, 182 | { 183 | "address": "0x888666CA69E0f178DED6D75b5726Cee99A87D698", 184 | "symbol": "ICN", 185 | "decimal": 18 186 | }, 187 | { 188 | "address": "0xb5A5F22694352C15B00323844aD545ABb2B11028", 189 | "symbol": "ICX", 190 | "decimal": 18 191 | }, 192 | { 193 | "address": "0xFA1a856Cfa3409CFa145Fa4e20Eb270dF3EB21ab", 194 | "symbol": "IOST", 195 | "decimal": 18 196 | }, 197 | { 198 | "address": "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5", 199 | "symbol": "KIN", 200 | "decimal": 18 201 | }, 202 | { 203 | "address": "0xdd974D5C2e2928deA5F71b9825b8b646686BD200", 204 | "symbol": "KNC", 205 | "decimal": 18 206 | }, 207 | { 208 | "address": "0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0", 209 | "symbol": "LOOM", 210 | "decimal": 18 211 | }, 212 | { 213 | "address": "0xEF68e7C694F40c8202821eDF525dE3782458639f", 214 | "symbol": "LRC", 215 | "decimal": 18 216 | }, 217 | { 218 | "address": "0xe25bCec5D3801cE3a794079BF94adF1B8cCD802D", 219 | "symbol": "MAN", 220 | "decimal": 18 221 | }, 222 | { 223 | "address": "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942", 224 | "symbol": "MANA", 225 | "decimal": 18 226 | }, 227 | { 228 | "address": "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d", 229 | "symbol": "MCO", 230 | "decimal": 8 231 | }, 232 | { 233 | "address": "0x40395044Ac3c0C57051906dA938B54BD6557F212", 234 | "symbol": "MGO", 235 | "decimal": 8 236 | }, 237 | { 238 | "address": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", 239 | "symbol": "MKR", 240 | "decimal": 18 241 | }, 242 | { 243 | "address": "0xCc80C051057B774cD75067Dc48f8987C4Eb97A5e", 244 | "symbol": "NEC", 245 | "decimal": 18 246 | }, 247 | { 248 | "address": "0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3", 249 | "symbol": "NPXS", 250 | "decimal": 18 251 | }, 252 | { 253 | "address": "0xB91318F35Bdb262E9423Bc7c7c2A3A93DD93C92C", 254 | "symbol": "NULS", 255 | "decimal": 18 256 | }, 257 | { 258 | "address": "0xbf52F2ab39e26E0951d2a02b49B7702aBe30406a", 259 | "symbol": "ODE", 260 | "decimal": 18 261 | }, 262 | { 263 | "address": "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", 264 | "symbol": "OMG", 265 | "decimal": 18 266 | }, 267 | { 268 | "address": "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280", 269 | "symbol": "PAY", 270 | "decimal": 18 271 | }, 272 | { 273 | "address": "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17", 274 | "symbol": "PLR", 275 | "decimal": 18 276 | }, 277 | { 278 | "address": "0x0e0989b1f9B8A38983c2BA8053269Ca62Ec9B195", 279 | "symbol": "POE", 280 | "decimal": 8 281 | }, 282 | { 283 | "address": "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC", 284 | "symbol": "POLY", 285 | "decimal": 18 286 | }, 287 | { 288 | "address": "0x595832F8FC6BF59c85C527fEC3740A1b7a361269", 289 | "symbol": "POWR", 290 | "decimal": 6 291 | }, 292 | { 293 | "address": "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a", 294 | "symbol": "PPT", 295 | "decimal": 8 296 | }, 297 | { 298 | "address": "0x618E75Ac90b12c6049Ba3b27f5d5F8651b0037F6", 299 | "symbol": "QASH", 300 | "decimal": 6 301 | }, 302 | { 303 | "address": "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d", 304 | "symbol": "QSP", 305 | "decimal": 18 306 | }, 307 | { 308 | "address": "0x9a642d6b3368ddc662CA244bAdf32cDA716005BC", 309 | "symbol": "QTUM", 310 | "decimal": 18 311 | }, 312 | { 313 | "address": "0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6", 314 | "symbol": "RDN", 315 | "decimal": 18 316 | }, 317 | { 318 | "address": "0x1985365e9f78359a9B6AD760e32412f4a445E862", 319 | "symbol": "REP", 320 | "decimal": 18 321 | }, 322 | { 323 | "address": "0x8f8221aFbB33998d8584A2B05749bA73c37a938a", 324 | "symbol": "REQ", 325 | "decimal": 18 326 | }, 327 | { 328 | "address": "0x607F4C5BB672230e8672085532f7e901544a7375", 329 | "symbol": "RLC", 330 | "decimal": 9 331 | }, 332 | { 333 | "address": "0x4156D3342D5c385a87D264F90653733592000581", 334 | "symbol": "SALT", 335 | "decimal": 8 336 | }, 337 | { 338 | "address": "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098", 339 | "symbol": "SAN", 340 | "decimal": 18 341 | }, 342 | { 343 | "address": "0x6F6DEb5db0C4994A8283A01D6CFeEB27Fc3bBe9C", 344 | "symbol": "SMART", 345 | "decimal": 0 346 | }, 347 | { 348 | "address": "0x78Eb8DC641077F049f910659b6d580E80dC4d237", 349 | "symbol": "SMT", 350 | "decimal": 8 351 | }, 352 | { 353 | "address": "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E", 354 | "symbol": "SNT", 355 | "decimal": 18 356 | }, 357 | { 358 | "address": "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25", 359 | "symbol": "SRN", 360 | "decimal": 18 361 | }, 362 | { 363 | "address": "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC", 364 | "symbol": "STORJ", 365 | "decimal": 8 366 | }, 367 | { 368 | "address": "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433", 369 | "symbol": "STORM", 370 | "decimal": 18 371 | }, 372 | { 373 | "address": "0x5c3a228510D246b78a3765C20221Cbf3082b44a4", 374 | "symbol": "STQ", 375 | "decimal": 18 376 | }, 377 | { 378 | "address": "0x12480E24eb5bec1a9D4369CaB6a80caD3c0A377A", 379 | "symbol": "SUB", 380 | "decimal": 2 381 | }, 382 | { 383 | "address": "0x85e076361cc813A908Ff672F9BAd1541474402b2", 384 | "symbol": "TEL", 385 | "decimal": 2 386 | }, 387 | { 388 | "address": "0x3883f5e181fccaF8410FA61e12b59BAd963fb645", 389 | "symbol": "THETA", 390 | "decimal": 18 391 | }, 392 | { 393 | "address": "0xf230b790E05390FC8295F4d3F60332c93BEd42e2", 394 | "symbol": "TRX", 395 | "decimal": 6 396 | }, 397 | { 398 | "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7", 399 | "symbol": "USDT", 400 | "decimal": 6 401 | }, 402 | { 403 | "address": "0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374", 404 | "symbol": "VERI", 405 | "decimal": 18 406 | }, 407 | { 408 | "address": "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022", 409 | "symbol": "WAX", 410 | "decimal": 8 411 | }, 412 | { 413 | "address": "0xb7cB1C96dB6B22b0D3d9536E0108d062BD488F74", 414 | "symbol": "WTC", 415 | "decimal": 18 416 | }, 417 | { 418 | "address": "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27", 419 | "symbol": "ZIL", 420 | "decimal": 12 421 | }, 422 | { 423 | "address": "0xE41d2489571d322189246DaFA5ebDe1F4699F498", 424 | "symbol": "ZRX", 425 | "decimal": 18 426 | } 427 | ] --------------------------------------------------------------------------------