├── .bowerrc ├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── app ├── app.js ├── components │ └── version │ │ ├── interpolate-filter.js │ │ ├── interpolate-filter_test.js │ │ ├── version-directive.js │ │ ├── version-directive_test.js │ │ ├── version.js │ │ └── version_test.js ├── index.html ├── scripts │ └── controllers │ │ ├── addressInfoController.js │ │ ├── blockInfosController.js │ │ ├── mainController.js │ │ └── transactionInfosController.js ├── styles │ └── main.css └── views │ ├── addressInfo.html │ ├── blockInfos.html │ ├── main.html │ └── transactionInfos.html ├── bower.json ├── e2e-tests ├── protractor.conf.js └── scenarios.js ├── karma.conf.js └── package.json /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/bower,node,osx,webstorm 2 | 3 | ### Bower ### 4 | bower_components 5 | .bower-cache 6 | .bower-registry 7 | .bower-tmp 8 | 9 | 10 | ### Node ### 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directory 37 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 38 | node_modules 39 | 40 | 41 | ### OSX ### 42 | .DS_Store 43 | .AppleDouble 44 | .LSOverride 45 | 46 | # Icon must end with two \r 47 | Icon 48 | 49 | 50 | # Thumbnails 51 | ._* 52 | 53 | # Files that might appear in the root of a volume 54 | .DocumentRevisions-V100 55 | .fseventsd 56 | .Spotlight-V100 57 | .TemporaryItems 58 | .Trashes 59 | .VolumeIcon.icns 60 | 61 | # Directories potentially created on remote AFP share 62 | .AppleDB 63 | .AppleDesktop 64 | Network Trash Folder 65 | Temporary Items 66 | .apdisk 67 | 68 | 69 | ### WebStorm ### 70 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 71 | 72 | *.iml 73 | 74 | ## Directory-based project format: 75 | .idea/ 76 | # if you remove the above rule, at least ignore the following: 77 | 78 | # User-specific stuff: 79 | # .idea/workspace.xml 80 | # .idea/tasks.xml 81 | # .idea/dictionaries 82 | 83 | # Sensitive or high-churn files: 84 | # .idea/dataSources.ids 85 | # .idea/dataSources.xml 86 | # .idea/sqlDataSources.xml 87 | # .idea/dynamic.xml 88 | # .idea/uiDesigner.xml 89 | 90 | # Gradle: 91 | # .idea/gradle.xml 92 | # .idea/libraries 93 | 94 | # Mongo Explorer plugin: 95 | # .idea/mongoSettings.xml 96 | 97 | ## File-based project format: 98 | *.ipr 99 | *.iws 100 | 101 | ## Plugin-specific files: 102 | 103 | # IntelliJ 104 | /out/ 105 | 106 | # mpeltonen/sbt-idea plugin 107 | .idea_modules/ 108 | 109 | # JIRA plugin 110 | atlassian-ide-plugin.xml 111 | 112 | # Crashlytics plugin (for Android Studio and IntelliJ) 113 | com_crashlytics_export_strings.xml 114 | crashlytics.properties 115 | crashlytics-build.properties 116 | 117 | 118 | ### Explorer ### 119 | !.gitkeep 120 | tmp 121 | app/.divshot-cache/ 122 | app/divshot.json 123 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globalstrict": true, 3 | "globals": { 4 | "angular": false, 5 | "describe": false, 6 | "it": false, 7 | "expect": false, 8 | "beforeEach": false, 9 | "afterEach": false, 10 | "module": false, 11 | "inject": false 12 | } 13 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | before_script: 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | - npm start > /dev/null & 9 | - npm run update-webdriver 10 | - sleep 1 # give server time to start 11 | 12 | script: 13 | - node_modules/.bin/karma start karma.conf.js --no-auto-watch --single-run --reporters=dots --browsers=Firefox 14 | - node_modules/.bin/protractor e2e-tests/protractor.conf.js --browser=firefox 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EthExplorer (In Progress) 2 | 3 | ![EthExplorer Screenshot](http://i.imgur.com/NHFYq0x.png) 4 | 5 | ##License 6 | 7 | GPL (see LICENSE) 8 | 9 | ##Installation 10 | 11 | Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git "Git installation") if you haven't already 12 | 13 | Clone the repo 14 | 15 | `git clone https://github.com/etherparty/explorer` 16 | 17 | Download [Nodejs and npm](https://docs.npmjs.com/getting-started/installing-node "Nodejs install") if you don't have them 18 | 19 | Start the program. All dependencies will be automatically downloaded 20 | 21 | `npm start` 22 | 23 | Then visit http://localhost:8000 in your browser of choice. You might get an error message: 24 | 25 | `geth --rpc --rpccorsdomain "http://localhost:8000"` 26 | 27 | Install [geth](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum "Geth install") if you don't already have it, then run the above command. 28 | 29 | Then refresh the page in your browser 30 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('ethExplorer', ['ngRoute','ui.bootstrap']) 4 | 5 | .config(['$routeProvider', 6 | function($routeProvider) { 7 | $routeProvider. 8 | when('/', { 9 | templateUrl: 'views/main.html', 10 | controller: 'mainCtrl' 11 | }). 12 | when('/block/:blockId', { 13 | templateUrl: 'views/blockInfos.html', 14 | controller: 'blockInfosCtrl' 15 | }). 16 | when('/transaction/:transactionId', { 17 | templateUrl: 'views/transactionInfos.html', 18 | controller: 'transactionInfosCtrl' 19 | }). 20 | when('/address/:addressId', { 21 | templateUrl: 'views/addressInfo.html', 22 | controller: 'addressInfoCtrl' 23 | }). 24 | otherwise({ 25 | redirectTo: '/' 26 | }); 27 | }]) 28 | .run(function($rootScope) { 29 | var web3 = new Web3(); 30 | var eth_node_url = 'http://localhost:8545'; // TODO: remote URL 31 | web3.setProvider(new web3.providers.HttpProvider(eth_node_url)); 32 | $rootScope.web3 = web3; 33 | function sleepFor( sleepDuration ){ 34 | var now = new Date().getTime(); 35 | while(new Date().getTime() < now + sleepDuration){ /* do nothing */ } 36 | } 37 | var connected = false; 38 | if(!web3.isConnected()) { 39 | $('#connectwarning').modal({keyboard:false,backdrop:'static'}) 40 | $('#connectwarning').modal('show') 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /app/components/version/interpolate-filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('myApp.version.interpolate-filter', []) 4 | 5 | .filter('interpolate', ['version', function(version) { 6 | return function(text) { 7 | return String(text).replace(/\%VERSION\%/mg, version); 8 | }; 9 | }]); 10 | -------------------------------------------------------------------------------- /app/components/version/interpolate-filter_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('myApp.version module', function() { 4 | beforeEach(module('myApp.version')); 5 | 6 | describe('interpolate filter', function() { 7 | beforeEach(module(function($provide) { 8 | $provide.value('version', 'TEST_VER'); 9 | })); 10 | 11 | it('should replace VERSION', inject(function(interpolateFilter) { 12 | expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after'); 13 | })); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /app/components/version/version-directive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('myApp.version.version-directive', []) 4 | 5 | .directive('appVersion', ['version', function(version) { 6 | return function(scope, elm, attrs) { 7 | elm.text(version); 8 | }; 9 | }]); 10 | -------------------------------------------------------------------------------- /app/components/version/version-directive_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('myApp.version module', function() { 4 | beforeEach(module('myApp.version')); 5 | 6 | describe('app-version directive', function() { 7 | it('should print current version', function() { 8 | module(function($provide) { 9 | $provide.value('version', 'TEST_VER'); 10 | }); 11 | inject(function($compile, $rootScope) { 12 | var element = $compile('')($rootScope); 13 | expect(element.text()).toEqual('TEST_VER'); 14 | }); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /app/components/version/version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('myApp.version', [ 4 | 'myApp.version.interpolate-filter', 5 | 'myApp.version.version-directive' 6 | ]) 7 | 8 | .value('version', '0.1'); 9 | -------------------------------------------------------------------------------- /app/components/version/version_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('myApp.version module', function() { 4 | beforeEach(module('myApp.version')); 5 | 6 | describe('version service', function() { 7 | it('should return current version', inject(function(version) { 8 | expect(version).toEqual('0.1'); 9 | })); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ethereum Block Explorer 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 35 | 36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 71 | 72 | 73 | 81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/scripts/controllers/addressInfoController.js: -------------------------------------------------------------------------------- 1 | angular.module('ethExplorer') 2 | .controller('addressInfoCtrl', function ($rootScope, $scope, $location, $routeParams, $q) { 3 | 4 | var web3 = $rootScope.web3; 5 | 6 | $scope.init=function(){ 7 | 8 | $scope.addressId=$routeParams.addressId; 9 | 10 | if($scope.addressId!==undefined) { 11 | getAddressInfos().then(function(result){ 12 | $scope.balance = result.balance; 13 | $scope.balanceInEther = result.balanceInEther; 14 | }); 15 | } 16 | 17 | 18 | function getAddressInfos(){ 19 | var deferred = $q.defer(); 20 | 21 | web3.eth.getBalance($scope.addressId,function(error, result) { 22 | if(!error) { 23 | deferred.resolve({ 24 | balance: result, 25 | balanceInEther: web3.fromWei(result, 'ether') 26 | }); 27 | } else { 28 | deferred.reject(error); 29 | } 30 | }); 31 | return deferred.promise; 32 | } 33 | 34 | 35 | }; 36 | 37 | $scope.init(); 38 | 39 | }); 40 | -------------------------------------------------------------------------------- /app/scripts/controllers/blockInfosController.js: -------------------------------------------------------------------------------- 1 | angular.module('ethExplorer') 2 | .controller('blockInfosCtrl', function ($rootScope, $scope, $location, $routeParams,$q) { 3 | 4 | var web3 = $rootScope.web3; 5 | 6 | $scope.init = function() 7 | { 8 | 9 | $scope.blockId = $routeParams.blockId; 10 | 11 | if($scope.blockId!==undefined) { 12 | 13 | getBlockInfos() 14 | .then(function(result){ 15 | var number = web3.eth.blockNumber; 16 | 17 | $scope.result = result; 18 | 19 | if(result.hash!==undefined){ 20 | $scope.hash = result.hash; 21 | } 22 | else{ 23 | $scope.hash ='pending'; 24 | } 25 | if(result.miner!==undefined){ 26 | $scope.miner = result.miner; 27 | } 28 | else{ 29 | $scope.miner ='pending'; 30 | } 31 | $scope.gasLimit = result.gasLimit; 32 | $scope.gasUsed = result.gasUsed; 33 | $scope.nonce = result.nonce; 34 | $scope.difficulty = ("" + result.difficulty).replace(/['"]+/g, ''); 35 | $scope.gasLimit = result.gasLimit; // that's a string 36 | $scope.nonce = result.nonce; 37 | $scope.number = result.number; 38 | $scope.parentHash = result.parentHash; 39 | $scope.blockNumber = result.number; 40 | $scope.timestamp = result.timestamp; 41 | $scope.extraData = result.extraData; 42 | $scope.dataFromHex = hex2a(result.extraData); 43 | $scope.size = result.size; 44 | if($scope.blockNumber!==undefined){ 45 | $scope.conf = number - $scope.blockNumber + " Confirmations"; 46 | if($scope.conf===0 + " Confirmations"){ 47 | $scope.conf='Unconfirmed'; 48 | } 49 | } 50 | if($scope.blockNumber!==undefined){ 51 | var info = web3.eth.getBlock($scope.blockNumber); 52 | if(info!==undefined){ 53 | var newDate = new Date(); 54 | newDate.setTime(info.timestamp*1000); 55 | $scope.time = newDate.toUTCString(); 56 | } 57 | } 58 | 59 | 60 | 61 | }); 62 | 63 | } else { 64 | $location.path("/"); 65 | } 66 | 67 | 68 | function getBlockInfos() { 69 | var deferred = $q.defer(); 70 | 71 | web3.eth.getBlock($scope.blockId,function(error, result) { 72 | if(!error) { 73 | deferred.resolve(result); 74 | } else { 75 | deferred.reject(error); 76 | } 77 | }); 78 | return deferred.promise; 79 | 80 | } 81 | 82 | 83 | }; 84 | $scope.init(); 85 | 86 | // parse transactions 87 | $scope.transactions = [] 88 | web3.eth.getBlockTransactionCount($scope.blockId, function(error, result){ 89 | var txCount = result 90 | 91 | for (var blockIdx = 0; blockIdx < txCount; blockIdx++) { 92 | web3.eth.getTransactionFromBlock($scope.blockId, blockIdx, function(error, result) { 93 | 94 | var transaction = { 95 | id: result.hash, 96 | hash: result.hash, 97 | from: result.from, 98 | to: result.to, 99 | gas: result.gas, 100 | input: result.input, 101 | value: result.value 102 | } 103 | $scope.$apply( 104 | $scope.transactions.push(transaction) 105 | ) 106 | }) 107 | } 108 | }) 109 | 110 | 111 | function hex2a(hexx) { 112 | var hex = hexx.toString();//force conversion 113 | var str = ''; 114 | for (var i = 0; i < hex.length; i += 2) 115 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 116 | return str; 117 | } 118 | }); 119 | -------------------------------------------------------------------------------- /app/scripts/controllers/mainController.js: -------------------------------------------------------------------------------- 1 | angular.module('ethExplorer') 2 | .controller('mainCtrl', function ($rootScope, $scope, $location) { 3 | 4 | var web3 = $rootScope.web3; 5 | var maxBlocks = 50; // TODO: into setting file or user select 6 | var blockNum = $scope.blockNum = parseInt(web3.eth.blockNumber, 10); 7 | if (maxBlocks > blockNum) { 8 | maxBlocks = blockNum + 1; 9 | } 10 | 11 | // get latest 50 blocks 12 | $scope.blocks = []; 13 | for (var i = 0; i < maxBlocks; ++i) { 14 | $scope.blocks.push(web3.eth.getBlock(blockNum - i)); 15 | } 16 | 17 | $scope.processRequest = function() { 18 | var requestStr = $scope.ethRequest.split('0x').join(''); 19 | 20 | if (requestStr.length === 40) 21 | return goToAddrInfos(requestStr) 22 | else if(requestStr.length === 64) { 23 | if(/[0-9a-zA-Z]{64}?/.test(requestStr)) 24 | return goToTxInfos('0x'+requestStr) 25 | else if(/[0-9]{1,7}?/.test(requestStr)) 26 | return goToBlockInfos(requestStr) 27 | }else if(parseInt(requestStr) > 0) 28 | return goToBlockInfos(parseInt(requestStr)) 29 | 30 | alert('Don\'t know how to handle '+ requestStr) 31 | }; 32 | 33 | 34 | function goToBlockInfos(requestStr) { 35 | $location.path('/block/'+requestStr); 36 | } 37 | 38 | function goToAddrInfos(requestStr) { 39 | $location.path('/address/'+requestStr); 40 | } 41 | 42 | function goToTxInfos (requestStr) { 43 | $location.path('/transaction/'+requestStr); 44 | } 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /app/scripts/controllers/transactionInfosController.js: -------------------------------------------------------------------------------- 1 | angular.module('ethExplorer') 2 | .controller('transactionInfosCtrl', function ($rootScope, $scope, $location, $routeParams,$q) { 3 | 4 | var web3 = $rootScope.web3; 5 | 6 | $scope.init=function() 7 | { 8 | $scope.txId=$routeParams.transactionId; 9 | 10 | if($scope.txId!==undefined) { // add a test to check if it match tx paterns to avoid useless API call, clients are not obliged to come from the search form... 11 | 12 | getTransactionInfos() 13 | .then(function(result){ 14 | //TODO Refactor this logic, asynchron calls + services.... 15 | var number = web3.eth.blockNumber; 16 | 17 | $scope.result = result; 18 | 19 | if(result.blockHash!==undefined){ 20 | $scope.blockHash = result.blockHash; 21 | } 22 | else{ 23 | $scope.blockHash ='pending'; 24 | } 25 | if(result.blockNumber!==undefined){ 26 | $scope.blockNumber = result.blockNumber; 27 | } 28 | else{ 29 | $scope.blockNumber ='pending'; 30 | } 31 | $scope.from = result.from; 32 | $scope.gas = result.gas; 33 | $scope.gasPrice = result.gasPrice.c[0] + " WEI"; 34 | $scope.hash = result.hash; 35 | $scope.input = result.input; // that's a string 36 | $scope.nonce = result.nonce; 37 | $scope.to = result.to; 38 | $scope.transactionIndex = result.transactionIndex; 39 | $scope.ethValue = result.value.c[0] / 10000; 40 | $scope.txprice = (result.gas * result.gasPrice)/1000000000000000000 + " ETH"; 41 | if($scope.blockNumber!==undefined){ 42 | $scope.conf = number - $scope.blockNumber; 43 | if($scope.conf===0){ 44 | $scope.conf='unconfirmed'; //TODO change color button when unconfirmed... ng-if or ng-class 45 | } 46 | } 47 | //TODO Refactor this logic, asynchron calls + services.... 48 | if($scope.blockNumber!==undefined){ 49 | var info = web3.eth.getBlock($scope.blockNumber); 50 | if(info!==undefined){ 51 | $scope.time = info.timestamp; 52 | } 53 | } 54 | 55 | }); 56 | 57 | } 58 | 59 | 60 | 61 | else{ 62 | $location.path("/"); // add a trigger to display an error message so user knows he messed up with the TX number 63 | } 64 | 65 | 66 | function getTransactionInfos(){ 67 | var deferred = $q.defer(); 68 | 69 | web3.eth.getTransaction($scope.txId,function(error, result) { 70 | if(!error){ 71 | deferred.resolve(result); 72 | } 73 | else{ 74 | deferred.reject(error); 75 | } 76 | }); 77 | return deferred.promise; 78 | 79 | } 80 | 81 | 82 | 83 | }; 84 | $scope.init(); 85 | console.log($scope.result); 86 | 87 | }); 88 | -------------------------------------------------------------------------------- /app/styles/main.css: -------------------------------------------------------------------------------- 1 | /* app css stylesheet */ 2 | 3 | .menu { 4 | list-style: none; 5 | border-bottom: 0.1em solid black; 6 | margin-bottom: 2em; 7 | padding: 0 0 0.5em; 8 | } 9 | 10 | .menu:before { 11 | content: "["; 12 | } 13 | 14 | .menu:after { 15 | content: "]"; 16 | } 17 | 18 | .menu > li { 19 | display: inline; 20 | } 21 | 22 | .menu > li:before { 23 | content: "|"; 24 | padding-right: 0.3em; 25 | } 26 | 27 | .menu > li:nth-child(1):before { 28 | content: ""; 29 | padding: 0; 30 | } 31 | -------------------------------------------------------------------------------- /app/views/addressInfo.html: -------------------------------------------------------------------------------- 1 |
2 |

