├── .gitignore ├── .prettierrc ├── tsconfig.json ├── LICENSE ├── package.json ├── tslint.json ├── src ├── __TEST-SCRIPT__ │ └── playground.ts ├── ABI │ └── erc-20-abi.json └── index.ts └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "singleQuote": true, 4 | "bracketSpacing": true, 5 | "printWidth": 80 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es6", "dom", "es2016", "es2017"], 5 | "module": "esnext", 6 | "declaration": true, 7 | "outDir": "./dist/esm", 8 | "rootDir": "./src", 9 | "strict": true, 10 | "moduleResolution": "node", 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "esModuleInterop": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "allowSyntheticDefaultImports": true, 20 | "sourceMap": false, 21 | "resolveJsonModule": true, 22 | /* Advanced Options */ 23 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 24 | }, 25 | "exclude": [ 26 | "node_modules", 27 | "dist", 28 | "src/jest", 29 | "**/*.spec.ts", 30 | "**/*.mock.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Josh Stevens 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ethereum-erc20-token-balances-multicall", 3 | "version": "1.0.2", 4 | "description": "Get all erc20 token balances for the ethereum addresses you want on chain in 1 JSONRPC call.", 5 | "main": "dist/cjs/index.js", 6 | "module": "./dist/esm/index.js", 7 | "types": "./dist/esm/index.d.ts", 8 | "scripts": { 9 | "build": "npm run build:esm && npm run build:cjs", 10 | "build:esm": "tsc", 11 | "build:cjs": "tsc --module commonjs --outDir dist/cjs", 12 | "playground": "node ./dist/cjs/__TEST-SCRIPT__/playground.js", 13 | "watch": "tsc --module commonjs --outDir dist/cjs --watch", 14 | "test": "npm test", 15 | "prepublishOnly": "npm run build" 16 | }, 17 | "author": "joshstevens19@hotmail.co.uk", 18 | "license": "ISC", 19 | "dependencies": { 20 | "bignumber.js": "^9.0.1", 21 | "ethereum-multicall": "^2.7.1" 22 | }, 23 | "devDependencies": { 24 | "@ethersproject/providers": "^5.0.10", 25 | "@types/node": "^14.11.2", 26 | "ethers": "^5.0.15", 27 | "typescript": "^4.1.3", 28 | "web3": "^1.5.1" 29 | }, 30 | "files": [ 31 | "dist", 32 | "package.json", 33 | "package-lock.json", 34 | "README.md", 35 | "LICENSE" 36 | ], 37 | "keywords": [ 38 | "ethereum", 39 | "blockchain", 40 | "JSONRPC" 41 | ], 42 | "bugs": { 43 | "url": "https://github.com/joshstevens19/ethereum-erc20-token-balances-multicall/issues" 44 | }, 45 | "homepage": "https://github.com/joshstevens19/ethereum-erc20-token-balances-multicall" 46 | } 47 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": {}, 5 | "rules": { 6 | "variable-name": false, 7 | "prefer-for-of": false, 8 | "no-arg": true, 9 | "no-any": true, 10 | "no-bitwise": false, 11 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 12 | "no-construct": true, 13 | "no-debugger": true, 14 | "no-duplicate-super": true, 15 | "no-empty": false, 16 | "no-empty-interface": true, 17 | "no-eval": true, 18 | "no-inferrable-types": [true, "ignore-params"], 19 | "no-misused-new": true, 20 | "no-redundant-jsdoc": true, 21 | "no-shadowed-variable": true, 22 | "no-string-literal": false, 23 | "no-string-throw": true, 24 | "no-switch-case-fall-through": true, 25 | "no-trailing-whitespace": true, 26 | "no-unnecessary-initializer": true, 27 | "no-unused-expression": true, 28 | "no-var-keyword": true, 29 | "object-literal-sort-keys": false, 30 | "prefer-const": true, 31 | "interface-name": false, 32 | "trailing-comma": false, 33 | "array-type": false, 34 | "forin": false, 35 | "quotemark": [true, "single"], 36 | "semicolon": [true, "always"], 37 | "typedef": [ 38 | true, 39 | "call-signature", 40 | "property-declaration", 41 | "parameter", 42 | "object-destructuring", 43 | "array-destructuring" 44 | ], 45 | "typedef-whitespace": [ 46 | true, 47 | { 48 | "call-signature": "nospace", 49 | "index-signature": "nospace", 50 | "parameter": "nospace", 51 | "property-declaration": "nospace", 52 | "variable-declaration": "nospace" 53 | } 54 | ], 55 | "no-input-rename": false, 56 | "no-output-rename": false, 57 | "unified-signatures": false 58 | }, 59 | "rulesDirectory": [] 60 | } 61 | -------------------------------------------------------------------------------- /src/__TEST-SCRIPT__/playground.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import Web3 from 'web3'; 3 | import { 4 | // getBalancesForEthereumAddress, 5 | getBalancesForEthereumAddresses, 6 | } from '..'; 7 | 8 | // const executeEthers = async () => { 9 | // const provider = new ethers.providers.InfuraProvider( 10 | // 1, 11 | // '9aa3d95b3bc440fa88ea12eaa4456161' 12 | // ); 13 | 14 | // const balances = await getBalancesForEthereumAddress({ 15 | // contractAddresses: [ 16 | // '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 17 | // '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 18 | // ], 19 | // ethereumAddress: '0x37c81284caA97131339415687d192BF7D18F0f2a', 20 | // providerOptions: { 21 | // ethersProvider: provider, 22 | // }, 23 | // formatBalances: true, 24 | // }); 25 | 26 | // console.log('ethers', balances); 27 | // }; 28 | 29 | // executeEthers(); 30 | 31 | // const executeWeb3 = async () => { 32 | // const web3 = new Web3( 33 | // 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161' 34 | // ); 35 | // const balances = await getBalancesForEthereumAddress({ 36 | // contractAddresses: [ 37 | // '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 38 | // '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 39 | // ], 40 | // ethereumAddress: '0x37c81284caA97131339415687d192BF7D18F0f2a', 41 | // providerOptions: { 42 | // web3Instance: web3, 43 | // }, 44 | // formatBalances: true, 45 | // }); 46 | 47 | // console.log('web3', balances); 48 | // }; 49 | 50 | // executeWeb3(); 51 | 52 | const executeEthersBulk = async () => { 53 | const provider = new ethers.providers.InfuraProvider( 54 | 1, 55 | '9aa3d95b3bc440fa88ea12eaa4456161' 56 | ); 57 | 58 | const balances = await getBalancesForEthereumAddresses({ 59 | contractAddresses: [ 60 | '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 61 | '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 62 | ], 63 | ethereumAddresses: [ 64 | '0x37c81284caA97131339415687d192BF7D18F0f2a', 65 | '0x699c2daD091ffcF18f3cd9E8495929CA3a64dFe1', 66 | ], 67 | providerOptions: { 68 | ethersProvider: provider, 69 | }, 70 | formatBalances: true, 71 | }); 72 | 73 | console.log('ethers', balances); 74 | }; 75 | 76 | executeEthersBulk(); 77 | 78 | const executeWeb3Bulk = async () => { 79 | const web3 = new Web3( 80 | 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161' 81 | ); 82 | const balances = await getBalancesForEthereumAddresses({ 83 | contractAddresses: [ 84 | '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 85 | '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 86 | ], 87 | ethereumAddresses: [ 88 | '0x37c81284caA97131339415687d192BF7D18F0f2a', 89 | '0x699c2daD091ffcF18f3cd9E8495929CA3a64dFe1', 90 | ], 91 | providerOptions: { 92 | web3Instance: web3, 93 | }, 94 | formatBalances: true, 95 | }); 96 | 97 | console.log('web3', balances); 98 | }; 99 | 100 | executeWeb3Bulk(); 101 | -------------------------------------------------------------------------------- /src/ABI/erc-20-abi.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "constant": true, 3 | "inputs": [], 4 | "name": "name", 5 | "outputs": [{ 6 | "name": "", 7 | "type": "string" 8 | }], 9 | "payable": false, 10 | "stateMutability": "view", 11 | "type": "function" 12 | }, 13 | { 14 | "constant": false, 15 | "inputs": [{ 16 | "name": "_spender", 17 | "type": "address" 18 | }, 19 | { 20 | "name": "_value", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "approve", 25 | "outputs": [{ 26 | "name": "", 27 | "type": "bool" 28 | }], 29 | "payable": false, 30 | "stateMutability": "nonpayable", 31 | "type": "function" 32 | }, 33 | { 34 | "constant": true, 35 | "inputs": [], 36 | "name": "totalSupply", 37 | "outputs": [{ 38 | "name": "", 39 | "type": "uint256" 40 | }], 41 | "payable": false, 42 | "stateMutability": "view", 43 | "type": "function" 44 | }, 45 | { 46 | "constant": false, 47 | "inputs": [{ 48 | "name": "_from", 49 | "type": "address" 50 | }, 51 | { 52 | "name": "_to", 53 | "type": "address" 54 | }, 55 | { 56 | "name": "_value", 57 | "type": "uint256" 58 | } 59 | ], 60 | "name": "transferFrom", 61 | "outputs": [{ 62 | "name": "", 63 | "type": "bool" 64 | }], 65 | "payable": false, 66 | "stateMutability": "nonpayable", 67 | "type": "function" 68 | }, 69 | { 70 | "constant": true, 71 | "inputs": [], 72 | "name": "decimals", 73 | "outputs": [{ 74 | "name": "", 75 | "type": "uint8" 76 | }], 77 | "payable": false, 78 | "stateMutability": "view", 79 | "type": "function" 80 | }, 81 | { 82 | "constant": true, 83 | "inputs": [{ 84 | "name": "_owner", 85 | "type": "address" 86 | }], 87 | "name": "balanceOf", 88 | "outputs": [{ 89 | "name": "balance", 90 | "type": "uint256" 91 | }], 92 | "payable": false, 93 | "stateMutability": "view", 94 | "type": "function" 95 | }, 96 | { 97 | "constant": true, 98 | "inputs": [], 99 | "name": "symbol", 100 | "outputs": [{ 101 | "name": "", 102 | "type": "string" 103 | }], 104 | "payable": false, 105 | "stateMutability": "view", 106 | "type": "function" 107 | }, 108 | { 109 | "constant": false, 110 | "inputs": [{ 111 | "name": "_to", 112 | "type": "address" 113 | }, 114 | { 115 | "name": "_value", 116 | "type": "uint256" 117 | } 118 | ], 119 | "name": "transfer", 120 | "outputs": [{ 121 | "name": "", 122 | "type": "bool" 123 | }], 124 | "payable": false, 125 | "stateMutability": "nonpayable", 126 | "type": "function" 127 | }, 128 | { 129 | "constant": true, 130 | "inputs": [{ 131 | "name": "_owner", 132 | "type": "address" 133 | }, 134 | { 135 | "name": "_spender", 136 | "type": "address" 137 | } 138 | ], 139 | "name": "allowance", 140 | "outputs": [{ 141 | "name": "", 142 | "type": "uint256" 143 | }], 144 | "payable": false, 145 | "stateMutability": "view", 146 | "type": "function" 147 | }, 148 | { 149 | "payable": true, 150 | "stateMutability": "payable", 151 | "type": "fallback" 152 | }, 153 | { 154 | "anonymous": false, 155 | "inputs": [{ 156 | "indexed": true, 157 | "name": "owner", 158 | "type": "address" 159 | }, 160 | { 161 | "indexed": true, 162 | "name": "spender", 163 | "type": "address" 164 | }, 165 | { 166 | "indexed": false, 167 | "name": "value", 168 | "type": "uint256" 169 | } 170 | ], 171 | "name": "Approval", 172 | "type": "event" 173 | }, 174 | { 175 | "anonymous": false, 176 | "inputs": [{ 177 | "indexed": true, 178 | "name": "from", 179 | "type": "address" 180 | }, 181 | { 182 | "indexed": true, 183 | "name": "to", 184 | "type": "address" 185 | }, 186 | { 187 | "indexed": false, 188 | "name": "value", 189 | "type": "uint256" 190 | } 191 | ], 192 | "name": "Transfer", 193 | "type": "event" 194 | } 195 | ] 196 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | import { 3 | ContractCallContext, 4 | ContractCallResults, 5 | ContractCallReturnContext, 6 | Multicall, 7 | } from 'ethereum-multicall'; 8 | import ERC20Abi from './ABI/erc-20-abi.json'; 9 | 10 | export interface ProviderOptions { 11 | // tslint:disable-next-line: no-any 12 | web3Instance?: any | undefined; 13 | // tslint:disable-next-line: no-any 14 | ethersProvider?: any | undefined; 15 | } 16 | 17 | interface Erc20TokensBalancesRequestBase { 18 | contractAddresses: string[]; 19 | providerOptions: ProviderOptions; 20 | multicallCustomContractAddress?: string; 21 | // default true! 22 | formatBalances?: boolean; 23 | } 24 | 25 | export interface SingleEthereumAddressRequest 26 | extends Erc20TokensBalancesRequestBase { 27 | ethereumAddress: string; 28 | } 29 | 30 | export interface MultipleEthereumAddressRequest 31 | extends Erc20TokensBalancesRequestBase { 32 | ethereumAddresses: string[]; 33 | } 34 | 35 | export interface Token { 36 | contractAddress: string; 37 | decimals: number; 38 | symbol: string; 39 | name: string; 40 | balance: string; 41 | } 42 | 43 | export interface BalanceResult { 44 | ethereumAddress: string; 45 | tokens: Token[]; 46 | } 47 | 48 | const BALANCE = 0; 49 | const SYMBOL = 1; 50 | const DECIMALS = 2; 51 | const NAME = 3; 52 | 53 | /** 54 | * Get balances for the user for all contracts in 1 single jsonrpc call 55 | * @param request The erc20 token balance request 56 | */ 57 | export async function getBalancesForEthereumAddress( 58 | request: SingleEthereumAddressRequest 59 | ): Promise { 60 | if (request.formatBalances === undefined) { 61 | request.formatBalances = true; 62 | } 63 | 64 | const multicall = buildMultiCallInstance(request); 65 | 66 | const contractCallContext: ContractCallContext[] = []; 67 | 68 | for (let i = 0; i < request.contractAddresses.length; i++) { 69 | const token = request.contractAddresses[i]; 70 | contractCallContext.push( 71 | buildContractCallContext(token, request.ethereumAddress, token) 72 | ); 73 | } 74 | 75 | const contractCallResults: ContractCallResults = await multicall.call( 76 | contractCallContext 77 | ); 78 | 79 | const tokens: Token[] = []; 80 | 81 | for (const result in contractCallResults.results) { 82 | tokens.push( 83 | buildToken(contractCallResults.results[result], request.formatBalances) 84 | ); 85 | } 86 | 87 | return { ethereumAddress: request.ethereumAddress, tokens }; 88 | } 89 | 90 | /** 91 | * Get balances for all users for all contracts in 1 single jsonrpc call 92 | * @param request The erc20 token balance request 93 | */ 94 | export async function getBalancesForEthereumAddresses( 95 | request: MultipleEthereumAddressRequest 96 | ): Promise { 97 | if (request.formatBalances === undefined) { 98 | request.formatBalances = true; 99 | } 100 | 101 | const multicall = buildMultiCallInstance(request); 102 | 103 | const contractCallContext: ContractCallContext[] = []; 104 | 105 | for (let i = 0; i < request.contractAddresses.length; i++) { 106 | const token = request.contractAddresses[i]; 107 | for (let u = 0; u < request.ethereumAddresses.length; u++) { 108 | const ethereumAddress = request.ethereumAddresses[u]; 109 | contractCallContext.push( 110 | buildContractCallContext( 111 | `${token}_${ethereumAddress}`, 112 | ethereumAddress, 113 | token 114 | ) 115 | ); 116 | } 117 | } 118 | 119 | const contractCallResults: ContractCallResults = await multicall.call( 120 | contractCallContext 121 | ); 122 | 123 | const balanceResults: BalanceResult[] = []; 124 | 125 | for (const result in contractCallResults.results) { 126 | const token = buildToken( 127 | contractCallResults.results[result], 128 | request.formatBalances 129 | ); 130 | 131 | const balanceResult = balanceResults.find((balance) => 132 | result.includes(balance.ethereumAddress) 133 | ); 134 | 135 | if (balanceResult) { 136 | balanceResult.tokens.push(token); 137 | } else { 138 | balanceResults.push({ 139 | ethereumAddress: result.split('_')[1], 140 | tokens: [token], 141 | }); 142 | } 143 | } 144 | 145 | return balanceResults; 146 | } 147 | 148 | /** 149 | * Build the token 150 | * @param tokenInfo The token info 151 | * @param formatBalances The format balances 152 | */ 153 | function buildToken( 154 | tokenInfo: ContractCallReturnContext, 155 | formatBalances: boolean 156 | ): Token { 157 | const decimals = tokenInfo.callsReturnContext[DECIMALS].returnValues[0]; 158 | 159 | return { 160 | contractAddress: tokenInfo.originalContractCallContext.contractAddress, 161 | symbol: tokenInfo.callsReturnContext[SYMBOL].returnValues[0], 162 | decimals, 163 | name: tokenInfo.callsReturnContext[NAME].returnValues[0], 164 | balance: 165 | formatBalances === true 166 | ? new BigNumber( 167 | tokenInfo.callsReturnContext[BALANCE].returnValues[0].hex 168 | ) 169 | .shiftedBy(decimals * -1) 170 | .toFixed() 171 | : tokenInfo.callsReturnContext[BALANCE].returnValues[0].hex, 172 | }; 173 | } 174 | 175 | /** 176 | * Build the multicall instance 177 | * @param request The erc20 token balance request 178 | */ 179 | function buildMultiCallInstance( 180 | request: Erc20TokensBalancesRequestBase 181 | ): Multicall { 182 | if (request.providerOptions.ethersProvider) { 183 | return new Multicall({ 184 | ethersProvider: request.providerOptions.ethersProvider, 185 | multicallCustomContractAddress: request.multicallCustomContractAddress, 186 | tryAggregate: true, 187 | }); 188 | } 189 | 190 | return new Multicall({ 191 | web3Instance: request.providerOptions.web3Instance, 192 | multicallCustomContractAddress: request.multicallCustomContractAddress, 193 | tryAggregate: true, 194 | }); 195 | } 196 | 197 | /** 198 | * Build contract call context 199 | * @param reference The reference for call 200 | * @param ethereumAddress The ethereum address 201 | * @param contractAddress The contract address 202 | */ 203 | function buildContractCallContext( 204 | reference: string, 205 | ethereumAddress: string, 206 | contractAddress: string 207 | ): ContractCallContext { 208 | return { 209 | reference, 210 | contractAddress, 211 | abi: ERC20Abi, 212 | calls: [ 213 | { 214 | reference: 'balance', 215 | methodName: 'balanceOf', 216 | methodParameters: [ethereumAddress], 217 | }, 218 | { 219 | reference: 'symbol', 220 | methodName: 'symbol', 221 | methodParameters: [], 222 | }, 223 | { 224 | reference: 'decimals', 225 | methodName: 'decimals', 226 | methodParameters: [], 227 | }, 228 | { 229 | reference: 'name', 230 | methodName: 'name', 231 | methodParameters: [], 232 | }, 233 | ], 234 | }; 235 | } 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/ethereum-erc20-token-balances-multicall.svg)](https://badge.fury.io/js/ethereum-erc20-token-balances-multicall) 2 | ![downloads](https://img.shields.io/npm/dw/ethereum-erc20-token-balances-multicall) 3 | 4 | # ethereum-erc20-token-balances-multicall 5 | 6 | This library will fetch all the token balances you want within 1 JSONRPC call. This solves long loading time when your trying to show balances for a lot of assets and brings down JSONRPC request limits. It also brings down the metadata for the token aka decimal places, name, symbol and balance. This will return in lighting speed if you compare that to say doing 50 seperate JSONRPC calls this brings the speed down around 50x. You can call a single ethereum address or you can call as many ethereum addresses as you want. 7 | 8 | This uses the ethereum-multicall which is a lightweight library for interacting with the [multicall](https://github.com/joshstevens19/ethereum-multicall) smart contract. 9 | 10 | ethereum-erc20-token-balances-multicall is fully written in typescript so has full compile time support. The motivation of this package was to expose a super simple and easy to understand interface for you to take the full benefits of the fast loading of erc20 balances for users. Also to not being opinionated on how you use it, you can use it with web3 or ethers. 11 | 12 | ## Supports 13 | 14 | - mainnet 15 | - kovan 16 | - görli 17 | - rinkeby 18 | - ropsten 19 | - binance smart chain 20 | - xdai 21 | - matic 22 | - mumbai 23 | 24 | ## Installation 25 | 26 | ### npm: 27 | 28 | ```js 29 | $ npm install ethereum-erc20-token-balances-multicall 30 | ``` 31 | 32 | ### yarn: 33 | 34 | ```js 35 | $ yarn add ethereum-erc20-token-balances-multicall 36 | ``` 37 | 38 | ## Usage 39 | 40 | ### Ethers 41 | 42 | #### Query just 1 ethereum address: 43 | 44 | ```ts 45 | import { getBalancesForEthereumAddress } from 'ethereum-erc20-token-balances-multicall'; 46 | 47 | const balances = await getBalancesForEthereumAddress({ 48 | // erc20 tokens you want to query! 49 | contractAddresses: [ 50 | '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 51 | '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 52 | // unlimited amount you can keep adding and adding 53 | // you can also build up easily through the code as well 54 | ], 55 | // ethereum address of the user you want to get the balances for 56 | ethereumAddress: 'THE_ETHEREUM_ADDRESS', 57 | // your ethers provider 58 | providerOptions: { 59 | ethersProvider: YOUR_ETHERS_PROVIDER, 60 | }, 61 | }); 62 | 63 | console.log('result', balances); 64 | ``` 65 | 66 | Result: 67 | 68 | ```ts 69 | { 70 | ethereumAddress: 'THE_ETHEREUM_ADDRESS', 71 | tokens: 72 | [ 73 | { 74 | contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 75 | symbol: 'UNI', 76 | decimals: 18, 77 | name: 'Uniswap', 78 | balance: '703.523279430449604142', 79 | }, 80 | { 81 | contractAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 82 | symbol: 'GTC', 83 | decimals: 18, 84 | name: 'Gitcoin', 85 | balance: '400.606', 86 | }, 87 | ]; 88 | } 89 | ``` 90 | 91 | #### Query > 1 ethereum addresses balances 92 | 93 | ```ts 94 | import { getBalancesForEthereumAddresses } from 'ethereum-erc20-token-balances-multicall'; 95 | 96 | const balances = await getBalancesForEthereumAddresses({ 97 | // erc20 tokens you want to query! 98 | contractAddresses: [ 99 | '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 100 | '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 101 | // unlimited amount you can keep adding and adding 102 | // you can also build up easily through the code as well 103 | ], 104 | // ethereum addresses you want to get the balance for 105 | ethereumAddress: ['ADDRESS_1', 'ADDRESS_2'], 106 | // your ethers provider 107 | providerOptions: { 108 | ethersProvider: YOUR_ETHERS_PROVIDER, 109 | }, 110 | }); 111 | 112 | console.log('result', balances); 113 | ``` 114 | 115 | Result: 116 | 117 | ```ts 118 | [{ 119 | ethereumAddress: 'ADDRESS_1', 120 | tokens: 121 | [ 122 | { 123 | contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 124 | symbol: 'UNI', 125 | decimals: 18, 126 | name: 'Uniswap', 127 | balance: '703.523279430449604142', 128 | }, 129 | { 130 | contractAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 131 | symbol: 'GTC', 132 | decimals: 18, 133 | name: 'Gitcoin', 134 | balance: '400.606', 135 | }, 136 | ]; 137 | }, { 138 | ethereumAddress: 'ADDRESS_2', 139 | tokens: 140 | [ 141 | { 142 | contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 143 | symbol: 'UNI', 144 | decimals: 18, 145 | name: 'Uniswap', 146 | balance: '23.523279430449604142', 147 | }, 148 | { 149 | contractAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 150 | symbol: 'GTC', 151 | decimals: 18, 152 | name: 'Gitcoin', 153 | balance: '293.606', 154 | }, 155 | ]; 156 | }] 157 | ``` 158 | 159 | ### Web3 160 | 161 | #### Query just 1 ethereum address: 162 | 163 | ```ts 164 | import { getBalancesForEthereumAddress } from 'ethereum-erc20-token-balances-multicall'; 165 | 166 | const balances = await getBalancesForEthereumAddress({ 167 | // erc20 tokens you want to query! 168 | contractAddresses: [ 169 | '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 170 | '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 171 | // unlimited amount you can keep adding and adding 172 | // you can also build up easily through the code as well 173 | ], 174 | // ethereum address of the user you want to get the balances for 175 | ethereumAddress: 'THE_ETHEREUM_ADDRESS', 176 | // your web3 provider 177 | providerOptions: { 178 | web3Instance: YOUR_WEB_PROVIDER, 179 | }, 180 | }); 181 | 182 | console.log('result', balances); 183 | ``` 184 | 185 | Result: 186 | 187 | ```ts 188 | { 189 | ethereumAddress: 'THE_ETHEREUM_ADDRESS', 190 | tokens: 191 | [ 192 | { 193 | contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 194 | symbol: 'UNI', 195 | decimals: 18, 196 | name: 'Uniswap', 197 | balance: '703.523279430449604142', 198 | }, 199 | { 200 | contractAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 201 | symbol: 'GTC', 202 | decimals: 18, 203 | name: 'Gitcoin', 204 | balance: '400.606', 205 | }, 206 | ]; 207 | } 208 | ``` 209 | 210 | #### Query > 1 ethereum addresses balances 211 | 212 | ```ts 213 | import { getBalancesForEthereumAddresses } from 'ethereum-erc20-token-balances-multicall'; 214 | 215 | const balances = await getBalancesForEthereumAddresses({ 216 | // erc20 tokens you want to query! 217 | contractAddresses: [ 218 | '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 219 | '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 220 | // unlimited amount you can keep adding and adding 221 | // you can also build up easily through the code as well 222 | ], 223 | // ethereum addresses you want to get the balance for 224 | ethereumAddress: ['ADDRESS_1', 'ADDRESS_2'], 225 | // your web3 provider 226 | providerOptions: { 227 | web3Instance: YOUR_WEB_PROVIDER, 228 | }, 229 | }); 230 | 231 | console.log('result', balances); 232 | ``` 233 | 234 | Result: 235 | 236 | ```ts 237 | [{ 238 | ethereumAddress: 'ADDRESS_1', 239 | tokens: 240 | [ 241 | { 242 | contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 243 | symbol: 'UNI', 244 | decimals: 18, 245 | name: 'Uniswap', 246 | balance: '703.523279430449604142', 247 | }, 248 | { 249 | contractAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 250 | symbol: 'GTC', 251 | decimals: 18, 252 | name: 'Gitcoin', 253 | balance: '400.606', 254 | }, 255 | ]; 256 | }, { 257 | ethereumAddress: 'ADDRESS_2', 258 | tokens: 259 | [ 260 | { 261 | contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 262 | symbol: 'UNI', 263 | decimals: 18, 264 | name: 'Uniswap', 265 | balance: '23.523279430449604142', 266 | }, 267 | { 268 | contractAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', 269 | symbol: 'GTC', 270 | decimals: 18, 271 | name: 'Gitcoin', 272 | balance: '293.606', 273 | }, 274 | ]; 275 | }] 276 | ``` 277 | 278 | ## Issues 279 | 280 | Please raise any issues in the below link. 281 | 282 | https://github.com/joshstevens19/ethereum-erc20-token-balances-multicall/issues 283 | 284 | ## Thanks And Support 285 | 286 | This package is brought to you by [Josh Stevens](https://github.com/joshstevens19). My aim is to be able to keep creating these awesome packages to help the Ethereum space grow with easier-to-use tools to allow the learning curve to get involved with blockchain development easier and making Ethereum ecosystem better. If you want to help with that vision and allow me to invest more time into creating cool packages or if this package has saved you a lot of development time donations are welcome, every little helps. By donating, you are supporting me to be able to maintain existing packages, extend existing packages (as Ethereum matures), and allowing me to build more packages for Ethereum due to being able to invest more time into it. Thanks, everyone! 287 | 288 | ## Direct donations 289 | 290 | Direct donations any token accepted - Eth address > `0x699c2daD091ffcF18f3cd9E8495929CA3a64dFe1` 291 | 292 | ## Github sponsors 293 | 294 | [sponsor me](https://github.com/sponsors/joshstevens19) via github using fiat money 295 | --------------------------------------------------------------------------------