Address 3 | View information about an Ethereum Address 4 |

5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
{{addressId}}
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
Balance (Wei){{balance}}
Balance (Ether){{balanceInEther}}
26 |
27 | 28 |
-------------------------------------------------------------------------------- /app/views/blockInfos.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Block 4 | View information about an Ethereum Block 5 |

6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
{{hash}}
15 |
16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
Summary
Block Number{{number}}
Received Time 30 | {{timestamp}} 31 |
Difficulty 36 | {{difficulty}} 37 |
Nonce{{nonce}}
Size{{size}}
Miner{{miner}}
Gas Limit{{gasLimit}}
Data{{extraData}}
Data (Translated){{dataFromHex}}
67 |
68 | 69 |

70 | Transactions 71 | - contained in current block 72 |

73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
Transaction #{{$index+1}}
Hash #{{tx.hash}}
From{{tx.from}}
To{{tx.to}}
Gas{{tx.gas}}
Input{{tx.input}}
Value{{tx.value}}
106 |
107 |
108 | 109 |
110 | -------------------------------------------------------------------------------- /app/views/main.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Welcome to the Etherparty Block Explorer!

4 | 5 |

6 | Latest Block: {{blockNum}} 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
Block #Tx #SizeTimestamp
{{i.number}}{{i.transactions.length}}{{i.size}}{{i.timestamp}}
26 | 27 |
28 | -------------------------------------------------------------------------------- /app/views/transactionInfos.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Transaction 4 | View information about an Ethereum transaction 5 |

6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 21 | 28 | 29 | 30 |
{{txId}}
15 | {{from}} 16 |
17 |
19 | 20 | 22 | {{to}} 23 | 24 | {{ethValue}} ETH 25 | 26 |
27 |
31 |
32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
Summary
Block Hash{{blockHash}}
Received Time 46 | {{time}} 47 |
Included In Block 52 | {{blockNumber}} 53 |
Gas Used{{gas}}
Gas Price{{gasPrice}}
Number of transactions made by the sender prior to this one{{nonce}}
Transaction price{{txprice}}
Data{{input}}
79 |
80 | 81 |
82 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-seed", 3 | "description": "A starter project for AngularJS", 4 | "version": "0.0.0", 5 | "homepage": "https://github.com/angular/angular-seed", 6 | "license": "MIT", 7 | "private": true, 8 | "dependencies": { 9 | "angular": "~1.4.0", 10 | "angular-route": "~1.4.0", 11 | "angular-loader": "~1.4.0", 12 | "angular-mocks": "~1.4.0", 13 | "html5-boilerplate": "~5.2.0", 14 | "web3": "~0.14.0", 15 | "angular-bootstrap": "~0.13.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /e2e-tests/protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | allScriptsTimeout: 11000, 3 | 4 | specs: [ 5 | '*.js' 6 | ], 7 | 8 | capabilities: { 9 | 'browserName': 'chrome' 10 | }, 11 | 12 | baseUrl: 'http://localhost:8000/app/', 13 | 14 | framework: 'jasmine', 15 | 16 | jasmineNodeOpts: { 17 | defaultTimeoutInterval: 30000 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /e2e-tests/scenarios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* https://github.com/angular/protractor/blob/master/docs/toc.md */ 4 | 5 | describe('my app', function() { 6 | 7 | 8 | it('should automatically redirect to /view1 when location hash/fragment is empty', function() { 9 | browser.get('index.html'); 10 | expect(browser.getLocationAbsUrl()).toMatch("/view1"); 11 | }); 12 | 13 | 14 | describe('view1', function() { 15 | 16 | beforeEach(function() { 17 | browser.get('index.html#/view1'); 18 | }); 19 | 20 | 21 | it('should render view1 when user navigates to /view1', function() { 22 | expect(element.all(by.css('[ng-view] p')).first().getText()). 23 | toMatch(/partial for view 1/); 24 | }); 25 | 26 | }); 27 | 28 | 29 | describe('view2', function() { 30 | 31 | beforeEach(function() { 32 | browser.get('index.html#/view2'); 33 | }); 34 | 35 | 36 | it('should render view2 when user navigates to /view2', function() { 37 | expect(element.all(by.css('[ng-view] p')).first().getText()). 38 | toMatch(/partial for view 2/); 39 | }); 40 | 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config){ 2 | config.set({ 3 | 4 | basePath : './', 5 | 6 | files : [ 7 | 'app/bower_components/angular/angular.js', 8 | 'app/bower_components/angular-route/angular-route.js', 9 | 'app/bower_components/angular-mocks/angular-mocks.js', 10 | 'app/components/**/*.js', 11 | 'app/view*/**/*.js' 12 | ], 13 | 14 | autoWatch : true, 15 | 16 | frameworks: ['jasmine'], 17 | 18 | browsers : ['Chrome'], 19 | 20 | plugins : [ 21 | 'karma-chrome-launcher', 22 | 'karma-firefox-launcher', 23 | 'karma-jasmine', 24 | 'karma-junit-reporter' 25 | ], 26 | 27 | junitReporter : { 28 | outputFile: 'test_out/unit.xml', 29 | suite: 'unit' 30 | } 31 | 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EthereumExplorer", 3 | "private": false, 4 | "version": "0.1.0", 5 | "description": "A lightweight ethereum block explorer", 6 | "repository": "https://github.com/etherparty/explorer", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "bower": "^1.3.1", 10 | "http-server": "^0.6.1", 11 | "jasmine-core": "^2.3.4", 12 | "karma": "~0.12", 13 | "karma-chrome-launcher": "^0.1.12", 14 | "karma-firefox-launcher": "^0.1.6", 15 | "karma-jasmine": "^0.3.5", 16 | "karma-junit-reporter": "^0.2.2", 17 | "protractor": "^2.1.0", 18 | "shelljs": "^0.2.6" 19 | }, 20 | "scripts": { 21 | "postinstall": "bower install", 22 | 23 | "prestart": "npm install", 24 | "start": "http-server ./app -a localhost -p 8000 -c-1", 25 | 26 | "pretest": "npm install", 27 | "test": "karma start karma.conf.js", 28 | "test-single-run": "karma start karma.conf.js --single-run", 29 | 30 | "preupdate-webdriver": "npm install", 31 | "update-webdriver": "webdriver-manager update", 32 | 33 | "preprotractor": "npm run update-webdriver", 34 | "protractor": "protractor e2e-tests/protractor.conf.js", 35 | 36 | "update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + sed(/sourceMappingURL=angular-loader.min.js.map/,'sourceMappingURL=bower_components/angular-loader/angular-loader.min.js.map','app/bower_components/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'app/index-async.html');\"" 37 | } 38 | } 39 | --------------------------------------------------------------------------------